From 32e1683f8b0b5b5bb24986adeb99118ceb875ea5 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 8 Jan 2021 09:10:50 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- CHANGELOG.md | 13 +++ Gemfile | 2 +- Gemfile.lock | 4 +- app/assets/javascripts/jobs/store/actions.js | 2 +- .../javascripts/lib/utils/common_utils.js | 87 -------------- app/assets/javascripts/lib/utils/favicon.js | 30 +++++ .../javascripts/lib/utils/favicon_ci.js | 16 +++ app/assets/javascripts/pipelines.js | 2 +- .../mr_widget_options.vue | 2 +- ...tion-expiration-banner-not-dismissable.yml | 5 - .../293845-allow-nullable-digest.yml | 5 - ...r-the-max-int-size-in-bytes-fail-to-up.yml | 5 - ...om-fix-project-access-token-regression.yml | 5 - ...ary-update-service-to-invalidate-cache.yml | 5 - changelogs/unreleased/id-bump-net-ldap.yml | 5 + .../unreleased/update-helm-2-to-2-17-0.yml | 5 - jest.config.base.js | 2 +- package.json | 1 + spec/frontend/lib/utils/common_utils_spec.js | 21 ---- spec/frontend/lib/utils/favicon_ci_spec.js | 50 +++++++++ spec/frontend/lib/utils/favicon_spec.js | 39 +++++++ spec/frontend/lib/utils/mock_data.js | 3 - spec/frontend/test_setup.js | 3 + .../vue_mr_widget/mr_widget_options_spec.js | 22 ++-- spec/javascripts/lib/utils/browser_spec.js | 106 ------------------ yarn.lock | 5 + 26 files changed, 177 insertions(+), 268 deletions(-) create mode 100644 app/assets/javascripts/lib/utils/favicon.js create mode 100644 app/assets/javascripts/lib/utils/favicon_ci.js delete mode 100644 changelogs/unreleased/289925-subscription-expiration-banner-not-dismissable.yml delete mode 100644 changelogs/unreleased/293845-allow-nullable-digest.yml delete mode 100644 changelogs/unreleased/294221-gitlab-pages-deployments-over-the-max-int-size-in-bytes-fail-to-up.yml delete mode 100644 changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml delete mode 100644 changelogs/unreleased/fix-canary-update-service-to-invalidate-cache.yml create mode 100644 changelogs/unreleased/id-bump-net-ldap.yml delete mode 100644 changelogs/unreleased/update-helm-2-to-2-17-0.yml create mode 100644 spec/frontend/lib/utils/favicon_ci_spec.js create mode 100644 spec/frontend/lib/utils/favicon_spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 645cc391339..56ddb1350bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 13.7.3 (2021-01-08) + +### Fixed (7 changes) + +- Fix Canary Ingress weight is not reflected on UI immediately. !50246 +- Change pages deployments size to bigint. !50262 +- Fix viewing container repositories with tags with corrupted manifest. !50362 +- Fix the graphQL type for container repository tags. !50419 +- Fix(eetrialbanner): fix EE trial banner to allow dismiss. !50436 +- Update Helm 2 version to 2.17.0. !50547 +- Fix project access token regression. !50800 + + ## 13.7.2 (2021-01-07) ### Security (7 changes) diff --git a/Gemfile b/Gemfile index 83940fceedb..5634b465c5f 100644 --- a/Gemfile +++ b/Gemfile @@ -81,7 +81,7 @@ gem 'gpgme', '~> 2.0.19' # GitLab fork with several improvements to original library. For full list of changes # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master gem 'gitlab_omniauth-ldap', '~> 2.1.1', require: 'omniauth-ldap' -gem 'net-ldap' +gem 'net-ldap', '~> 0.16.3' # API # Locked at Grape v1.4.0 until https://github.com/ruby-grape/grape/pull/2088 is merged diff --git a/Gemfile.lock b/Gemfile.lock index bbd95d38b47..2e7a7696caa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -733,7 +733,7 @@ GEM nakayoshi_fork (0.0.4) nap (1.1.0) nenv (0.3.0) - net-ldap (0.16.2) + net-ldap (0.16.3) net-ntp (2.1.3) net-ssh (6.0.0) netrc (0.11.0) @@ -1423,7 +1423,7 @@ DEPENDENCIES minitest (~> 5.11.0) multi_json (~> 1.14.1) nakayoshi_fork (~> 0.0.4) - net-ldap + net-ldap (~> 0.16.3) net-ntp net-ssh (~> 6.0) nokogiri (~> 1.10.9) diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js index 6da8f8b455e..e76a3693db9 100644 --- a/app/assets/javascripts/jobs/store/actions.js +++ b/app/assets/javascripts/jobs/store/actions.js @@ -2,7 +2,7 @@ import Visibility from 'visibilityjs'; import * as types from './mutation_types'; import axios from '~/lib/utils/axios_utils'; import Poll from '~/lib/utils/poll'; -import { setFaviconOverlay, resetFavicon } from '~/lib/utils/common_utils'; +import { setFaviconOverlay, resetFavicon } from '~/lib/utils/favicon'; import { deprecatedCreateFlash as flash } from '~/flash'; import { __ } from '~/locale'; import { diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 1c8cb423828..128ef5b335e 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -6,7 +6,6 @@ import { GlBreakpointInstance as breakpointInstance } from '@gitlab/ui/dist/util import $ from 'jquery'; import { isFunction, defer } from 'lodash'; import Cookies from 'js-cookie'; -import axios from './axios_utils'; import { getLocationHash } from './url_utility'; import { convertToCamelCase, convertToSnakeCase } from './text_utility'; import { isObject } from './type_utility'; @@ -548,92 +547,6 @@ export const backOff = (fn, timeout = 60000) => { }); }; -export const createOverlayIcon = (iconPath, overlayPath) => { - const faviconImage = document.createElement('img'); - - return new Promise((resolve) => { - faviconImage.onload = () => { - const size = 32; - - const canvas = document.createElement('canvas'); - canvas.width = size; - canvas.height = size; - - const context = canvas.getContext('2d'); - context.clearRect(0, 0, size, size); - context.drawImage( - faviconImage, - 0, - 0, - faviconImage.width, - faviconImage.height, - 0, - 0, - size, - size, - ); - - const overlayImage = document.createElement('img'); - overlayImage.onload = () => { - context.drawImage( - overlayImage, - 0, - 0, - overlayImage.width, - overlayImage.height, - 0, - 0, - size, - size, - ); - - const faviconWithOverlayUrl = canvas.toDataURL(); - - resolve(faviconWithOverlayUrl); - }; - overlayImage.src = overlayPath; - }; - faviconImage.src = iconPath; - }); -}; - -export const setFaviconOverlay = (overlayPath) => { - const faviconEl = document.getElementById('favicon'); - - if (!faviconEl) { - return null; - } - - const iconPath = faviconEl.getAttribute('data-original-href'); - - return createOverlayIcon(iconPath, overlayPath).then((faviconWithOverlayUrl) => - faviconEl.setAttribute('href', faviconWithOverlayUrl), - ); -}; - -export const resetFavicon = () => { - const faviconEl = document.getElementById('favicon'); - - if (faviconEl) { - const originalFavicon = faviconEl.getAttribute('data-original-href'); - faviconEl.setAttribute('href', originalFavicon); - } -}; - -export const setCiStatusFavicon = (pageUrl) => - axios - .get(pageUrl) - .then(({ data }) => { - if (data && data.favicon) { - return setFaviconOverlay(data.favicon); - } - return resetFavicon(); - }) - .catch((error) => { - resetFavicon(); - throw error; - }); - export const spriteIcon = (icon, className = '') => { const classAttribute = className.length > 0 ? `class="${className}"` : ''; diff --git a/app/assets/javascripts/lib/utils/favicon.js b/app/assets/javascripts/lib/utils/favicon.js new file mode 100644 index 00000000000..47596a76306 --- /dev/null +++ b/app/assets/javascripts/lib/utils/favicon.js @@ -0,0 +1,30 @@ +import { FaviconOverlayManager } from '@gitlab/favicon-overlay'; +import { memoize } from 'lodash'; + +// FaviconOverlayManager is a glorious singleton/static class. Let's start to encapsulate that with this helper. +const getDefaultFaviconManager = memoize(async () => { + await FaviconOverlayManager.initialize({ faviconSelector: '#favicon' }); + + return FaviconOverlayManager; +}); + +export const setFaviconOverlay = async (path) => { + const manager = await getDefaultFaviconManager(); + + manager.setFaviconOverlay(path); +}; + +export const resetFavicon = async () => { + const manager = await getDefaultFaviconManager(); + + manager.resetFaviconOverlay(); +}; + +/** + * Clears the cached memoization of the default manager. + * + * This is needed for determinism in tests. + */ +export const clearMemoizeCache = () => { + getDefaultFaviconManager.cache.clear(); +}; diff --git a/app/assets/javascripts/lib/utils/favicon_ci.js b/app/assets/javascripts/lib/utils/favicon_ci.js new file mode 100644 index 00000000000..613e2620e02 --- /dev/null +++ b/app/assets/javascripts/lib/utils/favicon_ci.js @@ -0,0 +1,16 @@ +import axios from './axios_utils'; +import { setFaviconOverlay, resetFavicon } from './favicon'; + +export const setCiStatusFavicon = (pageUrl) => + axios + .get(pageUrl) + .then(({ data }) => { + if (data && data.favicon) { + return setFaviconOverlay(data.favicon); + } + return resetFavicon(); + }) + .catch((error) => { + resetFavicon(); + throw error; + }); diff --git a/app/assets/javascripts/pipelines.js b/app/assets/javascripts/pipelines.js index 07abe714367..3b4e8d0e019 100644 --- a/app/assets/javascripts/pipelines.js +++ b/app/assets/javascripts/pipelines.js @@ -1,5 +1,5 @@ import LinkedTabs from './lib/utils/bootstrap_linked_tabs'; -import { setCiStatusFavicon } from './lib/utils/common_utils'; +import { setCiStatusFavicon } from './lib/utils/favicon_ci'; export default class Pipelines { constructor(options = {}) { diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index a620e155ff5..cebbc7f2466 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -43,7 +43,7 @@ import SourceBranchRemovalStatus from './components/source_branch_removal_status import TerraformPlan from './components/terraform/mr_widget_terraform_container.vue'; import GroupedCodequalityReportsApp from '../reports/codequality_report/grouped_codequality_reports_app.vue'; import GroupedTestReportsApp from '../reports/components/grouped_test_reports_app.vue'; -import { setFaviconOverlay } from '../lib/utils/common_utils'; +import { setFaviconOverlay } from '../lib/utils/favicon'; import GroupedAccessibilityReportsApp from '../reports/accessibility_report/grouped_accessibility_reports_app.vue'; import getStateQuery from './queries/get_state.query.graphql'; diff --git a/changelogs/unreleased/289925-subscription-expiration-banner-not-dismissable.yml b/changelogs/unreleased/289925-subscription-expiration-banner-not-dismissable.yml deleted file mode 100644 index 311784e05f8..00000000000 --- a/changelogs/unreleased/289925-subscription-expiration-banner-not-dismissable.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Fix(eetrialbanner): fix EE trial banner to allow dismiss' -merge_request: 50436 -author: -type: fixed diff --git a/changelogs/unreleased/293845-allow-nullable-digest.yml b/changelogs/unreleased/293845-allow-nullable-digest.yml deleted file mode 100644 index cc3d1198a1c..00000000000 --- a/changelogs/unreleased/293845-allow-nullable-digest.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix viewing container repositories with tags with corrupted manifest -merge_request: 50362 -author: -type: fixed diff --git a/changelogs/unreleased/294221-gitlab-pages-deployments-over-the-max-int-size-in-bytes-fail-to-up.yml b/changelogs/unreleased/294221-gitlab-pages-deployments-over-the-max-int-size-in-bytes-fail-to-up.yml deleted file mode 100644 index 233d52f0df5..00000000000 --- a/changelogs/unreleased/294221-gitlab-pages-deployments-over-the-max-int-size-in-bytes-fail-to-up.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Change pages deployments size to bigint -merge_request: 50262 -author: -type: fixed diff --git a/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml b/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml deleted file mode 100644 index c280ccb21ec..00000000000 --- a/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix project access token regression -merge_request: 50800 -author: -type: fixed diff --git a/changelogs/unreleased/fix-canary-update-service-to-invalidate-cache.yml b/changelogs/unreleased/fix-canary-update-service-to-invalidate-cache.yml deleted file mode 100644 index aea2dec8268..00000000000 --- a/changelogs/unreleased/fix-canary-update-service-to-invalidate-cache.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix Canary Ingress weight is not reflected on UI immediately -merge_request: 50246 -author: -type: fixed diff --git a/changelogs/unreleased/id-bump-net-ldap.yml b/changelogs/unreleased/id-bump-net-ldap.yml new file mode 100644 index 00000000000..556b20c4309 --- /dev/null +++ b/changelogs/unreleased/id-bump-net-ldap.yml @@ -0,0 +1,5 @@ +--- +title: Update net-ldap gem version +merge_request: 50888 +author: +type: other diff --git a/changelogs/unreleased/update-helm-2-to-2-17-0.yml b/changelogs/unreleased/update-helm-2-to-2-17-0.yml deleted file mode 100644 index d6941878b48..00000000000 --- a/changelogs/unreleased/update-helm-2-to-2-17-0.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update Helm 2 version to 2.17.0 -merge_request: 50547 -author: -type: fixed diff --git a/jest.config.base.js b/jest.config.base.js index 3ac6aa9091c..6ad63dcf15f 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -91,7 +91,7 @@ module.exports = (path) => { '^.+\\.(md|zip|png)$': 'jest-raw-loader', }, transformIgnorePatterns: [ - 'node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor|monaco-yaml|fast-mersenne-twister)/)', + 'node_modules/(?!(@gitlab/ui|@gitlab/favicon-overlay|bootstrap-vue|three|monaco-editor|monaco-yaml|fast-mersenne-twister)/)', ], timers: 'fake', testEnvironment: '/spec/frontend/environment.js', diff --git a/package.json b/package.json index 737d9071b52..e9385625b37 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@babel/plugin-syntax-import-meta": "^7.10.1", "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", + "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "1.178.0", "@gitlab/tributejs": "1.0.0", "@gitlab/ui": "25.4.0", diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js index 84aa9c32288..90222f0f718 100644 --- a/spec/frontend/lib/utils/common_utils_spec.js +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -514,27 +514,6 @@ describe('common_utils', () => { }); }); - describe('resetFavicon', () => { - beforeEach(() => { - const favicon = document.createElement('link'); - favicon.setAttribute('id', 'favicon'); - favicon.setAttribute('data-original-href', 'default/favicon'); - document.body.appendChild(favicon); - }); - - afterEach(() => { - document.body.removeChild(document.getElementById('favicon')); - }); - - it('should reset page favicon to the default icon', () => { - const favicon = document.getElementById('favicon'); - favicon.setAttribute('href', 'new/favicon'); - commonUtils.resetFavicon(); - - expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon'); - }); - }); - describe('spriteIcon', () => { let beforeGon; diff --git a/spec/frontend/lib/utils/favicon_ci_spec.js b/spec/frontend/lib/utils/favicon_ci_spec.js new file mode 100644 index 00000000000..e35b008b862 --- /dev/null +++ b/spec/frontend/lib/utils/favicon_ci_spec.js @@ -0,0 +1,50 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { setFaviconOverlay, resetFavicon } from '~/lib/utils/favicon'; +import { setCiStatusFavicon } from '~/lib/utils/favicon_ci'; + +jest.mock('~/lib/utils/favicon'); + +const TEST_URL = '/test/pipelinable/1'; +const TEST_FAVICON = '/favicon.test.ico'; + +describe('~/lib/utils/favicon_ci', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + mock = null; + }); + + describe('setCiStatusFavicon', () => { + it.each` + response | setFaviconOverlayCalls | resetFaviconCalls + ${{}} | ${[]} | ${[[]]} + ${{ favicon: TEST_FAVICON }} | ${[[TEST_FAVICON]]} | ${[]} + `( + 'with response=$response', + async ({ response, setFaviconOverlayCalls, resetFaviconCalls }) => { + mock.onGet(TEST_URL).replyOnce(200, response); + + expect(setFaviconOverlay).not.toHaveBeenCalled(); + expect(resetFavicon).not.toHaveBeenCalled(); + + await setCiStatusFavicon(TEST_URL); + + expect(setFaviconOverlay.mock.calls).toEqual(setFaviconOverlayCalls); + expect(resetFavicon.mock.calls).toEqual(resetFaviconCalls); + }, + ); + + it('with error', async () => { + mock.onGet(TEST_URL).replyOnce(500); + + await expect(setCiStatusFavicon(TEST_URL)).rejects.toEqual(expect.any(Error)); + expect(resetFavicon).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/frontend/lib/utils/favicon_spec.js b/spec/frontend/lib/utils/favicon_spec.js new file mode 100644 index 00000000000..1b986432b8a --- /dev/null +++ b/spec/frontend/lib/utils/favicon_spec.js @@ -0,0 +1,39 @@ +import { FaviconOverlayManager } from '@gitlab/favicon-overlay'; +import * as faviconUtils from '~/lib/utils/favicon'; + +jest.mock('@gitlab/favicon-overlay'); + +describe('~/lib/utils/favicon', () => { + afterEach(() => { + faviconUtils.clearMemoizeCache(); + }); + + describe.each` + fnName | managerFn | args + ${'setFaviconOverlay'} | ${FaviconOverlayManager.setFaviconOverlay} | ${['test']} + ${'resetFavicon'} | ${FaviconOverlayManager.resetFaviconOverlay} | ${[]} + `('$fnName', ({ fnName, managerFn, args }) => { + const call = () => faviconUtils[fnName](...args); + + it('initializes only once when called', async () => { + expect(FaviconOverlayManager.initialize).not.toHaveBeenCalled(); + + // Call twice so we can make sure initialize is only called once + await call(); + await call(); + + expect(FaviconOverlayManager.initialize).toHaveBeenCalledWith({ + faviconSelector: '#favicon', + }); + expect(FaviconOverlayManager.initialize).toHaveBeenCalledTimes(1); + }); + + it('passes call to manager', async () => { + expect(managerFn).not.toHaveBeenCalled(); + + await call(); + + expect(managerFn).toHaveBeenCalledWith(...args); + }); + }); +}); diff --git a/spec/frontend/lib/utils/mock_data.js b/spec/frontend/lib/utils/mock_data.js index c466b0cd1ed..df1f79529e7 100644 --- a/spec/frontend/lib/utils/mock_data.js +++ b/spec/frontend/lib/utils/mock_data.js @@ -3,6 +3,3 @@ export const faviconDataUrl = export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC'; - -export const faviconWithOverlayDataUrl = - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGtElEQVRYR8WXf3CT9R3H35/nSdIQIktrCf0RStI0FYRjVBAccxTq5MDBKUoz4ZyjbPO87q4yBsPDMdExTjlvIsdQexyI0oMBeuKhdjsNHhwcMgpjIlublLIm/UlJKZSSJs/z/e6+T5v0CQ22wB/7/pPck8/383l9fj6fEOec8H88NAjAS1LwknsFSVLU8WXd1rtm85LUeKnwGQKzjj3s33azvsEAAEIlnn8ByHL4/Pa7BgAQLCm8QOBOh88vDQkQeMxjMkcQEYKqYsyJWWPhgs/80TsFafzROJtkNIXFfYI0pfXqPeennjqlxPUNikBoTuEmEF+lCRBV3G0aQiWFrwH8d30AWJubGdiEfZzdGqDEEwbICnADQGGHry7zTr0X94IlnnMACggwAWh0+PxOvb5EBGqmTTNkj7ySxWS62C+g5Usm1Zn95YXG24UQ+r5n75Li6Ux4LBkyc7/4t5YSLSr6Lgg9UvBLcKocMEYKON/gGB3YoA/bcGFCczzLQdieLE9bHL66FakBSjzCU0cSAHDa4at7aLhG9XLBEk8zAVnxZxyIEhBy+PwFgwAafpxvNzK5NZUhrX28JA07Cl6SmtvcOUwm4ZAouHj7ad+jMrN1dqb3iG7oS4EYPh2etQS+XiesC8TQ3ZD3yZJsHuUPgbMcI+ej5v3ncv5PasNlk1p7JJnzJL+I0/O5h+u0VCdqIDi78AQRHuirft3hYJzQPvawPydVdPI+/OnTnNNKBjYVXHRa8rFFGeb4w1he0wZ7d/84IXTEhxzxUsgitB2LPFGwvgGUfLSeZUpEXqEqrIdz0nr4iHOUfeOccb/tNMtutzWHPeWcJc0aMxm5lkxYDGloj1zB+Sv/RXXTSXzaeBwSY3j+bHNv2bdtMYCbpHtRkNFd36xFQN3tXkZhvgP1fdPi5kMEXL4oIXKVAA58M8aCVQs84BYLXi5aDq+zGJTqYr+i4PV2vHxmJ/7WUoOn2i/jz6yhW7JjrdSV8U4fQFV+I2Q4UIsedMCSSlcsgp72WtnSajOhzDsBNtsYfFD8e+Rbs4fdIG98uw9vnj+AX7FWvk4NHZOXXphF/INx2SpJIU2L8L4GDAoMwlP9kWSg6awcKVs83tyUnY5Dj75+W8bjutae3o5d9X/HTiWAuUtOS6RUOR8Hp48TxjgU/AMSeKJ1Ej/tMWXG1sxwGt98sBxe5+xhe64XVLiK2Z9XwNgdRLXyzQsC4ENwelIHAFxDBOdh1qdCdNLCoon8RnY+HZ6/+TtzPhTZweAxlJ94C5VqoI2U3a7rACzJjQqgBd24CGscos1kxPQZ38fqSU/jhQkDvN9lrKG7FeUnNuPVKcvwYOb4hGgvi2HSx8vwRKyJkVLl+hk43gdBAcfADBD1cA4RXIdZ1EN1Zjqem+DGoUc2oigjMUlvaV8YL/1qPVpuhOG+JwdH5m1Okn3m6Eacaz3V2jeI9uTbVYY6AKOSKw8MX0MBg2lXjh3r3Hk4s7ASdrMtSWxnoBpZIzIwP3e69lxv3Gay4q/F6zDJ5kq6s6amEnsafJ0Db8P9JKkx1w5wPJuY36IToojgNMzb8rLwmsuB2kW7YDWMSCgTg+YXx9+AQZKxdUaFZiju+a2Mi8uvnH0f2/2f9g4AVE4z4LlTilrlehag9xIpEam4jO4DXfdaV97nwtH5byW137VYD5Yc2YAz4YAGIYx2RLq0z1Sex8l//fUWfBI83jh4Kd1PEuAwqVGjWEwSS+nJJmt0sWu86d0frMQCR/LbWQ8hDAxlXMgUV69Q67ubv0q5FUNAlHKmVLnXE/gfREpUiaQHqAizXbO0UN98BMTSo39Cw7UW7E2Rc728qJGHP68ASbQyNYCQTkAUzCSwQ+CwvSjnsQPGLOnI/C0YO3Lwxq5yhhtqb1KNpGqT1TXvigJU0jh33xpAf7NymoGNDJ9sJtPkYuNkqTh7KnY8vGaoeZPy93+GA1joe4kzzv/SVLqvYngA/dFgVfnlb8tjtm6Ux+I39y/Gqone24IQM+GxL15UO3q7WrhsnhJatCs8PAC9md3OrPK0goaDyEj7uXsuXi0qg4HkIUGE52XHNqmXIl0RGOiHoUV7xb+v5K14SC39At79Ximdhc8ekjImuiyjsXryUszLnY40yThIhSi4bbUHsbfBJ6ZKE5dpQdz4HQOgf2a8tLvklY+M6cuvSnJummxSZ46+X+7biMzaRnSu84IauNYsE5HCOX+HDCPWi7DrKW8/BTcVZ2UN8Me57kc5448TaCYR5XJwC0BtHMwPjs/SgAP1pfuCqSL8Pxhr/wunLWAOAAAAAElFTkSuQmCC'; diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index 39ee8a3822a..1c786b32f90 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -13,6 +13,9 @@ import './helpers/dom_shims'; import './helpers/jquery'; import '~/commons/bootstrap'; +// This module has some fairly decent visual test coverage in it's own repository. +jest.mock('@gitlab/favicon-overlay'); + process.on('unhandledRejection', global.promiseRejectionHandler); setupManualMocks(); diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index b4083385117..0344ac0ab68 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -7,6 +7,7 @@ import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; import eventHub from '~/vue_merge_request_widget/event_hub'; import notify from '~/lib/utils/notify'; import SmartInterval from '~/smart_interval'; +import { setFaviconOverlay } from '~/lib/utils/favicon'; import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; import mockData from './mock_data'; import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data'; @@ -14,6 +15,8 @@ import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/consta jest.mock('~/smart_interval'); +jest.mock('~/lib/utils/favicon'); + const returnPromise = (data) => new Promise((resolve) => { resolve({ @@ -421,21 +424,12 @@ describe('mrWidgetOptions', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should call setFavicon method', (done) => { + it('should call setFavicon method', async () => { vm.mr.ciStatusFaviconPath = overlayDataUrl; - vm.setFaviconHelper() - .then(() => { - /* - It would be better if we'd could mock commonUtils.setFaviconURL - with a spy and test that it was called. We are doing the following - tests as a proxy to show that the function has been called - */ - expect(faviconElement.getAttribute('href')).not.toEqual(null); - expect(faviconElement.getAttribute('href')).not.toEqual(overlayDataUrl); - expect(faviconElement.getAttribute('href')).not.toEqual(faviconDataUrl); - }) - .then(done) - .catch(done.fail); + + await vm.setFaviconHelper(); + + expect(setFaviconOverlay).toHaveBeenCalledWith(overlayDataUrl); }); it('should not call setFavicon when there is no ciStatusFaviconPath', (done) => { diff --git a/spec/javascripts/lib/utils/browser_spec.js b/spec/javascripts/lib/utils/browser_spec.js index d219ccfacaa..f41fa2503b1 100644 --- a/spec/javascripts/lib/utils/browser_spec.js +++ b/spec/javascripts/lib/utils/browser_spec.js @@ -5,30 +5,8 @@ * https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment */ -import MockAdapter from 'axios-mock-adapter'; import { GlBreakpointInstance as breakpointInstance } from '@gitlab/ui/dist/utils'; -import axios from '~/lib/utils/axios_utils'; import * as commonUtils from '~/lib/utils/common_utils'; -import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data'; - -const PIXEL_TOLERANCE = 0.2; - -/** - * Loads a data URL as the src of an - * {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image|Image} - * and resolves to that Image once loaded. - * - * @param url - * @returns {Promise} - */ -const urlToImage = (url) => - new Promise((resolve) => { - const img = new Image(); - img.onload = function () { - resolve(img); - }; - img.src = url; - }); describe('common_utils browser specific specs', () => { describe('contentTop', () => { @@ -63,90 +41,6 @@ describe('common_utils browser specific specs', () => { }); }); - describe('createOverlayIcon', () => { - it('should return the favicon with the overlay', (done) => { - commonUtils - .createOverlayIcon(faviconDataUrl, overlayDataUrl) - .then((url) => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)])) - .then(([actual, expected]) => { - expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE); - done(); - }) - .catch(done.fail); - }); - }); - - describe('setFaviconOverlay', () => { - beforeEach(() => { - const favicon = document.createElement('link'); - favicon.setAttribute('id', 'favicon'); - favicon.setAttribute('data-original-href', faviconDataUrl); - document.body.appendChild(favicon); - }); - - afterEach(() => { - document.body.removeChild(document.getElementById('favicon')); - }); - - it('should set page favicon to provided favicon overlay', (done) => { - commonUtils - .setFaviconOverlay(overlayDataUrl) - .then(() => document.getElementById('favicon').getAttribute('href')) - .then((url) => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)])) - .then(([actual, expected]) => { - expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE); - done(); - }) - .catch(done.fail); - }); - }); - - describe('setCiStatusFavicon', () => { - const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`; - let mock; - - beforeEach(() => { - const favicon = document.createElement('link'); - favicon.setAttribute('id', 'favicon'); - favicon.setAttribute('href', 'null'); - favicon.setAttribute('data-original-href', faviconDataUrl); - document.body.appendChild(favicon); - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - document.body.removeChild(document.getElementById('favicon')); - }); - - it('should reset favicon in case of error', (done) => { - mock.onGet(BUILD_URL).replyOnce(500); - - commonUtils.setCiStatusFavicon(BUILD_URL).catch(() => { - const favicon = document.getElementById('favicon'); - - expect(favicon.getAttribute('href')).toEqual(faviconDataUrl); - done(); - }); - }); - - it('should set page favicon to CI status favicon based on provided status', (done) => { - mock.onGet(BUILD_URL).reply(200, { - favicon: overlayDataUrl, - }); - - commonUtils - .setCiStatusFavicon(BUILD_URL) - .then(() => document.getElementById('favicon').getAttribute('href')) - .then((url) => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)])) - .then(([actual, expected]) => { - expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE); - done(); - }) - .catch(done.fail); - }); - }); - describe('isInViewport', () => { let el; diff --git a/yarn.lock b/yarn.lock index 2d0b2dbd089..4b981c4280e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -861,6 +861,11 @@ eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" +"@gitlab/favicon-overlay@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984" + integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q== + "@gitlab/svgs@1.178.0": version "1.178.0" resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.178.0.tgz#069edb8abb4c7137d48f527592476655f066538b"