diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
commit | b39512ed755239198a9c294b6a45e65c05900235 (patch) | |
tree | d234a3efade1de67c46b9e5a38ce813627726aa7 /spec/frontend/vue_mr_widget/mr_widget_options_spec.js | |
parent | d31474cf3b17ece37939d20082b07f6657cc79a9 (diff) |
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'spec/frontend/vue_mr_widget/mr_widget_options_spec.js')
-rw-r--r-- | spec/frontend/vue_mr_widget/mr_widget_options_spec.js | 1266 |
1 files changed, 0 insertions, 1266 deletions
diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js deleted file mode 100644 index b3af5eba364..00000000000 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ /dev/null @@ -1,1266 +0,0 @@ -import { GlBadge, GlLink, GlIcon, GlButton, GlDropdown } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import Vue, { nextTick } from 'vue'; -import VueApollo from 'vue-apollo'; -import * as Sentry from '@sentry/browser'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import { securityReportMergeRequestDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data'; -import api from '~/api'; -import axios from '~/lib/utils/axios_utils'; -import Poll from '~/lib/utils/poll'; -import { setFaviconOverlay } from '~/lib/utils/favicon'; -import notify from '~/lib/utils/notify'; -import SmartInterval from '~/smart_interval'; -import { - registerExtension, - registeredExtensions, -} from '~/vue_merge_request_widget/components/extensions'; -import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants'; -import eventHub from '~/vue_merge_request_widget/event_hub'; -import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; -import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; -import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue'; -import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql'; -import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data'; -import mockData from './mock_data'; -import { - workingExtension, - collapsedDataErrorExtension, - fullDataErrorExtension, - fullReportExtension, - noTelemetryExtension, - pollingExtension, - pollingFullDataExtension, - pollingErrorExtension, - multiPollingExtension, -} from './test_extensions'; - -jest.mock('~/api.js'); - -jest.mock('~/smart_interval'); - -jest.mock('~/lib/utils/favicon'); - -jest.mock('@sentry/browser', () => ({ - setExtra: jest.fn(), - setExtras: jest.fn(), - captureMessage: jest.fn(), - captureException: jest.fn(), -})); - -Vue.use(VueApollo); - -describe('MrWidgetOptions', () => { - let wrapper; - let mock; - - const COLLABORATION_MESSAGE = 'Members who can merge are allowed to add commits'; - const findExtensionToggleButton = () => - wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]'); - const findExtensionLink = (linkHref) => - wrapper.find(`[data-testid="widget-extension"] [href="${linkHref}"]`); - - beforeEach(() => { - gl.mrWidgetData = { ...mockData }; - gon.features = { asyncMrWidget: true }; - - mock = new MockAdapter(axios); - mock.onGet(mockData.merge_request_widget_path).reply(() => [200, { ...mockData }]); - mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, { ...mockData }]); - }); - - afterEach(() => { - mock.restore(); - wrapper.destroy(); - - gl.mrWidgetData = {}; - gon.features = {}; - }); - - const createComponent = (mrData = mockData, options = {}) => { - wrapper = mount(MrWidgetOptions, { - propsData: { - mrData: { ...mrData }, - }, - ...options, - }); - - return axios.waitForAll(); - }; - - const findSuggestPipeline = () => wrapper.find('[data-testid="mr-suggest-pipeline"]'); - const findSuggestPipelineButton = () => findSuggestPipeline().find('button'); - const findSecurityMrWidget = () => wrapper.find('[data-testid="security-mr-widget"]'); - - describe('default', () => { - beforeEach(() => { - jest.spyOn(document, 'dispatchEvent'); - return createComponent(); - }); - - describe('data', () => { - it('should instantiate Store and Service', () => { - expect(wrapper.vm.mr).toBeDefined(); - expect(wrapper.vm.service).toBeDefined(); - }); - }); - - describe('computed', () => { - describe('componentName', () => { - it.each` - state | componentName - ${'merged'} | ${'mr-widget-merged'} - ${'conflicts'} | ${'mr-widget-conflicts'} - ${'shaMismatch'} | ${'sha-mismatch'} - `('should translate $state into $componentName', ({ state, componentName }) => { - wrapper.vm.mr.state = state; - - expect(wrapper.vm.componentName).toEqual(componentName); - }); - }); - - describe('shouldRenderPipelines', () => { - it('should return true when hasCI is true', () => { - wrapper.vm.mr.hasCI = true; - - expect(wrapper.vm.shouldRenderPipelines).toBeTruthy(); - }); - - it('should return false when hasCI is false', () => { - wrapper.vm.mr.hasCI = false; - - expect(wrapper.vm.shouldRenderPipelines).toBeFalsy(); - }); - }); - - describe('shouldRenderRelatedLinks', () => { - it('should return false for the initial data', () => { - expect(wrapper.vm.shouldRenderRelatedLinks).toBeFalsy(); - }); - - it('should return true if there is relatedLinks in MR', () => { - Vue.set(wrapper.vm.mr, 'relatedLinks', {}); - - expect(wrapper.vm.shouldRenderRelatedLinks).toBeTruthy(); - }); - }); - - describe('shouldRenderSourceBranchRemovalStatus', () => { - beforeEach(() => { - wrapper.vm.mr.state = 'readyToMerge'; - }); - - it('should return true when cannot remove source branch and branch will be removed', () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - - expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(true); - }); - - it('should return false when can remove source branch and branch will be removed', () => { - wrapper.vm.mr.canRemoveSourceBranch = true; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - - expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); - }); - - it('should return false when cannot remove source branch and branch will not be removed', () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = false; - - expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); - }); - - it('should return false when in merged state', () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - wrapper.vm.mr.state = 'merged'; - - expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); - }); - - it('should return false when in nothing to merge state', () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - wrapper.vm.mr.state = 'nothingToMerge'; - - expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); - }); - }); - - describe('shouldRenderCollaborationStatus', () => { - describe('when collaboration is allowed', () => { - beforeEach(() => { - wrapper.vm.mr.allowCollaboration = true; - }); - - describe('when merge request is opened', () => { - beforeEach(() => { - wrapper.vm.mr.isOpen = true; - return nextTick(); - }); - - it('should render collaboration status', () => { - expect(wrapper.text()).toContain(COLLABORATION_MESSAGE); - }); - }); - - describe('when merge request is not opened', () => { - beforeEach(() => { - wrapper.vm.mr.isOpen = false; - return nextTick(); - }); - - it('should not render collaboration status', () => { - expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE); - }); - }); - }); - - describe('when collaboration is not allowed', () => { - beforeEach(() => { - wrapper.vm.mr.allowCollaboration = false; - }); - - describe('when merge request is opened', () => { - beforeEach(() => { - wrapper.vm.mr.isOpen = true; - return nextTick(); - }); - - it('should not render collaboration status', () => { - expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE); - }); - }); - }); - }); - - describe('showMergePipelineForkWarning', () => { - describe('when the source project and target project are the same', () => { - beforeEach(() => { - Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true); - Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); - Vue.set(wrapper.vm.mr, 'targetProjectId', 1); - return nextTick(); - }); - - it('should be false', () => { - expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false); - }); - }); - - describe('when merge pipelines are not enabled', () => { - beforeEach(() => { - Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', false); - Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); - Vue.set(wrapper.vm.mr, 'targetProjectId', 2); - return nextTick(); - }); - - it('should be false', () => { - expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false); - }); - }); - - describe('when merge pipelines are enabled _and_ the source project and target project are different', () => { - beforeEach(() => { - Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true); - Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); - Vue.set(wrapper.vm.mr, 'targetProjectId', 2); - return nextTick(); - }); - - it('should be true', () => { - expect(wrapper.vm.showMergePipelineForkWarning).toEqual(true); - }); - }); - }); - - describe('formattedHumanAccess', () => { - it('when user is a tool admin but not a member of project', () => { - wrapper.vm.mr.humanAccess = null; - - expect(wrapper.vm.formattedHumanAccess).toEqual(''); - }); - - it('when user a member of the project', () => { - wrapper.vm.mr.humanAccess = 'Owner'; - - expect(wrapper.vm.formattedHumanAccess).toEqual('owner'); - }); - }); - }); - - describe('methods', () => { - describe('checkStatus', () => { - let cb; - let isCbExecuted; - - beforeEach(() => { - jest.spyOn(wrapper.vm.service, 'checkStatus').mockResolvedValue({ data: mockData }); - jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation(() => {}); - jest.spyOn(wrapper.vm, 'handleNotification').mockImplementation(() => {}); - - isCbExecuted = false; - cb = () => { - isCbExecuted = true; - }; - }); - - it('should tell service to check status if document is visible', () => { - wrapper.vm.checkStatus(cb); - - return nextTick().then(() => { - expect(wrapper.vm.service.checkStatus).toHaveBeenCalled(); - expect(wrapper.vm.mr.setData).toHaveBeenCalled(); - expect(wrapper.vm.handleNotification).toHaveBeenCalledWith(mockData); - expect(isCbExecuted).toBeTruthy(); - }); - }); - }); - - describe('initPolling', () => { - it('should call SmartInterval', () => { - wrapper.vm.initPolling(); - - expect(SmartInterval).toHaveBeenCalledWith( - expect.objectContaining({ - callback: wrapper.vm.checkStatus, - }), - ); - }); - }); - - describe('initDeploymentsPolling', () => { - it('should call SmartInterval', () => { - wrapper.vm.initDeploymentsPolling(); - - expect(SmartInterval).toHaveBeenCalledWith( - expect.objectContaining({ - callback: wrapper.vm.fetchPreMergeDeployments, - }), - ); - }); - }); - - describe('fetchDeployments', () => { - it('should fetch deployments', () => { - jest - .spyOn(wrapper.vm.service, 'fetchDeployments') - .mockResolvedValue({ data: [{ id: 1, status: SUCCESS }] }); - - wrapper.vm.fetchPreMergeDeployments(); - - return nextTick().then(() => { - expect(wrapper.vm.service.fetchDeployments).toHaveBeenCalled(); - expect(wrapper.vm.mr.deployments.length).toEqual(1); - expect(wrapper.vm.mr.deployments[0].id).toBe(1); - }); - }); - }); - - describe('fetchActionsContent', () => { - it('should fetch content of Cherry Pick and Revert modals', () => { - jest - .spyOn(wrapper.vm.service, 'fetchMergeActionsContent') - .mockResolvedValue({ data: 'hello world' }); - - wrapper.vm.fetchActionsContent(); - - return nextTick().then(() => { - expect(wrapper.vm.service.fetchMergeActionsContent).toHaveBeenCalled(); - expect(document.body.textContent).toContain('hello world'); - expect(document.dispatchEvent).toHaveBeenCalledWith( - new CustomEvent('merged:UpdateActions'), - ); - }); - }); - }); - - describe('bindEventHubListeners', () => { - it.each` - event | method | methodArgs - ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${(x) => [x]} - ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${(x) => [x, true]} - ${'FetchActionsContent'} | ${'fetchActionsContent'} | ${() => []} - ${'EnablePolling'} | ${'resumePolling'} | ${() => []} - ${'DisablePolling'} | ${'stopPolling'} | ${() => []} - `('should bind to $event', ({ event, method, methodArgs }) => { - jest.spyOn(wrapper.vm, method).mockImplementation(); - - const eventArg = {}; - eventHub.$emit(event, eventArg); - - expect(wrapper.vm[method]).toHaveBeenCalledWith(...methodArgs(eventArg)); - }); - - it('should bind to SetBranchRemoveFlag', () => { - expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(false); - - eventHub.$emit('SetBranchRemoveFlag', [true]); - - expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(true); - }); - - it('should bind to FailedToMerge', () => { - wrapper.vm.mr.state = ''; - wrapper.vm.mr.mergeError = ''; - - const mergeError = 'Something bad happened!'; - eventHub.$emit('FailedToMerge', mergeError); - - expect(wrapper.vm.mr.state).toBe('failedToMerge'); - expect(wrapper.vm.mr.mergeError).toBe(mergeError); - }); - - it('should bind to UpdateWidgetData', () => { - jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation(); - - const data = { ...mockData }; - eventHub.$emit('UpdateWidgetData', data); - - expect(wrapper.vm.mr.setData).toHaveBeenCalledWith(data); - }); - }); - - describe('setFavicon', () => { - let faviconElement; - - beforeEach(() => { - const favicon = document.createElement('link'); - favicon.setAttribute('id', 'favicon'); - favicon.dataset.originalHref = faviconDataUrl; - document.body.appendChild(favicon); - - faviconElement = document.getElementById('favicon'); - }); - - afterEach(() => { - document.body.removeChild(document.getElementById('favicon')); - }); - - it('should call setFavicon method', async () => { - wrapper.vm.mr.ciStatusFaviconPath = overlayDataUrl; - - await wrapper.vm.setFaviconHelper(); - - expect(setFaviconOverlay).toHaveBeenCalledWith(overlayDataUrl); - }); - - it('should not call setFavicon when there is no ciStatusFaviconPath', async () => { - wrapper.vm.mr.ciStatusFaviconPath = null; - await wrapper.vm.setFaviconHelper(); - expect(faviconElement.getAttribute('href')).toEqual(null); - }); - }); - - describe('handleNotification', () => { - const data = { - ci_status: 'running', - title: 'title', - pipeline: { details: { status: { label: 'running-label' } } }, - }; - - beforeEach(() => { - jest.spyOn(notify, 'notifyMe').mockImplementation(() => {}); - - wrapper.vm.mr.ciStatus = 'failed'; - wrapper.vm.mr.gitlabLogo = 'logo.png'; - }); - - it('should call notifyMe', () => { - wrapper.vm.handleNotification(data); - - expect(notify.notifyMe).toHaveBeenCalledWith( - 'Pipeline running-label', - 'Pipeline running-label for "title"', - 'logo.png', - ); - }); - - it('should not call notifyMe if the status has not changed', () => { - wrapper.vm.mr.ciStatus = data.ci_status; - - wrapper.vm.handleNotification(data); - - expect(notify.notifyMe).not.toHaveBeenCalled(); - }); - - it('should not notify if no pipeline provided', () => { - wrapper.vm.handleNotification({ - ...data, - pipeline: undefined, - }); - - expect(notify.notifyMe).not.toHaveBeenCalled(); - }); - }); - - describe('resumePolling', () => { - it('should call stopTimer on pollingInterval', () => { - jest.spyOn(wrapper.vm.pollingInterval, 'resume').mockImplementation(() => {}); - - wrapper.vm.resumePolling(); - - expect(wrapper.vm.pollingInterval.resume).toHaveBeenCalled(); - }); - }); - - describe('stopPolling', () => { - it('should call stopTimer on pollingInterval', () => { - jest.spyOn(wrapper.vm.pollingInterval, 'stopTimer').mockImplementation(() => {}); - - wrapper.vm.stopPolling(); - - expect(wrapper.vm.pollingInterval.stopTimer).toHaveBeenCalled(); - }); - }); - }); - - describe('rendering relatedLinks', () => { - beforeEach(() => { - return createComponent({ - ...mockData, - issues_links: { - closing: ` - <a class="close-related-link" href="#"> - Close - </a> - `, - }, - }); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders if there are relatedLinks', () => { - expect(wrapper.find('.close-related-link').exists()).toBe(true); - }); - - it('does not render if state is nothingToMerge', async () => { - wrapper.vm.mr.state = stateKey.nothingToMerge; - await nextTick(); - expect(wrapper.find('.close-related-link').exists()).toBe(false); - }); - }); - - describe('rendering source branch removal status', () => { - it('renders when user cannot remove branch and branch should be removed', async () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - wrapper.vm.mr.state = 'readyToMerge'; - - await nextTick(); - const tooltip = wrapper.find('[data-testid="question-o-icon"]'); - - expect(wrapper.text()).toContain('Deletes the source branch'); - expect(tooltip.attributes('title')).toBe( - 'A user with write access to the source branch selected this option', - ); - }); - - it('does not render in merged state', async () => { - wrapper.vm.mr.canRemoveSourceBranch = false; - wrapper.vm.mr.shouldRemoveSourceBranch = true; - wrapper.vm.mr.state = 'merged'; - - await nextTick(); - expect(wrapper.text()).toContain('The source branch has been deleted'); - expect(wrapper.text()).not.toContain('Deletes the source branch'); - }); - }); - - describe('rendering deployments', () => { - const changes = [ - { - path: 'index.html', - external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/index.html', - }, - { - path: 'imgs/gallery.html', - external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/imgs/gallery.html', - }, - { - path: 'about/', - external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/about/', - }, - ]; - const deploymentMockData = { - id: 15, - name: 'review/diplo', - url: '/root/acets-review-apps/environments/15', - stop_url: '/root/acets-review-apps/environments/15/stop', - metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics', - metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics', - external_url: 'http://diplo.', - external_url_formatted: 'diplo.', - deployed_at: '2017-03-22T22:44:42.258Z', - deployed_at_formatted: 'Mar 22, 2017 10:44pm', - changes, - status: SUCCESS, - }; - - beforeEach(() => { - wrapper.vm.mr.deployments.push( - { - ...deploymentMockData, - }, - { - ...deploymentMockData, - id: deploymentMockData.id + 1, - }, - ); - - return nextTick(); - }); - - it('renders multiple deployments', () => { - expect(wrapper.findAll('.deploy-heading').length).toBe(2); - }); - - it('renders dropdpown with multiple file changes', () => { - expect( - wrapper.find('.js-mr-wigdet-deployment-dropdown').findAll('.js-filtered-dropdown-result') - .length, - ).toEqual(changes.length); - }); - }); - - describe('code quality widget', () => { - beforeEach(() => { - jest.spyOn(document, 'dispatchEvent'); - }); - it('renders the component when refactorCodeQualityExtension is false', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: false }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - - it('does not render the component when refactorCodeQualityExtension is true', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: true }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - }); - - describe('pipeline for target branch after merge', () => { - describe('with information for target branch pipeline', () => { - beforeEach(() => { - wrapper.vm.mr.state = 'merged'; - wrapper.vm.mr.mergePipeline = { - id: 127, - user: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: null, - web_url: 'http://localhost:3000/root', - status_tooltip_html: null, - path: '/root', - }, - active: true, - coverage: null, - source: 'push', - created_at: '2018-10-22T11:41:35.186Z', - updated_at: '2018-10-22T11:41:35.433Z', - path: '/root/ci-web-terminal/pipelines/127', - flags: { - latest: true, - stuck: true, - auto_devops: false, - yaml_errors: false, - retryable: false, - cancelable: true, - failure_reason: false, - }, - details: { - status: { - icon: 'status_pending', - text: 'pending', - label: 'pending', - group: 'pending', - tooltip: 'pending', - has_details: true, - details_path: '/root/ci-web-terminal/pipelines/127', - illustration: null, - favicon: - '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png', - }, - duration: null, - finished_at: null, - stages: [ - { - name: 'test', - title: 'test: pending', - status: { - icon: 'status_pending', - text: 'pending', - label: 'pending', - group: 'pending', - tooltip: 'pending', - has_details: true, - details_path: '/root/ci-web-terminal/pipelines/127#test', - illustration: null, - favicon: - '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png', - }, - path: '/root/ci-web-terminal/pipelines/127#test', - dropdown_path: '/root/ci-web-terminal/pipelines/127/stage.json?stage=test', - }, - ], - artifacts: [], - manual_actions: [], - scheduled_actions: [], - }, - ref: { - name: 'main', - path: '/root/ci-web-terminal/commits/main', - tag: false, - branch: true, - }, - commit: { - id: 'aa1939133d373c94879becb79d91828a892ee319', - short_id: 'aa193913', - title: "Merge branch 'main-test' into 'main'", - created_at: '2018-10-22T11:41:33.000Z', - parent_ids: [ - '4622f4dd792468993003caf2e3be978798cbe096', - '76598df914cdfe87132d0c3c40f80db9fa9396a4', - ], - message: - "Merge branch 'main-test' into 'main'\n\nUpdate .gitlab-ci.yml\n\nSee merge request root/ci-web-terminal!1", - author_name: 'Administrator', - author_email: 'admin@example.com', - authored_date: '2018-10-22T11:41:33.000Z', - committer_name: 'Administrator', - committer_email: 'admin@example.com', - committed_date: '2018-10-22T11:41:33.000Z', - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: null, - web_url: 'http://localhost:3000/root', - status_tooltip_html: null, - path: '/root', - }, - author_gravatar_url: null, - commit_url: - 'http://localhost:3000/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319', - commit_path: '/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319', - }, - cancel_path: '/root/ci-web-terminal/pipelines/127/cancel', - }; - return nextTick(); - }); - - it('renders pipeline block', () => { - expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(true); - }); - - describe('with post merge deployments', () => { - beforeEach(() => { - wrapper.vm.mr.postMergeDeployments = [ - { - id: 15, - name: 'review/diplo', - url: '/root/acets-review-apps/environments/15', - stop_url: '/root/acets-review-apps/environments/15/stop', - metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics', - metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics', - external_url: 'http://diplo.', - external_url_formatted: 'diplo.', - deployed_at: '2017-03-22T22:44:42.258Z', - deployed_at_formatted: 'Mar 22, 2017 10:44pm', - changes: [ - { - path: 'index.html', - external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/index.html', - }, - { - path: 'imgs/gallery.html', - external_url: - 'http://root-main-patch-91341.volatile-watch.surge.sh/imgs/gallery.html', - }, - { - path: 'about/', - external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/about/', - }, - ], - status: 'success', - }, - ]; - - return nextTick(); - }); - - it('renders post deployment information', () => { - expect(wrapper.find('.js-post-deployment').exists()).toBe(true); - }); - }); - }); - - describe('without information for target branch pipeline', () => { - beforeEach(() => { - wrapper.vm.mr.state = 'merged'; - - return nextTick(); - }); - - it('does not render pipeline block', () => { - expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false); - }); - }); - - describe('when state is not merged', () => { - beforeEach(() => { - wrapper.vm.mr.state = 'archived'; - - return nextTick(); - }); - - it('does not render pipeline block', () => { - expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false); - }); - - it('does not render post deployment information', () => { - expect(wrapper.find('.js-post-deployment').exists()).toBe(false); - }); - }); - }); - - it('should not suggest pipelines when feature flag is not present', () => { - expect(findSuggestPipeline().exists()).toBe(false); - }); - }); - - describe('security widget', () => { - describe.each` - context | hasPipeline | shouldRender - ${'there is a pipeline'} | ${true} | ${true} - ${'no pipeline'} | ${false} | ${false} - `('given $context', ({ hasPipeline, shouldRender }) => { - beforeEach(() => { - const mrData = { - ...mockData, - ...(hasPipeline ? {} : { pipeline: null }), - }; - - // Override top-level mocked requests, which always use a fresh copy of - // mockData, which always includes the full pipeline object. - mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]); - mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]); - - return createComponent(mrData, { - apolloProvider: createMockApollo([ - [ - securityReportMergeRequestDownloadPathsQuery, - async () => ({ data: securityReportMergeRequestDownloadPathsQueryResponse }), - ], - ]), - }); - }); - - it(shouldRender ? 'renders' : 'does not render', () => { - expect(findSecurityMrWidget().exists()).toBe(shouldRender); - }); - }); - }); - - describe('suggestPipeline', () => { - beforeEach(() => { - mock.onAny().reply(200); - }); - - describe('given feature flag is enabled', () => { - beforeEach(async () => { - await createComponent(); - - wrapper.vm.mr.hasCI = false; - }); - - it('should suggest pipelines when none exist', () => { - expect(findSuggestPipeline().exists()).toBe(true); - }); - - it.each([ - { isDismissedSuggestPipeline: true }, - { mergeRequestAddCiConfigPath: null }, - { hasCI: true }, - ])('with %s, should not suggest pipeline', async (obj) => { - Object.assign(wrapper.vm.mr, obj); - - await nextTick(); - - expect(findSuggestPipeline().exists()).toBe(false); - }); - - it('should allow dismiss of the suggest pipeline message', async () => { - await findSuggestPipelineButton().trigger('click'); - - expect(findSuggestPipeline().exists()).toBe(false); - }); - }); - }); - - describe('merge error', () => { - it.each` - state | show | showText - ${'closed'} | ${false} | ${'hides'} - ${'merged'} | ${true} | ${'shows'} - ${'open'} | ${true} | ${'shows'} - `('it $showText merge error when state is $state', ({ state, show }) => { - createComponent({ ...mockData, state, merge_error: 'Error!' }); - - expect(wrapper.find('[data-testid="merge_error"]').exists()).toBe(show); - }); - }); - - describe('mock extension', () => { - let pollRequest; - - beforeEach(() => { - pollRequest = jest.spyOn(Poll.prototype, 'makeRequest'); - - registerExtension(workingExtension()); - - createComponent(); - }); - - afterEach(() => { - registeredExtensions.extensions = []; - }); - - it('renders collapsed data', async () => { - await waitForPromises(); - - expect(wrapper.text()).toContain('Test extension summary count: 1'); - }); - - it('renders full data', async () => { - await waitForPromises(); - - findExtensionToggleButton().trigger('click'); - - await nextTick(); - - expect( - wrapper.find('[data-testid="widget-extension-top-level"]').find(GlDropdown).exists(), - ).toBe(false); - - await nextTick(); - - const collapsedSection = wrapper.find('[data-testid="widget-extension-collapsed-section"]'); - expect(collapsedSection.exists()).toBe(true); - expect(collapsedSection.text()).toContain('Hello world'); - - // Renders icon in the row - expect(collapsedSection.find(GlIcon).exists()).toBe(true); - expect(collapsedSection.find(GlIcon).props('name')).toBe('status-failed'); - - // Renders badge in the row - expect(collapsedSection.find(GlBadge).exists()).toBe(true); - expect(collapsedSection.find(GlBadge).text()).toBe('Closed'); - - // Renders a link in the row - expect(collapsedSection.find(GlLink).exists()).toBe(true); - expect(collapsedSection.find(GlLink).text()).toBe('GitLab.com'); - - expect(collapsedSection.find(GlButton).exists()).toBe(true); - expect(collapsedSection.find(GlButton).text()).toBe('Full report'); - }); - - it('extension polling is not called if enablePolling flag is not passed', () => { - // called one time due to parent component polling (mount) - expect(pollRequest).toHaveBeenCalledTimes(1); - }); - }); - - describe('expansion', () => { - it('hides collapse button', async () => { - registerExtension(workingExtension(false)); - await createComponent(); - - expect(findExtensionToggleButton().exists()).toBe(false); - }); - - it('shows collapse button', async () => { - registerExtension(workingExtension(true)); - await createComponent(); - - expect(findExtensionToggleButton().exists()).toBe(true); - }); - }); - - describe('mock polling extension', () => { - let pollRequest; - - const findWidgetTestExtension = () => wrapper.find('[data-testid="widget-extension"]'); - - beforeEach(() => { - pollRequest = jest.spyOn(Poll.prototype, 'makeRequest'); - - registeredExtensions.extensions = []; - }); - - afterEach(() => { - registeredExtensions.extensions = []; - }); - - describe('success - multi polling', () => { - it('sets data when polling is complete', async () => { - registerExtension( - multiPollingExtension([ - () => - Promise.resolve({ - headers: { 'poll-interval': 0 }, - status: 200, - data: { reports: 'parsed' }, - }), - () => - Promise.resolve({ - status: 200, - data: { reports: 'parsed' }, - }), - ]), - ); - - await createComponent(); - expect(findWidgetTestExtension().html()).toContain( - 'Multi polling test extension reports: parsed, count: 2', - ); - }); - - it('shows loading state until polling is complete', async () => { - registerExtension( - multiPollingExtension([ - () => - Promise.resolve({ - headers: { 'poll-interval': 1 }, - status: 204, - }), - () => - Promise.resolve({ - status: 200, - data: { reports: 'parsed' }, - }), - ]), - ); - - await createComponent(); - expect(findWidgetTestExtension().html()).toContain('Test extension loading...'); - }); - }); - - describe('success', () => { - it('does not make additional requests after poll is successful', async () => { - registerExtension(pollingExtension); - - await createComponent(); - - expect(pollRequest).toHaveBeenCalledTimes(6); - }); - }); - - describe('success - full data polling', () => { - it('sets data when polling is complete', async () => { - registerExtension(pollingFullDataExtension); - - await createComponent(); - - api.trackRedisHllUserEvent.mockClear(); - api.trackRedisCounterEvent.mockClear(); - - findExtensionToggleButton().trigger('click'); - - // The default working extension is a "warning" type, which generates a second - more specific - telemetry event for expansions - expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(2); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_expand', - ); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_expand_warning', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(2); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_expand', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_expand_warning', - ); - }); - }); - - describe('error', () => { - it('does not make additional requests after poll has failed', async () => { - registerExtension(pollingErrorExtension); - await createComponent(); - - expect(pollRequest).toHaveBeenCalledTimes(6); - }); - - it('captures sentry error and displays error when poll has failed', async () => { - registerExtension(pollingErrorExtension); - await createComponent(); - - expect(Sentry.captureException).toHaveBeenCalledTimes(5); - expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error')); - expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed'); - }); - }); - }); - - describe('mock extension errors', () => { - afterEach(() => { - registeredExtensions.extensions = []; - }); - - it('handles collapsed data fetch errors', async () => { - registerExtension(collapsedDataErrorExtension); - await createComponent(); - - expect( - wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]').exists(), - ).toBe(false); - expect(Sentry.captureException).toHaveBeenCalledTimes(5); - expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error')); - expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed'); - }); - - it('handles full data fetch errors', async () => { - registerExtension(fullDataErrorExtension); - await createComponent(); - - expect(wrapper.findComponent(StatusIcon).props('iconName')).not.toBe('error'); - wrapper - .find('[data-testid="widget-extension"] [data-testid="toggle-button"]') - .trigger('click'); - - await nextTick(); - await waitForPromises(); - - expect(Sentry.captureException).toHaveBeenCalledTimes(1); - expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error')); - expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed'); - }); - }); - - describe('telemetry', () => { - afterEach(() => { - registeredExtensions.extensions = []; - }); - - it('triggers view events when mounted', () => { - registerExtension(workingExtension()); - createComponent(); - - expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_view', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(1); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_view', - ); - }); - - describe('expand button', () => { - it('triggers expand events when clicked', async () => { - registerExtension(workingExtension()); - createComponent(); - - await waitForPromises(); - - api.trackRedisHllUserEvent.mockClear(); - api.trackRedisCounterEvent.mockClear(); - - findExtensionToggleButton().trigger('click'); - - // The default working extension is a "warning" type, which generates a second - more specific - telemetry event for expansions - expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(2); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_expand', - ); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_expand_warning', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(2); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_expand', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_expand_warning', - ); - }); - - it.each` - widgetName | nonStandardEvent - ${'WidgetCodeQuality'} | ${'i_testing_code_quality_widget_total'} - ${'WidgetTerraform'} | ${'i_testing_terraform_widget_total'} - ${'WidgetIssues'} | ${'i_testing_load_performance_widget_total'} - ${'WidgetTestReport'} | ${'i_testing_summary_widget_total'} - `( - "sends non-standard events for the '$widgetName' widget", - async ({ widgetName, nonStandardEvent }) => { - const definition = { - ...workingExtension(), - name: widgetName, - }; - - registerExtension(definition); - createComponent(); - - await waitForPromises(); - - api.trackRedisHllUserEvent.mockClear(); - - findExtensionToggleButton().trigger('click'); - - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(nonStandardEvent); - }, - ); - }); - - it('triggers the "full report clicked" events when the appropriate button is clicked', () => { - registerExtension(fullReportExtension); - createComponent(); - - api.trackRedisHllUserEvent.mockClear(); - api.trackRedisCounterEvent.mockClear(); - - findExtensionLink('testref').trigger('click'); - - expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1); - expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_click_full_report', - ); - expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(1); - expect(api.trackRedisCounterEvent).toHaveBeenCalledWith( - 'i_code_review_merge_request_widget_test_extension_count_click_full_report', - ); - }); - - describe('when disabled', () => { - afterEach(() => { - registeredExtensions.extensions = []; - }); - - it("doesn't emit any telemetry events", async () => { - registerExtension(noTelemetryExtension); - createComponent(); - - await waitForPromises(); - - findExtensionToggleButton().trigger('click'); - findExtensionLink('testref').trigger('click'); // The "full report" link - - expect(api.trackRedisHllUserEvent).not.toHaveBeenCalled(); - expect(api.trackRedisCounterEvent).not.toHaveBeenCalled(); - }); - }); - }); -}); |