diff options
Diffstat (limited to 'spec/frontend/issue_show')
19 files changed, 0 insertions, 2442 deletions
diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js deleted file mode 100644 index e32215b4aa6..00000000000 --- a/spec/frontend/issue_show/components/app_spec.js +++ /dev/null @@ -1,682 +0,0 @@ -import { GlIntersectionObserver } from '@gitlab/ui'; -import MockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; -import { mountExtended } from 'helpers/vue_test_utils_helper'; -import '~/behaviors/markdown/render_gfm'; -import IssuableApp from '~/issue_show/components/app.vue'; -import DescriptionComponent from '~/issue_show/components/description.vue'; -import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue'; -import PinnedLinks from '~/issue_show/components/pinned_links.vue'; -import { IssuableStatus, IssuableStatusText, POLLING_DELAY } from '~/issue_show/constants'; -import eventHub from '~/issue_show/event_hub'; -import axios from '~/lib/utils/axios_utils'; -import { visitUrl } from '~/lib/utils/url_utility'; -import { - appProps, - initialRequest, - publishedIncidentUrl, - secondRequest, - zoomMeetingUrl, -} from '../mock_data/mock_data'; - -function formatText(text) { - return text.trim().replace(/\s\s+/g, ' '); -} - -jest.mock('~/lib/utils/url_utility'); -jest.mock('~/issue_show/event_hub'); - -const REALTIME_REQUEST_STACK = [initialRequest, secondRequest]; - -describe('Issuable output', () => { - let mock; - let realtimeRequestCount = 0; - let wrapper; - - const findStickyHeader = () => wrapper.findByTestId('issue-sticky-header'); - const findLockedBadge = () => wrapper.findByTestId('locked'); - const findConfidentialBadge = () => wrapper.findByTestId('confidential'); - const findHiddenBadge = () => wrapper.findByTestId('hidden'); - const findAlert = () => wrapper.find('.alert'); - - const mountComponent = (props = {}, options = {}, data = {}) => { - wrapper = mountExtended(IssuableApp, { - directives: { - GlTooltip: createMockDirective(), - }, - propsData: { ...appProps, ...props }, - provide: { - fullPath: 'gitlab-org/incidents', - iid: '19', - uploadMetricsFeatureAvailable: false, - }, - stubs: { - HighlightBar: true, - IncidentTabs: true, - }, - data() { - return { - ...data, - }; - }, - ...options, - }); - }; - - beforeEach(() => { - setFixtures(` - <div> - <title>Title</title> - <div class="detail-page-description content-block"> - <details open> - <summary>One</summary> - </details> - <details> - <summary>Two</summary> - </details> - </div> - <div class="flash-container"></div> - <span id="task_status"></span> - </div> - `); - - mock = new MockAdapter(axios); - mock - .onGet('/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes') - .reply(() => { - const res = Promise.resolve([200, REALTIME_REQUEST_STACK[realtimeRequestCount]]); - realtimeRequestCount += 1; - return res; - }); - - mountComponent(); - - jest.advanceTimersByTime(2); - }); - - afterEach(() => { - mock.restore(); - realtimeRequestCount = 0; - wrapper.vm.poll.stop(); - wrapper.destroy(); - }); - - it('should render a title/description/edited and update title/description/edited on update', () => { - let editedText; - return axios - .waitForAll() - .then(() => { - editedText = wrapper.find('.edited-text'); - }) - .then(() => { - expect(document.querySelector('title').innerText).toContain('this is a title (#1)'); - expect(wrapper.find('.title').text()).toContain('this is a title'); - expect(wrapper.find('.md').text()).toContain('this is a description!'); - expect(wrapper.find('.js-task-list-field').element.value).toContain( - 'this is a description', - ); - - expect(formatText(editedText.text())).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedText.find('.author-link').attributes('href')).toMatch(/\/some_user$/); - expect(editedText.find('time').text()).toBeTruthy(); - expect(wrapper.vm.state.lock_version).toBe(initialRequest.lock_version); - }) - .then(() => { - wrapper.vm.poll.makeRequest(); - return axios.waitForAll(); - }) - .then(() => { - expect(document.querySelector('title').innerText).toContain('2 (#1)'); - expect(wrapper.find('.title').text()).toContain('2'); - expect(wrapper.find('.md').text()).toContain('42'); - expect(wrapper.find('.js-task-list-field').element.value).toContain('42'); - expect(wrapper.find('.edited-text').text()).toBeTruthy(); - expect(formatText(wrapper.find('.edited-text').text())).toMatch( - /Edited[\s\S]+?by Other User/, - ); - - expect(editedText.find('.author-link').attributes('href')).toMatch(/\/other_user$/); - expect(editedText.find('time').text()).toBeTruthy(); - // As the lock_version value does not differ from the server, - // we should not see an alert - expect(findAlert().exists()).toBe(false); - }); - }); - - it('shows actions if permissions are correct', () => { - wrapper.vm.showForm = true; - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find('.markdown-selector').exists()).toBe(true); - }); - }); - - it('does not show actions if permissions are incorrect', () => { - wrapper.vm.showForm = true; - wrapper.setProps({ canUpdate: false }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find('.markdown-selector').exists()).toBe(false); - }); - }); - - it('does not update formState if form is already open', () => { - wrapper.vm.updateAndShowForm(); - - wrapper.vm.state.titleText = 'testing 123'; - - wrapper.vm.updateAndShowForm(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.store.formState.title).not.toBe('testing 123'); - }); - }); - - describe('Pinned links propagated', () => { - it.each` - prop | value - ${'zoomMeetingUrl'} | ${zoomMeetingUrl} - ${'publishedIncidentUrl'} | ${publishedIncidentUrl} - `('sets the $prop correctly on underlying pinned links', ({ prop, value }) => { - expect(wrapper.vm[prop]).toBe(value); - expect(wrapper.find(`[data-testid="${prop}"]`).attributes('href')).toBe(value); - }); - }); - - describe('updateIssuable', () => { - it('fetches new data after update', () => { - const updateStoreSpy = jest.spyOn(wrapper.vm, 'updateStoreState'); - const getDataSpy = jest.spyOn(wrapper.vm.service, 'getData'); - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({ - data: { web_url: window.location.pathname }, - }); - - return wrapper.vm.updateIssuable().then(() => { - expect(updateStoreSpy).toHaveBeenCalled(); - expect(getDataSpy).toHaveBeenCalled(); - }); - }); - - it('correctly updates issuable data', () => { - const spy = jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({ - data: { web_url: window.location.pathname }, - }); - - return wrapper.vm.updateIssuable().then(() => { - expect(spy).toHaveBeenCalledWith(wrapper.vm.formState); - expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); - }); - }); - - it('does not redirect if issue has not moved', () => { - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({ - data: { - web_url: window.location.pathname, - confidential: wrapper.vm.isConfidential, - }, - }); - - return wrapper.vm.updateIssuable().then(() => { - expect(visitUrl).not.toHaveBeenCalled(); - }); - }); - - it('does not redirect if issue has not moved and user has switched tabs', () => { - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({ - data: { - web_url: '', - confidential: wrapper.vm.isConfidential, - }, - }); - - return wrapper.vm.updateIssuable().then(() => { - expect(visitUrl).not.toHaveBeenCalled(); - }); - }); - - it('redirects if returned web_url has changed', () => { - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({ - data: { - web_url: '/testing-issue-move', - confidential: wrapper.vm.isConfidential, - }, - }); - - wrapper.vm.updateIssuable(); - - return wrapper.vm.updateIssuable().then(() => { - expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move'); - }); - }); - - describe('shows dialog when issue has unsaved changed', () => { - it('confirms on title change', () => { - wrapper.vm.showForm = true; - wrapper.vm.state.titleText = 'title has changed'; - const e = { returnValue: null }; - wrapper.vm.handleBeforeUnloadEvent(e); - - return wrapper.vm.$nextTick().then(() => { - expect(e.returnValue).not.toBeNull(); - }); - }); - - it('confirms on description change', () => { - wrapper.vm.showForm = true; - wrapper.vm.state.descriptionText = 'description has changed'; - const e = { returnValue: null }; - wrapper.vm.handleBeforeUnloadEvent(e); - - return wrapper.vm.$nextTick().then(() => { - expect(e.returnValue).not.toBeNull(); - }); - }); - - it('does nothing when nothing has changed', () => { - const e = { returnValue: null }; - wrapper.vm.handleBeforeUnloadEvent(e); - - return wrapper.vm.$nextTick().then(() => { - expect(e.returnValue).toBeNull(); - }); - }); - }); - - describe('error when updating', () => { - it('closes form on error', () => { - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue(); - - return wrapper.vm.updateIssuable().then(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating issue`, - ); - }); - }); - - it('returns the correct error message for issuableType', () => { - jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue(); - wrapper.setProps({ issuableType: 'merge request' }); - - return wrapper.vm - .$nextTick() - .then(wrapper.vm.updateIssuable) - .then(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating merge request`, - ); - }); - }); - - it('shows error message from backend if exists', () => { - const msg = 'Custom error message from backend'; - jest - .spyOn(wrapper.vm.service, 'updateIssuable') - .mockRejectedValue({ response: { data: { errors: [msg] } } }); - - return wrapper.vm.updateIssuable().then(() => { - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `${wrapper.vm.defaultErrorMessage}. ${msg}`, - ); - }); - }); - }); - }); - - describe('deleteIssuable', () => { - it('changes URL when deleted', () => { - jest.spyOn(wrapper.vm.service, 'deleteIssuable').mockResolvedValue({ - data: { - web_url: '/test', - }, - }); - - return wrapper.vm.deleteIssuable().then(() => { - expect(visitUrl).toHaveBeenCalledWith('/test'); - }); - }); - - it('stops polling when deleting', () => { - const spy = jest.spyOn(wrapper.vm.poll, 'stop'); - jest.spyOn(wrapper.vm.service, 'deleteIssuable').mockResolvedValue({ - data: { - web_url: '/test', - }, - }); - - return wrapper.vm.deleteIssuable().then(() => { - expect(spy).toHaveBeenCalledWith(); - }); - }); - - it('closes form on error', () => { - jest.spyOn(wrapper.vm.service, 'deleteIssuable').mockRejectedValue(); - - return wrapper.vm.deleteIssuable().then(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - 'Error deleting issue', - ); - }); - }); - }); - - describe('updateAndShowForm', () => { - it('shows locked warning if form is open & data is different', () => { - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.vm.updateAndShowForm(); - - wrapper.vm.poll.makeRequest(); - - return new Promise((resolve) => { - wrapper.vm.$watch('formState.lockedWarningVisible', (value) => { - if (value) { - resolve(); - } - }); - }); - }) - .then(() => { - expect(wrapper.vm.formState.lockedWarningVisible).toBe(true); - expect(wrapper.vm.formState.lock_version).toBe(1); - expect(findAlert().exists()).toBe(true); - }); - }); - }); - - describe('requestTemplatesAndShowForm', () => { - let formSpy; - - beforeEach(() => { - formSpy = jest.spyOn(wrapper.vm, 'updateAndShowForm'); - }); - - it('shows the form if template names as hash request is successful', () => { - const mockData = { - test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }], - }; - mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData])); - - return wrapper.vm.requestTemplatesAndShowForm().then(() => { - expect(formSpy).toHaveBeenCalledWith(mockData); - }); - }); - - it('shows the form if template names as array request is successful', () => { - const mockData = [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }]; - mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData])); - - return wrapper.vm.requestTemplatesAndShowForm().then(() => { - expect(formSpy).toHaveBeenCalledWith(mockData); - }); - }); - - it('shows the form if template names request failed', () => { - mock - .onGet('/issuable-templates-path') - .reply(() => Promise.reject(new Error('something went wrong'))); - - return wrapper.vm.requestTemplatesAndShowForm().then(() => { - expect(document.querySelector('.flash-container .flash-text').textContent).toContain( - 'Error updating issue', - ); - - expect(formSpy).toHaveBeenCalledWith(); - }); - }); - }); - - describe('show inline edit button', () => { - it('should not render by default', () => { - expect(wrapper.find('.btn-edit').exists()).toBe(true); - }); - - it('should render if showInlineEditButton', () => { - wrapper.setProps({ showInlineEditButton: true }); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.find('.btn-edit').exists()).toBe(true); - }); - }); - }); - - describe('updateStoreState', () => { - it('should make a request and update the state of the store', () => { - const data = { foo: 1 }; - const getDataSpy = jest.spyOn(wrapper.vm.service, 'getData').mockResolvedValue({ data }); - const updateStateSpy = jest - .spyOn(wrapper.vm.store, 'updateState') - .mockImplementation(jest.fn); - - return wrapper.vm.updateStoreState().then(() => { - expect(getDataSpy).toHaveBeenCalled(); - expect(updateStateSpy).toHaveBeenCalledWith(data); - }); - }); - - it('should show error message if store update fails', () => { - jest.spyOn(wrapper.vm.service, 'getData').mockRejectedValue(); - wrapper.setProps({ issuableType: 'merge request' }); - - return wrapper.vm.updateStoreState().then(() => { - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating ${wrapper.vm.issuableType}`, - ); - }); - }); - }); - - describe('issueChanged', () => { - beforeEach(() => { - wrapper.vm.store.formState.title = ''; - wrapper.vm.store.formState.description = ''; - wrapper.setProps({ - initialDescriptionText: '', - initialTitleText: '', - }); - }); - - it('returns true when title is changed', () => { - wrapper.vm.store.formState.title = 'RandomText'; - - expect(wrapper.vm.issueChanged).toBe(true); - }); - - it('returns false when title is empty null', () => { - wrapper.vm.store.formState.title = null; - - expect(wrapper.vm.issueChanged).toBe(false); - }); - - it('returns true when description is changed', () => { - wrapper.vm.store.formState.description = 'RandomText'; - - expect(wrapper.vm.issueChanged).toBe(true); - }); - - it('returns false when description is empty null', () => { - wrapper.vm.store.formState.description = null; - - expect(wrapper.vm.issueChanged).toBe(false); - }); - - it('returns false when `initialDescriptionText` is null and `formState.description` is empty string', () => { - wrapper.vm.store.formState.description = ''; - wrapper.setProps({ initialDescriptionText: null }); - - expect(wrapper.vm.issueChanged).toBe(false); - }); - }); - - describe('sticky header', () => { - describe('when title is in view', () => { - it('is not shown', () => { - expect(findStickyHeader().exists()).toBe(false); - }); - }); - - describe('when title is not in view', () => { - beforeEach(() => { - wrapper.vm.state.titleText = 'Sticky header title'; - wrapper.find(GlIntersectionObserver).vm.$emit('disappear'); - }); - - it('shows with title', () => { - expect(findStickyHeader().text()).toContain('Sticky header title'); - }); - - it.each` - title | state - ${'shows with Open when status is opened'} | ${IssuableStatus.Open} - ${'shows with Closed when status is closed'} | ${IssuableStatus.Closed} - ${'shows with Open when status is reopened'} | ${IssuableStatus.Reopened} - `('$title', async ({ state }) => { - wrapper.setProps({ issuableStatus: state }); - - await nextTick(); - - expect(findStickyHeader().text()).toContain(IssuableStatusText[state]); - }); - - it.each` - title | isConfidential - ${'does not show confidential badge when issue is not confidential'} | ${false} - ${'shows confidential badge when issue is confidential'} | ${true} - `('$title', async ({ isConfidential }) => { - wrapper.setProps({ isConfidential }); - - await nextTick(); - - expect(findConfidentialBadge().exists()).toBe(isConfidential); - }); - - it.each` - title | isLocked - ${'does not show locked badge when issue is not locked'} | ${false} - ${'shows locked badge when issue is locked'} | ${true} - `('$title', async ({ isLocked }) => { - wrapper.setProps({ isLocked }); - - await nextTick(); - - expect(findLockedBadge().exists()).toBe(isLocked); - }); - - it.each` - title | isHidden - ${'does not show hidden badge when issue is not hidden'} | ${false} - ${'shows hidden badge when issue is hidden'} | ${true} - `('$title', async ({ isHidden }) => { - wrapper.setProps({ isHidden }); - - await nextTick(); - - const hiddenBadge = findHiddenBadge(); - - expect(hiddenBadge.exists()).toBe(isHidden); - - if (isHidden) { - expect(hiddenBadge.attributes('title')).toBe( - 'This issue is hidden because its author has been banned', - ); - expect(getBinding(hiddenBadge.element, 'gl-tooltip')).not.toBeUndefined(); - } - }); - }); - }); - - describe('Composable description component', () => { - const findIncidentTabs = () => wrapper.findComponent(IncidentTabs); - const findDescriptionComponent = () => wrapper.findComponent(DescriptionComponent); - const findPinnedLinks = () => wrapper.findComponent(PinnedLinks); - const borderClass = 'gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-mb-6'; - - describe('when using description component', () => { - it('renders the description component', () => { - expect(findDescriptionComponent().exists()).toBe(true); - }); - - it('does not render incident tabs', () => { - expect(findIncidentTabs().exists()).toBe(false); - }); - - it('adds a border below the header', () => { - expect(findPinnedLinks().attributes('class')).toContain(borderClass); - }); - }); - - describe('when using incident tabs description wrapper', () => { - beforeEach(() => { - mountComponent( - { - descriptionComponent: IncidentTabs, - showTitleBorder: false, - }, - { - mocks: { - $apollo: { - queries: { - alert: { - loading: false, - }, - }, - }, - }, - }, - ); - }); - - it('renders the description component', () => { - expect(findDescriptionComponent().exists()).toBe(true); - }); - - it('renders incident tabs', () => { - expect(findIncidentTabs().exists()).toBe(true); - }); - - it('does not add a border below the header', () => { - expect(findPinnedLinks().attributes('class')).not.toContain(borderClass); - }); - }); - }); - - describe('taskListUpdateStarted', () => { - it('stops polling', () => { - jest.spyOn(wrapper.vm.poll, 'stop'); - - wrapper.vm.taskListUpdateStarted(); - - expect(wrapper.vm.poll.stop).toHaveBeenCalled(); - }); - }); - - describe('taskListUpdateSucceeded', () => { - it('enables polling', () => { - jest.spyOn(wrapper.vm.poll, 'enable'); - jest.spyOn(wrapper.vm.poll, 'makeDelayedRequest'); - - wrapper.vm.taskListUpdateSucceeded(); - - expect(wrapper.vm.poll.enable).toHaveBeenCalled(); - expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY); - }); - }); - - describe('taskListUpdateFailed', () => { - it('enables polling and calls updateStoreState', () => { - jest.spyOn(wrapper.vm.poll, 'enable'); - jest.spyOn(wrapper.vm.poll, 'makeDelayedRequest'); - jest.spyOn(wrapper.vm, 'updateStoreState'); - - wrapper.vm.taskListUpdateFailed(); - - expect(wrapper.vm.poll.enable).toHaveBeenCalled(); - expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY); - expect(wrapper.vm.updateStoreState).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/description_spec.js b/spec/frontend/issue_show/components/description_spec.js deleted file mode 100644 index bdcc82cab81..00000000000 --- a/spec/frontend/issue_show/components/description_spec.js +++ /dev/null @@ -1,187 +0,0 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import '~/behaviors/markdown/render_gfm'; -import { TEST_HOST } from 'helpers/test_constants'; -import mountComponent from 'helpers/vue_mount_component_helper'; -import Description from '~/issue_show/components/description.vue'; -import TaskList from '~/task_list'; -import { descriptionProps as props } from '../mock_data/mock_data'; - -jest.mock('~/task_list'); - -describe('Description component', () => { - let vm; - let DescriptionComponent; - - beforeEach(() => { - DescriptionComponent = Vue.extend(Description); - - if (!document.querySelector('.issuable-meta')) { - const metaData = document.createElement('div'); - metaData.classList.add('issuable-meta'); - metaData.innerHTML = - '<div class="flash-container"></div><span id="task_status"></span><span id="task_status_short"></span>'; - - document.body.appendChild(metaData); - } - - vm = mountComponent(DescriptionComponent, props); - }); - - afterEach(() => { - vm.$destroy(); - }); - - afterAll(() => { - $('.issuable-meta .flash-container').remove(); - }); - - it('doesnt animate first description changes', () => { - vm.descriptionHtml = 'changed'; - - return vm.$nextTick().then(() => { - expect( - vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'), - ).toBeFalsy(); - jest.runAllTimers(); - return vm.$nextTick(); - }); - }); - - it('animates description changes on live update', () => { - vm.descriptionHtml = 'changed'; - return vm - .$nextTick() - .then(() => { - vm.descriptionHtml = 'changed second time'; - return vm.$nextTick(); - }) - .then(() => { - expect( - vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'), - ).toBeTruthy(); - jest.runAllTimers(); - return vm.$nextTick(); - }) - .then(() => { - expect( - vm.$el.querySelector('.md').classList.contains('issue-realtime-trigger-pulse'), - ).toBeTruthy(); - }); - }); - - it('applies syntax highlighting and math when description changed', () => { - const vmSpy = jest.spyOn(vm, 'renderGFM'); - const prototypeSpy = jest.spyOn($.prototype, 'renderGFM'); - vm.descriptionHtml = 'changed'; - - return vm.$nextTick().then(() => { - expect(vm.$refs['gfm-content']).toBeDefined(); - expect(vmSpy).toHaveBeenCalled(); - expect(prototypeSpy).toHaveBeenCalled(); - expect($.prototype.renderGFM).toHaveBeenCalled(); - }); - }); - - it('sets data-update-url', () => { - expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(TEST_HOST); - }); - - describe('TaskList', () => { - beforeEach(() => { - vm.$destroy(); - TaskList.mockClear(); - vm = mountComponent(DescriptionComponent, { ...props, issuableType: 'issuableType' }); - }); - - it('re-inits the TaskList when description changed', () => { - vm.descriptionHtml = 'changed'; - - expect(TaskList).toHaveBeenCalled(); - }); - - it('does not re-init the TaskList when canUpdate is false', () => { - vm.canUpdate = false; - vm.descriptionHtml = 'changed'; - - expect(TaskList).toHaveBeenCalledTimes(1); - }); - - it('calls with issuableType dataType', () => { - vm.descriptionHtml = 'changed'; - - expect(TaskList).toHaveBeenCalledWith({ - dataType: 'issuableType', - fieldName: 'description', - selector: '.detail-page-description', - onUpdate: expect.any(Function), - onSuccess: expect.any(Function), - onError: expect.any(Function), - lockVersion: 0, - }); - }); - }); - - describe('taskStatus', () => { - it('adds full taskStatus', () => { - vm.taskStatus = '1 of 1'; - - return vm.$nextTick().then(() => { - expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe( - '1 of 1', - ); - }); - }); - - it('adds short taskStatus', () => { - vm.taskStatus = '1 of 1'; - - return vm.$nextTick().then(() => { - expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe( - '1/1 task', - ); - }); - }); - - it('clears task status text when no tasks are present', () => { - vm.taskStatus = '0 of 0'; - - return vm.$nextTick().then(() => { - expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(''); - }); - }); - }); - - describe('taskListUpdateStarted', () => { - it('emits event to parent', () => { - const spy = jest.spyOn(vm, '$emit'); - - vm.taskListUpdateStarted(); - - expect(spy).toHaveBeenCalledWith('taskListUpdateStarted'); - }); - }); - - describe('taskListUpdateSuccess', () => { - it('emits event to parent', () => { - const spy = jest.spyOn(vm, '$emit'); - - vm.taskListUpdateSuccess(); - - expect(spy).toHaveBeenCalledWith('taskListUpdateSucceeded'); - }); - }); - - describe('taskListUpdateError', () => { - it('should create flash notification and emit an event to parent', () => { - const msg = - 'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.'; - const spy = jest.spyOn(vm, '$emit'); - - vm.taskListUpdateError(); - - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg); - expect(spy).toHaveBeenCalledWith('taskListUpdateFailed'); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/edit_actions_spec.js b/spec/frontend/issue_show/components/edit_actions_spec.js deleted file mode 100644 index 50c27cb5bda..00000000000 --- a/spec/frontend/issue_show/components/edit_actions_spec.js +++ /dev/null @@ -1,163 +0,0 @@ -import { GlButton, GlModal } from '@gitlab/ui'; -import { createLocalVue } from '@vue/test-utils'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import IssuableEditActions from '~/issue_show/components/edit_actions.vue'; -import eventHub from '~/issue_show/event_hub'; - -import { - getIssueStateQueryResponse, - updateIssueStateQueryResponse, -} from '../mock_data/apollo_mock'; - -const localVue = createLocalVue(); -localVue.use(VueApollo); - -describe('Edit Actions component', () => { - let wrapper; - let fakeApollo; - let mockIssueStateData; - - const mockResolvers = { - Query: { - issueState() { - return { - __typename: 'IssueState', - rawData: mockIssueStateData(), - }; - }, - }, - }; - - const modalId = 'delete-issuable-modal-1'; - - const createComponent = ({ props, data } = {}) => { - fakeApollo = createMockApollo([], mockResolvers); - - wrapper = shallowMountExtended(IssuableEditActions, { - apolloProvider: fakeApollo, - propsData: { - formState: { - title: 'GitLab Issue', - }, - canDestroy: true, - issuableType: 'issue', - ...props, - }, - data() { - return { - issueState: {}, - modalId, - ...data, - }; - }, - }); - }; - - async function deleteIssuable(localWrapper) { - localWrapper.findComponent(GlModal).vm.$emit('primary'); - } - - const findModal = () => wrapper.findComponent(GlModal); - const findEditButtons = () => wrapper.findAllComponents(GlButton); - const findDeleteButton = () => wrapper.findByTestId('issuable-delete-button'); - const findSaveButton = () => wrapper.findByTestId('issuable-save-button'); - const findCancelButton = () => wrapper.findByTestId('issuable-cancel-button'); - - beforeEach(() => { - mockIssueStateData = jest.fn(); - createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders all buttons as enabled', () => { - const buttons = findEditButtons().wrappers; - buttons.forEach((button) => { - expect(button.attributes('disabled')).toBeFalsy(); - }); - }); - - it('does not render the delete button if canDestroy is false', () => { - createComponent({ props: { canDestroy: false } }); - expect(findDeleteButton().exists()).toBe(false); - }); - - it('disables save button when title is blank', () => { - createComponent({ props: { formState: { title: '', issue_type: '' } } }); - - expect(findSaveButton().attributes('disabled')).toBe('true'); - }); - - it('does not render the delete button if showDeleteButton is false', () => { - createComponent({ props: { showDeleteButton: false } }); - - expect(findDeleteButton().exists()).toBe(false); - }); - - describe('updateIssuable', () => { - beforeEach(() => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - }); - - it('sends update.issauble event when clicking save button', () => { - findSaveButton().vm.$emit('click', { preventDefault: jest.fn() }); - - expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); - }); - }); - - describe('closeForm', () => { - beforeEach(() => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - }); - - it('emits close.form when clicking cancel', () => { - findCancelButton().vm.$emit('click'); - - expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); - }); - }); - - describe('renders create modal with the correct information', () => { - it('renders correct modal id', () => { - expect(findModal().attributes('modalid')).toBe(modalId); - }); - }); - - describe('deleteIssuable', () => { - beforeEach(() => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - }); - - it('does not send the `delete.issuable` event when clicking delete button', () => { - findDeleteButton().vm.$emit('click'); - expect(eventHub.$emit).not.toHaveBeenCalled(); - }); - - it('sends the `delete.issuable` event when clicking the delete confirm button', async () => { - expect(eventHub.$emit).toHaveBeenCalledTimes(0); - await deleteIssuable(wrapper); - expect(eventHub.$emit).toHaveBeenCalledWith('delete.issuable', { destroy_confirm: true }); - expect(eventHub.$emit).toHaveBeenCalledTimes(1); - }); - }); - - describe('with Apollo cache mock', () => { - it('renders the right delete button text per apollo cache type', async () => { - mockIssueStateData.mockResolvedValue(getIssueStateQueryResponse); - await waitForPromises(); - expect(findDeleteButton().text()).toBe('Delete issue'); - }); - - it('should not change the delete button text per apollo cache mutation', async () => { - mockIssueStateData.mockResolvedValue(updateIssueStateQueryResponse); - await waitForPromises(); - expect(findDeleteButton().text()).toBe('Delete issue'); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/edited_spec.js b/spec/frontend/issue_show/components/edited_spec.js deleted file mode 100644 index a1683f060c0..00000000000 --- a/spec/frontend/issue_show/components/edited_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import Vue from 'vue'; -import edited from '~/issue_show/components/edited.vue'; - -function formatText(text) { - return text.trim().replace(/\s\s+/g, ' '); -} - -describe('edited', () => { - const EditedComponent = Vue.extend(edited); - - it('should render an edited at+by string', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedAt: '2017-05-15T12:31:04.428Z', - updatedByName: 'Some User', - updatedByPath: '/some_user', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedComponent.$el.querySelector('time')).toBeTruthy(); - }); - - it('if no updatedAt is provided, no time element will be rendered', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedByName: 'Some User', - updatedByPath: '/some_user', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/); - expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedComponent.$el.querySelector('time')).toBeFalsy(); - }); - - it('if no updatedByName and updatedByPath is provided, no user element will be rendered', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedAt: '2017-05-15T12:31:04.428Z', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/); - expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy(); - expect(editedComponent.$el.querySelector('time')).toBeTruthy(); - }); -}); diff --git a/spec/frontend/issue_show/components/fields/description_spec.js b/spec/frontend/issue_show/components/fields/description_spec.js deleted file mode 100644 index a50be30cf4c..00000000000 --- a/spec/frontend/issue_show/components/fields/description_spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DescriptionField from '~/issue_show/components/fields/description.vue'; -import eventHub from '~/issue_show/event_hub'; -import MarkdownField from '~/vue_shared/components/markdown/field.vue'; - -describe('Description field component', () => { - let wrapper; - - const findTextarea = () => wrapper.find({ ref: 'textarea' }); - - const mountComponent = (description = 'test') => - shallowMount(DescriptionField, { - attachTo: document.body, - propsData: { - markdownPreviewPath: '/', - markdownDocsPath: '/', - formState: { - description, - }, - }, - stubs: { - MarkdownField, - }, - }); - - beforeEach(() => { - jest.spyOn(eventHub, '$emit'); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('renders markdown field with description', () => { - wrapper = mountComponent(); - - expect(findTextarea().element.value).toBe('test'); - }); - - it('renders markdown field with a markdown description', () => { - const markdown = '**test**'; - - wrapper = mountComponent(markdown); - - expect(findTextarea().element.value).toBe(markdown); - }); - - it('focuses field when mounted', () => { - wrapper = mountComponent(); - - expect(document.activeElement).toBe(findTextarea().element); - }); - - it('triggers update with meta+enter', () => { - wrapper = mountComponent(); - - findTextarea().trigger('keydown.enter', { metaKey: true }); - - expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); - }); - - it('triggers update with ctrl+enter', () => { - wrapper = mountComponent(); - - findTextarea().trigger('keydown.enter', { ctrlKey: true }); - - expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); - }); -}); diff --git a/spec/frontend/issue_show/components/fields/description_template_spec.js b/spec/frontend/issue_show/components/fields/description_template_spec.js deleted file mode 100644 index dc126c53f5e..00000000000 --- a/spec/frontend/issue_show/components/fields/description_template_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import Vue from 'vue'; -import descriptionTemplate from '~/issue_show/components/fields/description_template.vue'; - -describe('Issue description template component with templates as hash', () => { - let vm; - let formState; - - beforeEach(() => { - const Component = Vue.extend(descriptionTemplate); - formState = { - description: 'test', - }; - - vm = new Component({ - propsData: { - formState, - issuableTemplates: { - test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }], - }, - projectId: 1, - projectPath: '/', - namespacePath: '/', - projectNamespace: '/', - }, - }).$mount(); - }); - - it('renders templates as JSON hash in data attribute', () => { - expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe( - '{"test":[{"name":"test","id":"test","project_path":"/","namespace_path":"/"}]}', - ); - }); - - it('updates formState when changing template', () => { - vm.issuableTemplate.editor.setValue('test new template'); - - expect(formState.description).toBe('test new template'); - }); - - it('returns formState description with editor getValue', () => { - formState.description = 'testing new template'; - - expect(vm.issuableTemplate.editor.getValue()).toBe('testing new template'); - }); -}); - -describe('Issue description template component with templates as array', () => { - let vm; - let formState; - - beforeEach(() => { - const Component = Vue.extend(descriptionTemplate); - formState = { - description: 'test', - }; - - vm = new Component({ - propsData: { - formState, - issuableTemplates: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }], - projectId: 1, - projectPath: '/', - namespacePath: '/', - projectNamespace: '/', - }, - }).$mount(); - }); - - it('renders templates as JSON array in data attribute', () => { - expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe( - '[{"name":"test","id":"test","project_path":"/","namespace_path":"/"}]', - ); - }); -}); diff --git a/spec/frontend/issue_show/components/fields/title_spec.js b/spec/frontend/issue_show/components/fields/title_spec.js deleted file mode 100644 index 783ce9eb76c..00000000000 --- a/spec/frontend/issue_show/components/fields/title_spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import TitleField from '~/issue_show/components/fields/title.vue'; -import eventHub from '~/issue_show/event_hub'; - -describe('Title field component', () => { - let wrapper; - - const findInput = () => wrapper.find({ ref: 'input' }); - - beforeEach(() => { - jest.spyOn(eventHub, '$emit'); - - wrapper = shallowMount(TitleField, { - propsData: { - formState: { - title: 'test', - }, - }, - }); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('renders form control with formState title', () => { - expect(findInput().element.value).toBe('test'); - }); - - it('triggers update with meta+enter', () => { - findInput().trigger('keydown.enter', { metaKey: true }); - - expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); - }); - - it('triggers update with ctrl+enter', () => { - findInput().trigger('keydown.enter', { ctrlKey: true }); - - expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); - }); -}); diff --git a/spec/frontend/issue_show/components/fields/type_spec.js b/spec/frontend/issue_show/components/fields/type_spec.js deleted file mode 100644 index 95ae6f37877..00000000000 --- a/spec/frontend/issue_show/components/fields/type_spec.js +++ /dev/null @@ -1,120 +0,0 @@ -import { GlFormGroup, GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import IssueTypeField, { i18n } from '~/issue_show/components/fields/type.vue'; -import { IssuableTypes } from '~/issue_show/constants'; -import { - getIssueStateQueryResponse, - updateIssueStateQueryResponse, -} from '../../mock_data/apollo_mock'; - -const localVue = createLocalVue(); -localVue.use(VueApollo); - -describe('Issue type field component', () => { - let wrapper; - let fakeApollo; - let mockIssueStateData; - - const mockResolvers = { - Query: { - issueState() { - return { - __typename: 'IssueState', - rawData: mockIssueStateData(), - }; - }, - }, - Mutation: { - updateIssueState: jest.fn().mockResolvedValue(updateIssueStateQueryResponse), - }, - }; - - const findTypeFromGroup = () => wrapper.findComponent(GlFormGroup); - const findTypeFromDropDown = () => wrapper.findComponent(GlDropdown); - const findTypeFromDropDownItems = () => wrapper.findAllComponents(GlDropdownItem); - const findTypeFromDropDownItemAt = (at) => findTypeFromDropDownItems().at(at); - const findTypeFromDropDownItemIconAt = (at) => - findTypeFromDropDownItems().at(at).findComponent(GlIcon); - - const createComponent = ({ data } = {}, provide) => { - fakeApollo = createMockApollo([], mockResolvers); - - wrapper = shallowMount(IssueTypeField, { - localVue, - apolloProvider: fakeApollo, - data() { - return { - issueState: {}, - ...data, - }; - }, - provide: { - canCreateIncident: true, - ...provide, - }, - }); - }; - - beforeEach(() => { - mockIssueStateData = jest.fn(); - createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it.each` - at | text | icon - ${0} | ${IssuableTypes[0].text} | ${IssuableTypes[0].icon} - ${1} | ${IssuableTypes[1].text} | ${IssuableTypes[1].icon} - `(`renders the issue type $text with an icon in the dropdown`, ({ at, text, icon }) => { - expect(findTypeFromDropDownItemIconAt(at).attributes('name')).toBe(icon); - expect(findTypeFromDropDownItemAt(at).text()).toBe(text); - }); - - it('renders a form group with the correct label', () => { - expect(findTypeFromGroup().attributes('label')).toBe(i18n.label); - }); - - it('renders a form select with the `issue_type` value', () => { - expect(findTypeFromDropDown().attributes('value')).toBe(IssuableTypes.issue); - }); - - describe('with Apollo cache mock', () => { - it('renders the selected issueType', async () => { - mockIssueStateData.mockResolvedValue(getIssueStateQueryResponse); - await waitForPromises(); - expect(findTypeFromDropDown().attributes('value')).toBe(IssuableTypes.issue); - }); - - it('updates the `issue_type` in the apollo cache when the value is changed', async () => { - findTypeFromDropDownItems().at(1).vm.$emit('click', IssuableTypes.incident); - await wrapper.vm.$nextTick(); - expect(findTypeFromDropDown().attributes('value')).toBe(IssuableTypes.incident); - }); - - describe('when user is a guest', () => { - it('hides the incident type from the dropdown', async () => { - createComponent({}, { canCreateIncident: false, issueType: 'issue' }); - await waitForPromises(); - - expect(findTypeFromDropDownItemAt(0).isVisible()).toBe(true); - expect(findTypeFromDropDownItemAt(1).isVisible()).toBe(false); - expect(findTypeFromDropDown().attributes('value')).toBe(IssuableTypes.issue); - }); - - it('and incident is selected, includes incident in the dropdown', async () => { - createComponent({}, { canCreateIncident: false, issueType: 'incident' }); - await waitForPromises(); - - expect(findTypeFromDropDownItemAt(0).isVisible()).toBe(true); - expect(findTypeFromDropDownItemAt(1).isVisible()).toBe(true); - expect(findTypeFromDropDown().attributes('value')).toBe(IssuableTypes.incident); - }); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/form_spec.js b/spec/frontend/issue_show/components/form_spec.js deleted file mode 100644 index 28498cb90ec..00000000000 --- a/spec/frontend/issue_show/components/form_spec.js +++ /dev/null @@ -1,155 +0,0 @@ -import { GlAlert } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import Autosave from '~/autosave'; -import DescriptionTemplate from '~/issue_show/components/fields/description_template.vue'; -import IssueTypeField from '~/issue_show/components/fields/type.vue'; -import formComponent from '~/issue_show/components/form.vue'; -import LockedWarning from '~/issue_show/components/locked_warning.vue'; -import eventHub from '~/issue_show/event_hub'; - -jest.mock('~/autosave'); - -describe('Inline edit form component', () => { - let wrapper; - const defaultProps = { - canDestroy: true, - formState: { - title: 'b', - description: 'a', - lockedWarningVisible: false, - }, - issuableType: 'issue', - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectPath: '/', - projectId: 1, - projectNamespace: '/', - }; - - afterEach(() => { - wrapper.destroy(); - }); - - const createComponent = (props) => { - wrapper = shallowMount(formComponent, { - propsData: { - ...defaultProps, - ...props, - }, - }); - }; - - const findDescriptionTemplate = () => wrapper.findComponent(DescriptionTemplate); - const findIssuableTypeField = () => wrapper.findComponent(IssueTypeField); - const findLockedWarning = () => wrapper.findComponent(LockedWarning); - const findAlert = () => wrapper.findComponent(GlAlert); - - it('does not render template selector if no templates exist', () => { - createComponent(); - - expect(findDescriptionTemplate().exists()).toBe(false); - }); - - it('renders template selector when templates as array exists', () => { - createComponent({ - issuableTemplates: [ - { name: 'test', id: 'test', project_path: 'test', namespace_path: 'test' }, - ], - }); - - expect(findDescriptionTemplate().exists()).toBe(true); - }); - - it('renders template selector when templates as hash exists', () => { - createComponent({ - issuableTemplates: { - test: [{ name: 'test', id: 'test', project_path: 'test', namespace_path: 'test' }], - }, - }); - - expect(findDescriptionTemplate().exists()).toBe(true); - }); - - it.each` - issuableType | value - ${'issue'} | ${true} - ${'epic'} | ${false} - `( - 'when `issue_type` is set to "$issuableType" rendering the type select will be "$value"', - ({ issuableType, value }) => { - createComponent({ - issuableType, - }); - - expect(findIssuableTypeField().exists()).toBe(value); - }, - ); - - it('hides locked warning by default', () => { - createComponent(); - - expect(findLockedWarning().exists()).toBe(false); - }); - - it('shows locked warning if formState is different', () => { - createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } }); - - expect(findLockedWarning().exists()).toBe(true); - }); - - it('hides locked warning when currently saving', () => { - createComponent({ - formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true }, - }); - - expect(findLockedWarning().exists()).toBe(false); - }); - - describe('autosave', () => { - let spy; - - beforeEach(() => { - spy = jest.spyOn(Autosave.prototype, 'reset'); - }); - - it('initialized Autosave on mount', () => { - createComponent(); - - expect(Autosave).toHaveBeenCalledTimes(2); - }); - - it('calls reset on autosave when eventHub emits appropriate events', () => { - createComponent(); - - eventHub.$emit('close.form'); - - expect(spy).toHaveBeenCalledTimes(2); - - eventHub.$emit('delete.issuable'); - - expect(spy).toHaveBeenCalledTimes(4); - - eventHub.$emit('update.issuable'); - - expect(spy).toHaveBeenCalledTimes(6); - }); - - describe('outdated description', () => { - it('does not show warning if lock version from server is the same as the local lock version', () => { - createComponent(); - expect(findAlert().exists()).toBe(false); - }); - - it('shows warning if lock version from server differs than the local lock version', async () => { - Autosave.prototype.getSavedLockVersion.mockResolvedValue('lock version from local storage'); - - createComponent({ - formState: { ...defaultProps.formState, lock_version: 'lock version from server' }, - }); - - await wrapper.vm.$nextTick(); - expect(findAlert().exists()).toBe(true); - }); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/header_actions_spec.js b/spec/frontend/issue_show/components/header_actions_spec.js deleted file mode 100644 index 4df62ec8717..00000000000 --- a/spec/frontend/issue_show/components/header_actions_spec.js +++ /dev/null @@ -1,348 +0,0 @@ -import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui'; -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import Vuex from 'vuex'; -import createFlash, { FLASH_TYPES } from '~/flash'; -import { IssuableType } from '~/issuable_show/constants'; -import HeaderActions from '~/issue_show/components/header_actions.vue'; -import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants'; -import promoteToEpicMutation from '~/issue_show/queries/promote_to_epic.mutation.graphql'; -import * as urlUtility from '~/lib/utils/url_utility'; -import eventHub from '~/notes/event_hub'; -import createStore from '~/notes/stores'; - -jest.mock('~/flash'); - -describe('HeaderActions component', () => { - let dispatchEventSpy; - let mutateMock; - let wrapper; - let visitUrlSpy; - - const localVue = createLocalVue(); - localVue.use(Vuex); - const store = createStore(); - - const defaultProps = { - canCreateIssue: true, - canPromoteToEpic: true, - canReopenIssue: true, - canReportSpam: true, - canUpdateIssue: true, - iid: '32', - isIssueAuthor: true, - issueType: IssuableType.Issue, - newIssuePath: 'gitlab-org/gitlab-test/-/issues/new', - projectPath: 'gitlab-org/gitlab-test', - reportAbusePath: - '-/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%2Fgitlab-org%2Fgitlab-test%2F-%2Fissues%2F32&user_id=1', - submitAsSpamPath: 'gitlab-org/gitlab-test/-/issues/32/submit_as_spam', - }; - - const updateIssueMutationResponse = { data: { updateIssue: { errors: [] } } }; - - const promoteToEpicMutationResponse = { - data: { - promoteToEpic: { - errors: [], - epic: { - webPath: '/groups/gitlab-org/-/epics/1', - }, - }, - }, - }; - - const promoteToEpicMutationErrorResponse = { - data: { - promoteToEpic: { - errors: ['The issue has already been promoted to an epic.'], - epic: {}, - }, - }, - }; - - const findToggleIssueStateButton = () => wrapper.find(GlButton); - - const findDropdownAt = (index) => wrapper.findAll(GlDropdown).at(index); - - const findMobileDropdownItems = () => findDropdownAt(0).findAll(GlDropdownItem); - - const findDesktopDropdownItems = () => findDropdownAt(1).findAll(GlDropdownItem); - - const findModal = () => wrapper.find(GlModal); - - const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index); - - const mountComponent = ({ - props = {}, - issueState = IssuableStatus.Open, - blockedByIssues = [], - mutateResponse = {}, - } = {}) => { - mutateMock = jest.fn().mockResolvedValue(mutateResponse); - - store.dispatch('setNoteableData', { - blocked_by_issues: blockedByIssues, - state: issueState, - }); - - return shallowMount(HeaderActions, { - localVue, - store, - provide: { - ...defaultProps, - ...props, - }, - mocks: { - $apollo: { - mutate: mutateMock, - }, - }, - }); - }; - - afterEach(() => { - if (dispatchEventSpy) { - dispatchEventSpy.mockRestore(); - } - if (visitUrlSpy) { - visitUrlSpy.mockRestore(); - } - wrapper.destroy(); - }); - - describe.each` - issueType - ${IssuableType.Issue} - ${IssuableType.Incident} - `('when issue type is $issueType', ({ issueType }) => { - describe('close/reopen button', () => { - describe.each` - description | issueState | buttonText | newIssueState - ${`when the ${issueType} is open`} | ${IssuableStatus.Open} | ${`Close ${issueType}`} | ${IssueStateEvent.Close} - ${`when the ${issueType} is closed`} | ${IssuableStatus.Closed} | ${`Reopen ${issueType}`} | ${IssueStateEvent.Reopen} - `('$description', ({ issueState, buttonText, newIssueState }) => { - beforeEach(() => { - dispatchEventSpy = jest.spyOn(document, 'dispatchEvent'); - - wrapper = mountComponent({ - props: { issueType }, - issueState, - mutateResponse: updateIssueMutationResponse, - }); - }); - - it(`has text "${buttonText}"`, () => { - expect(findToggleIssueStateButton().text()).toBe(buttonText); - }); - - it('calls apollo mutation', () => { - findToggleIssueStateButton().vm.$emit('click'); - - expect(mutateMock).toHaveBeenCalledWith( - expect.objectContaining({ - variables: { - input: { - iid: defaultProps.iid, - projectPath: defaultProps.projectPath, - stateEvent: newIssueState, - }, - }, - }), - ); - }); - - it('dispatches a custom event to update the issue page', async () => { - findToggleIssueStateButton().vm.$emit('click'); - - await wrapper.vm.$nextTick(); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe.each` - description | isCloseIssueItemVisible | findDropdownItems - ${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} - ${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} - `('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => { - describe.each` - description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic - ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} - ${`when user cannot update ${issueType}`} | ${`Close ${issueType}`} | ${false} | ${false} | ${true} | ${true} | ${true} | ${true} - ${`when user can create ${issueType}`} | ${`New ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} - ${`when user cannot create ${issueType}`} | ${`New ${issueType}`} | ${false} | ${true} | ${false} | ${true} | ${true} | ${true} - ${'when user can promote to epic'} | ${'Promote to epic'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} - ${'when user cannot promote to epic'} | ${'Promote to epic'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${false} - ${'when user can report abuse'} | ${'Report abuse'} | ${true} | ${true} | ${true} | ${false} | ${true} | ${true} - ${'when user cannot report abuse'} | ${'Report abuse'} | ${false} | ${true} | ${true} | ${true} | ${true} | ${true} - ${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} - ${'when user cannot submit as spam'} | ${'Submit as spam'} | ${false} | ${true} | ${true} | ${true} | ${false} | ${true} - `( - '$description', - ({ - itemText, - isItemVisible, - canUpdateIssue, - canCreateIssue, - isIssueAuthor, - canReportSpam, - canPromoteToEpic, - }) => { - beforeEach(() => { - wrapper = mountComponent({ - props: { - canUpdateIssue, - canCreateIssue, - isIssueAuthor, - issueType, - canReportSpam, - canPromoteToEpic, - }, - }); - }); - - it(`${isItemVisible ? 'shows' : 'hides'} "${itemText}" item`, () => { - expect( - findDropdownItems() - .filter((item) => item.text() === itemText) - .exists(), - ).toBe(isItemVisible); - }); - }, - ); - }); - }); - - describe('when "Promote to epic" button is clicked', () => { - describe('when response is successful', () => { - beforeEach(() => { - visitUrlSpy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({}); - - wrapper = mountComponent({ - mutateResponse: promoteToEpicMutationResponse, - }); - - wrapper.find('[data-testid="promote-button"]').vm.$emit('click'); - }); - - it('invokes GraphQL mutation when clicked', () => { - expect(mutateMock).toHaveBeenCalledWith( - expect.objectContaining({ - mutation: promoteToEpicMutation, - variables: { - input: { - iid: defaultProps.iid, - projectPath: defaultProps.projectPath, - }, - }, - }), - ); - }); - - it('shows a success message and tells the user they are being redirected', () => { - expect(createFlash).toHaveBeenCalledWith({ - message: 'The issue was successfully promoted to an epic. Redirecting to epic...', - type: FLASH_TYPES.SUCCESS, - }); - }); - - it('redirects to newly created epic path', () => { - expect(visitUrlSpy).toHaveBeenCalledWith( - promoteToEpicMutationResponse.data.promoteToEpic.epic.webPath, - ); - }); - }); - - describe('when response contains errors', () => { - beforeEach(() => { - visitUrlSpy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({}); - - wrapper = mountComponent({ - mutateResponse: promoteToEpicMutationErrorResponse, - }); - - wrapper.find('[data-testid="promote-button"]').vm.$emit('click'); - }); - - it('shows an error message', () => { - expect(createFlash).toHaveBeenCalledWith({ - message: promoteToEpicMutationErrorResponse.data.promoteToEpic.errors.join('; '), - }); - }); - }); - }); - - describe('when `toggle.issuable.state` event is emitted', () => { - it('invokes a method to toggle the issue state', () => { - wrapper = mountComponent({ mutateResponse: updateIssueMutationResponse }); - - eventHub.$emit('toggle.issuable.state'); - - expect(mutateMock).toHaveBeenCalledWith( - expect.objectContaining({ - variables: { - input: { - iid: defaultProps.iid, - projectPath: defaultProps.projectPath, - stateEvent: IssueStateEvent.Close, - }, - }, - }), - ); - }); - }); - - describe('modal', () => { - const blockedByIssues = [ - { iid: 13, web_url: 'gitlab-org/gitlab-test/-/issues/13' }, - { iid: 79, web_url: 'gitlab-org/gitlab-test/-/issues/79' }, - ]; - - beforeEach(() => { - wrapper = mountComponent({ blockedByIssues }); - }); - - it('has title text', () => { - expect(findModal().attributes('title')).toBe( - 'Are you sure you want to close this blocked issue?', - ); - }); - - it('has body text', () => { - expect(findModal().text()).toContain( - 'This issue is currently blocked by the following issues:', - ); - }); - - it('calls apollo mutation when primary button is clicked', () => { - findModal().vm.$emit('primary'); - - expect(mutateMock).toHaveBeenCalledWith( - expect.objectContaining({ - variables: { - input: { - iid: defaultProps.iid.toString(), - projectPath: defaultProps.projectPath, - stateEvent: IssueStateEvent.Close, - }, - }, - }), - ); - }); - - describe.each` - ordinal | index - ${'first'} | ${0} - ${'second'} | ${1} - `('$ordinal blocked-by issue link', ({ index }) => { - it('has link text', () => { - expect(findModalLinkAt(index).text()).toBe(`#${blockedByIssues[index].iid}`); - }); - - it('has url', () => { - expect(findModalLinkAt(index).attributes('href')).toBe(blockedByIssues[index].web_url); - }); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js b/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js deleted file mode 100644 index 6758e6192b8..00000000000 --- a/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js +++ /dev/null @@ -1,94 +0,0 @@ -import { GlLink } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import merge from 'lodash/merge'; -import HighlightBar from '~/issue_show/components/incidents/highlight_bar.vue'; -import { formatDate } from '~/lib/utils/datetime_utility'; - -jest.mock('~/lib/utils/datetime_utility'); - -describe('Highlight Bar', () => { - let wrapper; - - const alert = { - iid: 1, - startedAt: '2020-05-29T10:39:22Z', - detailsUrl: 'http://127.0.0.1:3000/root/unique-alerts/-/alert_management/1/details', - eventCount: 1, - title: 'Alert 1', - }; - - const mountComponent = (options) => { - wrapper = shallowMount( - HighlightBar, - merge( - { - propsData: { alert }, - provide: { fullPath: 'test', iid: 1, slaFeatureAvailable: true }, - }, - options, - ), - ); - }; - - beforeEach(() => { - mountComponent(); - }); - - afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } - }); - - const findLink = () => wrapper.find(GlLink); - - describe('empty state', () => { - beforeEach(() => { - mountComponent({ propsData: { alert: null } }); - }); - - it('renders a empty component', () => { - expect(wrapper.isVisible()).toBe(false); - }); - }); - - describe('alert present', () => { - beforeEach(() => { - mountComponent(); - }); - - it('renders a link to the alert page', () => { - expect(findLink().exists()).toBe(true); - expect(findLink().attributes('href')).toBe(alert.detailsUrl); - expect(findLink().attributes('title')).toBe(alert.title); - expect(findLink().text()).toBe(`#${alert.iid}`); - }); - - it('renders formatted start time of the alert', () => { - const formattedDate = '2020-05-29 UTC'; - formatDate.mockReturnValueOnce(formattedDate); - mountComponent(); - expect(formatDate).toHaveBeenCalledWith(alert.startedAt, 'yyyy-mm-dd Z'); - expect(wrapper.text()).toContain(formattedDate); - }); - - it('renders a number of alert events', () => { - expect(wrapper.text()).toContain(alert.eventCount); - }); - }); - - describe('when child data is present', () => { - beforeEach(() => { - mountComponent({ - data() { - return { hasChildData: true }; - }, - }); - }); - - it('renders the highlight bar component', () => { - expect(wrapper.isVisible()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js deleted file mode 100644 index 6b9f5b17e99..00000000000 --- a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js +++ /dev/null @@ -1,143 +0,0 @@ -import { GlTab } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import merge from 'lodash/merge'; -import waitForPromises from 'helpers/wait_for_promises'; -import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; -import DescriptionComponent from '~/issue_show/components/description.vue'; -import HighlightBar from '~/issue_show/components/incidents/highlight_bar.vue'; -import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue'; -import INVALID_URL from '~/lib/utils/invalid_url'; -import Tracking from '~/tracking'; -import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; -import { descriptionProps } from '../../mock_data/mock_data'; - -const mockAlert = { - __typename: 'AlertManagementAlert', - detailsUrl: INVALID_URL, - iid: '1', -}; - -describe('Incident Tabs component', () => { - let wrapper; - - const mountComponent = (data = {}, options = {}) => { - wrapper = shallowMount( - IncidentTabs, - merge( - { - propsData: { - ...descriptionProps, - }, - stubs: { - DescriptionComponent: true, - MetricsTab: true, - }, - provide: { - fullPath: '', - iid: '', - uploadMetricsFeatureAvailable: true, - }, - data() { - return { alert: mockAlert, ...data }; - }, - mocks: { - $apollo: { - queries: { - alert: { - loading: true, - }, - }, - }, - }, - }, - options, - ), - ); - }; - - const findTabs = () => wrapper.findAll(GlTab); - const findSummaryTab = () => findTabs().at(0); - const findMetricsTab = () => wrapper.find('[data-testid="metrics-tab"]'); - const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]'); - const findAlertDetailsComponent = () => wrapper.find(AlertDetailsTable); - const findDescriptionComponent = () => wrapper.find(DescriptionComponent); - const findHighlightBarComponent = () => wrapper.find(HighlightBar); - - describe('empty state', () => { - beforeEach(() => { - mountComponent({ alert: null }); - }); - - it('does not show the alert details tab', () => { - expect(findAlertDetailsComponent().exists()).toBe(false); - }); - }); - - describe('with an alert present', () => { - beforeEach(() => { - mountComponent(); - }); - - it('renders the summary tab', () => { - expect(findSummaryTab().exists()).toBe(true); - expect(findSummaryTab().attributes('title')).toBe('Summary'); - }); - - it('renders the alert details tab', () => { - expect(findAlertDetailsTab().exists()).toBe(true); - expect(findAlertDetailsTab().attributes('title')).toBe('Alert details'); - }); - - it('renders the alert details table with the correct props', () => { - const alert = { iid: mockAlert.iid }; - - expect(findAlertDetailsComponent().props('alert')).toMatchObject(alert); - expect(findAlertDetailsComponent().props('loading')).toBe(true); - }); - - it('renders the description component with highlight bar', () => { - expect(findDescriptionComponent().exists()).toBe(true); - expect(findHighlightBarComponent().exists()).toBe(true); - }); - - it('renders the highlight bar component with the correct props', () => { - const alert = { detailsUrl: mockAlert.detailsUrl }; - - expect(findHighlightBarComponent().props('alert')).toMatchObject(alert); - }); - - it('passes all props to the description component', () => { - expect(findDescriptionComponent().props()).toMatchObject(descriptionProps); - }); - }); - - describe('upload metrics feature available', () => { - it('shows the metric tab when metrics are available', async () => { - mountComponent({}, { provide: { uploadMetricsFeatureAvailable: true } }); - - await waitForPromises(); - - expect(findMetricsTab().exists()).toBe(true); - }); - - it('hides the tab when metrics are not available', async () => { - mountComponent({}, { provide: { uploadMetricsFeatureAvailable: false } }); - - await waitForPromises(); - - expect(findMetricsTab().exists()).toBe(false); - }); - }); - - describe('Snowplow tracking', () => { - beforeEach(() => { - jest.spyOn(Tracking, 'event'); - mountComponent(); - }); - - it('should track incident details views', () => { - const { category, action } = trackIncidentDetailsViewsOptions; - expect(Tracking.event).toHaveBeenCalledWith(category, action); - }); - }); -}); diff --git a/spec/frontend/issue_show/components/pinned_links_spec.js b/spec/frontend/issue_show/components/pinned_links_spec.js deleted file mode 100644 index 3fe1f9fd6d9..00000000000 --- a/spec/frontend/issue_show/components/pinned_links_spec.js +++ /dev/null @@ -1,48 +0,0 @@ -import { GlButton } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import PinnedLinks from '~/issue_show/components/pinned_links.vue'; -import { STATUS_PAGE_PUBLISHED, JOIN_ZOOM_MEETING } from '~/issue_show/constants'; - -const plainZoomUrl = 'https://zoom.us/j/123456789'; -const plainStatusUrl = 'https://status.com'; - -describe('PinnedLinks', () => { - let wrapper; - - const findButtons = () => wrapper.findAll(GlButton); - - const createComponent = (props) => { - wrapper = shallowMount(PinnedLinks, { - propsData: { - zoomMeetingUrl: '', - publishedIncidentUrl: '', - ...props, - }, - }); - }; - - it('displays Zoom link', () => { - createComponent({ - zoomMeetingUrl: `<a href="${plainZoomUrl}">Zoom</a>`, - }); - - expect(findButtons().at(0).text()).toBe(JOIN_ZOOM_MEETING); - }); - - it('displays Status link', () => { - createComponent({ - publishedIncidentUrl: `<a href="${plainStatusUrl}">Status</a>`, - }); - - expect(findButtons().at(0).text()).toBe(STATUS_PAGE_PUBLISHED); - }); - - it('does not render if there are no links', () => { - createComponent({ - zoomMeetingUrl: '', - publishedIncidentUrl: '', - }); - - expect(findButtons()).toHaveLength(0); - }); -}); diff --git a/spec/frontend/issue_show/components/title_spec.js b/spec/frontend/issue_show/components/title_spec.js deleted file mode 100644 index 78880a7f540..00000000000 --- a/spec/frontend/issue_show/components/title_spec.js +++ /dev/null @@ -1,95 +0,0 @@ -import Vue from 'vue'; -import titleComponent from '~/issue_show/components/title.vue'; -import eventHub from '~/issue_show/event_hub'; -import Store from '~/issue_show/stores'; - -describe('Title component', () => { - let vm; - beforeEach(() => { - setFixtures(`<title />`); - - const Component = Vue.extend(titleComponent); - const store = new Store({ - titleHtml: '', - descriptionHtml: '', - issuableRef: '', - }); - vm = new Component({ - propsData: { - issuableRef: '#1', - titleHtml: 'Testing <img />', - titleText: 'Testing', - showForm: false, - formState: store.formState, - }, - }).$mount(); - }); - - it('renders title HTML', () => { - expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>'); - }); - - it('updates page title when changing titleHtml', () => { - const spy = jest.spyOn(vm, 'setPageTitle'); - vm.titleHtml = 'test'; - - return vm.$nextTick().then(() => { - expect(spy).toHaveBeenCalled(); - }); - }); - - it('animates title changes', () => { - vm.titleHtml = 'test'; - return vm - .$nextTick() - .then(() => { - expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-pre-pulse'); - jest.runAllTimers(); - return vm.$nextTick(); - }) - .then(() => { - expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-trigger-pulse'); - }); - }); - - it('updates page title after changing title', () => { - vm.titleHtml = 'changed'; - vm.titleText = 'changed'; - - return vm.$nextTick().then(() => { - expect(document.querySelector('title').textContent.trim()).toContain('changed'); - }); - }); - - describe('inline edit button', () => { - it('should not show by default', () => { - expect(vm.$el.querySelector('.btn-edit')).toBeNull(); - }); - - it('should not show if canUpdate is false', () => { - vm.showInlineEditButton = true; - vm.canUpdate = false; - - expect(vm.$el.querySelector('.btn-edit')).toBeNull(); - }); - - it('should show if showInlineEditButton and canUpdate', () => { - vm.showInlineEditButton = true; - vm.canUpdate = true; - - expect(vm.$el.querySelector('.btn-edit')).toBeDefined(); - }); - - it('should trigger open.form event when clicked', () => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - vm.showInlineEditButton = true; - vm.canUpdate = true; - - Vue.nextTick(() => { - vm.$el.querySelector('.btn-edit').click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('open.form'); - }); - }); - }); -}); diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js deleted file mode 100644 index 76989413edb..00000000000 --- a/spec/frontend/issue_show/issue_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import waitForPromises from 'helpers/wait_for_promises'; -import { initIssuableApp } from '~/issue_show/issue'; -import * as parseData from '~/issue_show/utils/parse_data'; -import axios from '~/lib/utils/axios_utils'; -import createStore from '~/notes/stores'; -import { appProps } from './mock_data/mock_data'; - -const mock = new MockAdapter(axios); -mock.onGet().reply(200); - -jest.mock('~/lib/utils/poll'); - -const setupHTML = (initialData) => { - document.body.innerHTML = `<div id="js-issuable-app"></div>`; - document.getElementById('js-issuable-app').dataset.initial = JSON.stringify(initialData); -}; - -describe('Issue show index', () => { - describe('initIssueableApp', () => { - it('should initialize app with no potential XSS attack', async () => { - const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {}); - const parseDataSpy = jest.spyOn(parseData, 'parseIssuableData'); - - setupHTML({ - ...appProps, - initialDescriptionHtml: '<svg onload=window.alert(1)>', - }); - - const initialDataEl = document.getElementById('js-issuable-app'); - const issuableData = parseData.parseIssuableData(initialDataEl); - initIssuableApp(issuableData, createStore()); - - await waitForPromises(); - - expect(parseDataSpy).toHaveBeenCalled(); - expect(alertSpy).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/frontend/issue_show/mock_data/apollo_mock.js b/spec/frontend/issue_show/mock_data/apollo_mock.js deleted file mode 100644 index bfd31e74393..00000000000 --- a/spec/frontend/issue_show/mock_data/apollo_mock.js +++ /dev/null @@ -1,9 +0,0 @@ -export const getIssueStateQueryResponse = { - issueType: 'issue', - isDirty: false, -}; - -export const updateIssueStateQueryResponse = { - issueType: 'incident', - isDirty: true, -}; diff --git a/spec/frontend/issue_show/mock_data/mock_data.js b/spec/frontend/issue_show/mock_data/mock_data.js deleted file mode 100644 index a73826954c3..00000000000 --- a/spec/frontend/issue_show/mock_data/mock_data.js +++ /dev/null @@ -1,60 +0,0 @@ -import { TEST_HOST } from 'helpers/test_constants'; - -export const initialRequest = { - title: '<p>this is a title</p>', - title_text: 'this is a title', - description: '<p>this is a description!</p>', - description_text: 'this is a description', - task_status: '2 of 4 completed', - updated_at: '2015-05-15T12:31:04.428Z', - updated_by_name: 'Some User', - updated_by_path: '/some_user', - lock_version: 1, -}; - -export const secondRequest = { - title: '<p>2</p>', - title_text: '2', - description: '<p>42</p>', - description_text: '42', - task_status: '0 of 0 completed', - updated_at: '2016-05-15T12:31:04.428Z', - updated_by_name: 'Other User', - updated_by_path: '/other_user', - lock_version: 2, -}; - -export const descriptionProps = { - canUpdate: true, - descriptionHtml: 'test', - descriptionText: 'test', - taskStatus: '', - updateUrl: TEST_HOST, -}; - -export const publishedIncidentUrl = 'https://status.com/'; - -export const zoomMeetingUrl = 'https://gitlab.zoom.us/j/95919234811'; - -export const appProps = { - canUpdate: true, - canDestroy: true, - endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes', - updateEndpoint: TEST_HOST, - issuableRef: '#1', - issuableStatus: 'opened', - initialTitleHtml: '', - initialTitleText: '', - initialDescriptionHtml: 'test', - initialDescriptionText: 'test', - lockVersion: 1, - issueType: 'issue', - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectNamespace: '/', - projectPath: '/', - projectId: 1, - issuableTemplateNamesPath: '/issuable-templates-path', - zoomMeetingUrl, - publishedIncidentUrl, -}; diff --git a/spec/frontend/issue_show/store_spec.js b/spec/frontend/issue_show/store_spec.js deleted file mode 100644 index b7fd70bf00e..00000000000 --- a/spec/frontend/issue_show/store_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import Store from '~/issue_show/stores'; -import updateDescription from '~/issue_show/utils/update_description'; - -jest.mock('~/issue_show/utils/update_description'); - -describe('Store', () => { - let store; - - beforeEach(() => { - store = new Store({ - descriptionHtml: '<p>This is a description</p>', - }); - }); - - describe('updateState', () => { - beforeEach(() => { - document.body.innerHTML = ` - <div class="detail-page-description content-block"> - <details open> - <summary>One</summary> - </details> - <details> - <summary>Two</summary> - </details> - </div> - `; - }); - - afterEach(() => { - document.getElementsByTagName('html')[0].innerHTML = ''; - }); - - it('calls updateDetailsState', () => { - store.updateState({ description: '' }); - - expect(updateDescription).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/spec/frontend/issue_show/utils/update_description_spec.js b/spec/frontend/issue_show/utils/update_description_spec.js deleted file mode 100644 index b2c6bd3c302..00000000000 --- a/spec/frontend/issue_show/utils/update_description_spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import updateDescription from '~/issue_show/utils/update_description'; - -describe('updateDescription', () => { - it('returns the correct value to be set as descriptionHtml', () => { - const actual = updateDescription( - '<details><summary>One</summary></details><details><summary>Two</summary></details>', - [{ open: true }, { open: false }], // mocking NodeList from the dom. - ); - - expect(actual).toEqual( - '<details open="true"><summary>One</summary></details><details><summary>Two</summary></details>', - ); - }); - - describe('when description details returned from api is different then whats currently on the dom', () => { - it('returns the description from the api', () => { - const dataDescription = '<details><summary>One</summary></details>'; - - const actual = updateDescription(dataDescription, []); - - expect(actual).toEqual(dataDescription); - }); - }); -}); |