Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/issue_show/components/app_spec.js')
-rw-r--r--spec/frontend/issue_show/components/app_spec.js682
1 files changed, 0 insertions, 682 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();
- });
- });
-});