summaryrefslogtreecommitdiff
path: root/spec/frontend/issues
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/issues')
-rw-r--r--spec/frontend/issues/create_merge_request_dropdown_spec.js19
-rw-r--r--spec/frontend/issues/list/components/issue_card_time_info_spec.js13
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js27
-rw-r--r--spec/frontend/issues/list/mock_data.js2
-rw-r--r--spec/frontend/issues/related_merge_requests/store/actions_spec.js40
-rw-r--r--spec/frontend/issues/show/components/app_spec.js84
-rw-r--r--spec/frontend/issues/show/components/description_spec.js170
-rw-r--r--spec/frontend/issues/show/components/fields/description_spec.js5
-rw-r--r--spec/frontend/issues/show/components/fields/description_template_spec.js101
-rw-r--r--spec/frontend/issues/show/components/fields/title_spec.js4
-rw-r--r--spec/frontend/issues/show/components/incidents/incident_tabs_spec.js18
-rw-r--r--spec/frontend/issues/show/mock_data/mock_data.js15
12 files changed, 257 insertions, 241 deletions
diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js
index c2cfb16fdf7..20b26f5abba 100644
--- a/spec/frontend/issues/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js
@@ -15,7 +15,7 @@ describe('CreateMergeRequestDropdown', () => {
<div id="dummy-wrapper-element">
<div class="available"></div>
<div class="unavailable">
- <div class="gl-spinner"></div>
+ <div class="js-create-mr-spinner"></div>
<div class="text"></div>
</div>
<div class="js-ref"></div>
@@ -38,21 +38,16 @@ describe('CreateMergeRequestDropdown', () => {
});
describe('getRef', () => {
- it('escapes branch names correctly', (done) => {
+ it('escapes branch names correctly', async () => {
const endpoint = `${dropdown.refsPath}contains%23hash`;
jest.spyOn(axios, 'get');
axiosMock.onGet(endpoint).replyOnce({});
- dropdown
- .getRef('contains#hash')
- .then(() => {
- expect(axios.get).toHaveBeenCalledWith(
- endpoint,
- expect.objectContaining({ cancelToken: expect.anything() }),
- );
- })
- .then(done)
- .catch(done.fail);
+ await dropdown.getRef('contains#hash');
+ expect(axios.get).toHaveBeenCalledWith(
+ endpoint,
+ expect.objectContaining({ cancelToken: expect.anything() }),
+ );
});
});
diff --git a/spec/frontend/issues/list/components/issue_card_time_info_spec.js b/spec/frontend/issues/list/components/issue_card_time_info_spec.js
index e9c48b60da4..c3f13ca6f9a 100644
--- a/spec/frontend/issues/list/components/issue_card_time_info_spec.js
+++ b/spec/frontend/issues/list/components/issue_card_time_info_spec.js
@@ -1,10 +1,11 @@
import { GlIcon, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
+import { IssuableStatus } from '~/issues/constants';
import IssueCardTimeInfo from '~/issues/list/components/issue_card_time_info.vue';
describe('CE IssueCardTimeInfo component', () => {
- useFakeDate(2020, 11, 11);
+ useFakeDate(2020, 11, 11); // 2020 Dec 11
let wrapper;
@@ -24,7 +25,7 @@ describe('CE IssueCardTimeInfo component', () => {
const findDueDate = () => wrapper.find('[data-testid="issuable-due-date"]');
const mountComponent = ({
- closedAt = null,
+ state = IssuableStatus.Open,
dueDate = issue.dueDate,
milestoneDueDate = issue.milestone.dueDate,
milestoneStartDate = issue.milestone.startDate,
@@ -38,7 +39,7 @@ describe('CE IssueCardTimeInfo component', () => {
dueDate: milestoneDueDate,
startDate: milestoneStartDate,
},
- closedAt,
+ state,
dueDate,
},
},
@@ -91,7 +92,7 @@ describe('CE IssueCardTimeInfo component', () => {
describe('when in the past', () => {
describe('when issue is open', () => {
it('renders in red', () => {
- wrapper = mountComponent({ dueDate: new Date('2020-10-10') });
+ wrapper = mountComponent({ dueDate: '2020-10-10' });
expect(findDueDate().classes()).toContain('gl-text-red-500');
});
@@ -100,8 +101,8 @@ describe('CE IssueCardTimeInfo component', () => {
describe('when issue is closed', () => {
it('does not render in red', () => {
wrapper = mountComponent({
- dueDate: new Date('2020-10-10'),
- closedAt: '2020-09-05T13:06:25Z',
+ dueDate: '2020-10-10',
+ state: IssuableStatus.Closed,
});
expect(findDueDate().classes()).not.toContain('gl-text-red-500');
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index 33c7ccac180..5a9bd1ff8e4 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -452,13 +452,26 @@ describe('CE IssuesListApp component', () => {
});
describe('IssuableByEmail component', () => {
- describe.each([true, false])(`when issue creation by email is enabled=%s`, (enabled) => {
- it(`${enabled ? 'renders' : 'does not render'}`, () => {
- wrapper = mountComponent({ provide: { initialEmail: enabled } });
-
- expect(findIssuableByEmail().exists()).toBe(enabled);
- });
- });
+ describe.each`
+ initialEmail | hasAnyIssues | isSignedIn | exists
+ ${false} | ${false} | ${false} | ${false}
+ ${false} | ${true} | ${false} | ${false}
+ ${false} | ${false} | ${true} | ${false}
+ ${false} | ${true} | ${true} | ${false}
+ ${true} | ${false} | ${false} | ${false}
+ ${true} | ${true} | ${false} | ${false}
+ ${true} | ${false} | ${true} | ${true}
+ ${true} | ${true} | ${true} | ${true}
+ `(
+ `when issue creation by email is enabled=$initialEmail`,
+ ({ initialEmail, hasAnyIssues, isSignedIn, exists }) => {
+ it(`${initialEmail ? 'renders' : 'does not render'}`, () => {
+ wrapper = mountComponent({ provide: { initialEmail, hasAnyIssues, isSignedIn } });
+
+ expect(findIssuableByEmail().exists()).toBe(exists);
+ });
+ },
+ );
});
describe('empty states', () => {
diff --git a/spec/frontend/issues/list/mock_data.js b/spec/frontend/issues/list/mock_data.js
index c883b20682e..b1a135ceb18 100644
--- a/spec/frontend/issues/list/mock_data.js
+++ b/spec/frontend/issues/list/mock_data.js
@@ -21,7 +21,6 @@ export const getIssuesQueryResponse = {
__typename: 'Issue',
id: 'gid://gitlab/Issue/123456',
iid: '789',
- closedAt: null,
confidential: false,
createdAt: '2021-05-22T04:08:01Z',
downvotes: 2,
@@ -30,6 +29,7 @@ export const getIssuesQueryResponse = {
humanTimeEstimate: null,
mergeRequestsCount: false,
moved: false,
+ state: 'opened',
title: 'Issue title',
updatedAt: '2021-05-22T04:08:01Z',
upvotes: 3,
diff --git a/spec/frontend/issues/related_merge_requests/store/actions_spec.js b/spec/frontend/issues/related_merge_requests/store/actions_spec.js
index 5f232fee09b..4327fac15d4 100644
--- a/spec/frontend/issues/related_merge_requests/store/actions_spec.js
+++ b/spec/frontend/issues/related_merge_requests/store/actions_spec.js
@@ -23,90 +23,82 @@ describe('RelatedMergeRequest store actions', () => {
});
describe('setInitialState', () => {
- it('commits types.SET_INITIAL_STATE with given props', (done) => {
+ it('commits types.SET_INITIAL_STATE with given props', () => {
const props = { a: 1, b: 2 };
- testAction(
+ return testAction(
actions.setInitialState,
props,
{},
[{ type: types.SET_INITIAL_STATE, payload: props }],
[],
- done,
);
});
});
describe('requestData', () => {
- it('commits types.REQUEST_DATA', (done) => {
- testAction(actions.requestData, null, {}, [{ type: types.REQUEST_DATA }], [], done);
+ it('commits types.REQUEST_DATA', () => {
+ return testAction(actions.requestData, null, {}, [{ type: types.REQUEST_DATA }], []);
});
});
describe('receiveDataSuccess', () => {
- it('commits types.RECEIVE_DATA_SUCCESS with data', (done) => {
+ it('commits types.RECEIVE_DATA_SUCCESS with data', () => {
const data = { a: 1, b: 2 };
- testAction(
+ return testAction(
actions.receiveDataSuccess,
data,
{},
[{ type: types.RECEIVE_DATA_SUCCESS, payload: data }],
[],
- done,
);
});
});
describe('receiveDataError', () => {
- it('commits types.RECEIVE_DATA_ERROR', (done) => {
- testAction(
+ it('commits types.RECEIVE_DATA_ERROR', () => {
+ return testAction(
actions.receiveDataError,
null,
{},
[{ type: types.RECEIVE_DATA_ERROR }],
[],
- done,
);
});
});
describe('fetchMergeRequests', () => {
describe('for a successful request', () => {
- it('should dispatch success action', (done) => {
+ it('should dispatch success action', () => {
const data = { a: 1 };
mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(200, data, { 'x-total': 2 });
- testAction(
+ return testAction(
actions.fetchMergeRequests,
null,
state,
[],
[{ type: 'requestData' }, { type: 'receiveDataSuccess', payload: { data, total: 2 } }],
- done,
);
});
});
describe('for a failing request', () => {
- it('should dispatch error action', (done) => {
+ it('should dispatch error action', async () => {
mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(400);
- testAction(
+ await testAction(
actions.fetchMergeRequests,
null,
state,
[],
[{ type: 'requestData' }, { type: 'receiveDataError' }],
- () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
- expect(createFlash).toHaveBeenCalledWith({
- message: expect.stringMatching('Something went wrong'),
- });
-
- done();
- },
);
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith({
+ message: expect.stringMatching('Something went wrong'),
+ });
});
});
});
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js
index ac2717a5028..5ab64d8e9ca 100644
--- a/spec/frontend/issues/show/components/app_spec.js
+++ b/spec/frontend/issues/show/components/app_spec.js
@@ -2,11 +2,14 @@ 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 { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import '~/behaviors/markdown/render_gfm';
import { IssuableStatus, IssuableStatusText } from '~/issues/constants';
import IssuableApp from '~/issues/show/components/app.vue';
import DescriptionComponent from '~/issues/show/components/description.vue';
+import EditedComponent from '~/issues/show/components/edited.vue';
+import FormComponent from '~/issues/show/components/form.vue';
+import TitleComponent from '~/issues/show/components/title.vue';
import IncidentTabs from '~/issues/show/components/incidents/incident_tabs.vue';
import PinnedLinks from '~/issues/show/components/pinned_links.vue';
import { POLLING_DELAY } from '~/issues/show/constants';
@@ -21,10 +24,6 @@ import {
zoomMeetingUrl,
} from '../mock_data/mock_data';
-function formatText(text) {
- return text.trim().replace(/\s\s+/g, ' ');
-}
-
jest.mock('~/lib/utils/url_utility');
jest.mock('~/issues/show/event_hub');
@@ -39,10 +38,15 @@ describe('Issuable output', () => {
const findLockedBadge = () => wrapper.findByTestId('locked');
const findConfidentialBadge = () => wrapper.findByTestId('confidential');
const findHiddenBadge = () => wrapper.findByTestId('hidden');
- const findAlert = () => wrapper.find('.alert');
+
+ const findTitle = () => wrapper.findComponent(TitleComponent);
+ const findDescription = () => wrapper.findComponent(DescriptionComponent);
+ const findEdited = () => wrapper.findComponent(EditedComponent);
+ const findForm = () => wrapper.findComponent(FormComponent);
+ const findPinnedLinks = () => wrapper.findComponent(PinnedLinks);
const mountComponent = (props = {}, options = {}, data = {}) => {
- wrapper = mountExtended(IssuableApp, {
+ wrapper = shallowMountExtended(IssuableApp, {
directives: {
GlTooltip: createMockDirective(),
},
@@ -104,23 +108,15 @@ describe('Issuable output', () => {
});
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(findTitle().props('titleText')).toContain('this is a title');
+ expect(findDescription().props('descriptionText')).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(findEdited().exists()).toBe(true);
+ expect(findEdited().props('updatedByPath')).toMatch(/\/some_user$/);
+ expect(findEdited().props('updatedAt')).toBeTruthy();
expect(wrapper.vm.state.lock_version).toBe(initialRequest.lock_version);
})
.then(() => {
@@ -128,20 +124,13 @@ describe('Issuable output', () => {
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(findTitle().props('titleText')).toContain('2');
+ expect(findDescription().props('descriptionText')).toContain('42');
- 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);
+ expect(findEdited().exists()).toBe(true);
+ expect(findEdited().props('updatedByName')).toBe('Other User');
+ expect(findEdited().props('updatedByPath')).toMatch(/\/other_user$/);
+ expect(findEdited().props('updatedAt')).toBeTruthy();
});
});
@@ -149,7 +138,7 @@ describe('Issuable output', () => {
wrapper.vm.showForm = true;
await nextTick();
- expect(wrapper.find('.markdown-selector').exists()).toBe(true);
+ expect(findForm().exists()).toBe(true);
});
it('does not show actions if permissions are incorrect', async () => {
@@ -157,7 +146,7 @@ describe('Issuable output', () => {
wrapper.setProps({ canUpdate: false });
await nextTick();
- expect(wrapper.find('.markdown-selector').exists()).toBe(false);
+ expect(findForm().exists()).toBe(false);
});
it('does not update formState if form is already open', async () => {
@@ -177,8 +166,7 @@ describe('Issuable output', () => {
${'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);
+ expect(findPinnedLinks().props(prop)).toBe(value);
});
});
@@ -327,7 +315,6 @@ describe('Issuable output', () => {
expect(wrapper.vm.formState.lockedWarningVisible).toBe(true);
expect(wrapper.vm.formState.lock_version).toBe(1);
- expect(findAlert().exists()).toBe(true);
});
});
@@ -374,15 +361,22 @@ describe('Issuable output', () => {
});
describe('show inline edit button', () => {
- it('should not render by default', () => {
- expect(wrapper.find('.btn-edit').exists()).toBe(true);
+ it('should render by default', () => {
+ expect(findTitle().props('showInlineEditButton')).toBe(true);
});
it('should render if showInlineEditButton', async () => {
wrapper.setProps({ showInlineEditButton: true });
await nextTick();
- expect(wrapper.find('.btn-edit').exists()).toBe(true);
+ expect(findTitle().props('showInlineEditButton')).toBe(true);
+ });
+
+ it('should not render if showInlineEditButton is false', async () => {
+ wrapper.setProps({ showInlineEditButton: false });
+
+ await nextTick();
+ expect(findTitle().props('showInlineEditButton')).toBe(false);
});
});
@@ -533,13 +527,11 @@ describe('Issuable output', () => {
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);
+ expect(findDescription().exists()).toBe(true);
});
it('does not render incident tabs', () => {
@@ -572,8 +564,8 @@ describe('Issuable output', () => {
);
});
- it('renders the description component', () => {
- expect(findDescriptionComponent().exists()).toBe(true);
+ it('does not the description component', () => {
+ expect(findDescription().exists()).toBe(false);
});
it('renders incident tabs', () => {
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index 08f8996de6f..0b3daadae1d 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -1,26 +1,35 @@
import $ from 'jquery';
import { nextTick } from 'vue';
import '~/behaviors/markdown/render_gfm';
-import { GlPopover, GlModal } from '@gitlab/ui';
+import { GlTooltip, GlModal } from '@gitlab/ui';
+import setWindowLocation from 'helpers/set_window_location_helper';
import { stubComponent } from 'helpers/stub_component';
import { TEST_HOST } from 'helpers/test_constants';
import { mockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createFlash from '~/flash';
import Description from '~/issues/show/components/description.vue';
+import { updateHistory } from '~/lib/utils/url_utility';
import TaskList from '~/task_list';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import {
descriptionProps as initialProps,
descriptionHtmlWithCheckboxes,
+ descriptionHtmlWithTask,
} from '../mock_data/mock_data';
jest.mock('~/flash');
+jest.mock('~/lib/utils/url_utility', () => ({
+ ...jest.requireActual('~/lib/utils/url_utility'),
+ updateHistory: jest.fn(),
+}));
jest.mock('~/task_list');
const showModal = jest.fn();
const hideModal = jest.fn();
+const $toast = {
+ show: jest.fn(),
+};
describe('Description component', () => {
let wrapper;
@@ -28,10 +37,9 @@ describe('Description component', () => {
const findGfmContent = () => wrapper.find('[data-testid="gfm-content"]');
const findTextarea = () => wrapper.find('[data-testid="textarea"]');
const findTaskActionButtons = () => wrapper.findAll('.js-add-task');
- const findConvertToTaskButton = () => wrapper.find('[data-testid="convert-to-task"]');
- const findTaskSvg = () => wrapper.find('[data-testid="issue-open-m-icon"]');
+ const findConvertToTaskButton = () => wrapper.find('.js-add-task');
- const findPopovers = () => wrapper.findAllComponents(GlPopover);
+ const findTooltips = () => wrapper.findAllComponents(GlTooltip);
const findModal = () => wrapper.findComponent(GlModal);
const findCreateWorkItem = () => wrapper.findComponent(CreateWorkItem);
const findWorkItemDetailModal = () => wrapper.findComponent(WorkItemDetailModal);
@@ -39,10 +47,14 @@ describe('Description component', () => {
function createComponent({ props = {}, provide = {} } = {}) {
wrapper = shallowMountExtended(Description, {
propsData: {
+ issueId: 1,
...initialProps,
...props,
},
provide,
+ mocks: {
+ $toast,
+ },
stubs: {
GlModal: stubComponent(GlModal, {
methods: {
@@ -50,12 +62,13 @@ describe('Description component', () => {
hide: hideModal,
},
}),
- GlPopover,
},
});
}
beforeEach(() => {
+ setWindowLocation(TEST_HOST);
+
if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div');
metaData.classList.add('issuable-meta');
@@ -253,9 +266,9 @@ describe('Description component', () => {
expect(findTaskActionButtons()).toHaveLength(3);
});
- it('renders a list of popovers corresponding to checkboxes in description HTML', () => {
- expect(findPopovers()).toHaveLength(3);
- expect(findPopovers().at(0).props('target')).toBe(
+ it('renders a list of tooltips corresponding to checkboxes in description HTML', () => {
+ expect(findTooltips()).toHaveLength(3);
+ expect(findTooltips().at(0).props('target')).toBe(
findTaskActionButtons().at(0).attributes('id'),
);
});
@@ -264,92 +277,113 @@ describe('Description component', () => {
expect(findModal().props('visible')).toBe(false);
});
- it('opens a modal when a button on popover is clicked and displays correct title', async () => {
- findConvertToTaskButton().vm.$emit('click');
- expect(showModal).toHaveBeenCalled();
- await nextTick();
+ it('opens a modal when a button is clicked and displays correct title', async () => {
+ await findConvertToTaskButton().trigger('click');
expect(findCreateWorkItem().props('initialTitle').trim()).toBe('todo 1');
});
- it('closes the modal on `closeCreateTaskModal` event', () => {
- findConvertToTaskButton().vm.$emit('click');
+ it('closes the modal on `closeCreateTaskModal` event', async () => {
+ await findConvertToTaskButton().trigger('click');
findCreateWorkItem().vm.$emit('closeModal');
expect(hideModal).toHaveBeenCalled();
});
- it('updates description HTML on `onCreate` event', async () => {
- const newTitle = 'New title';
- findConvertToTaskButton().vm.$emit('click');
- findCreateWorkItem().vm.$emit('onCreate', { title: newTitle });
+ it('emits `updateDescription` on `onCreate` event', () => {
+ const newDescription = `<p>New description</p>`;
+ findCreateWorkItem().vm.$emit('onCreate', newDescription);
expect(hideModal).toHaveBeenCalled();
- await nextTick();
+ expect(wrapper.emitted('updateDescription')).toEqual([[newDescription]]);
+ });
+
+ it('shows toast after delete success', async () => {
+ findWorkItemDetailModal().vm.$emit('workItemDeleted');
- expect(findTaskSvg().exists()).toBe(true);
- expect(wrapper.text()).toContain(newTitle);
+ expect($toast.show).toHaveBeenCalledWith('Work item deleted');
});
});
describe('work items detail', () => {
- const id = '1';
- const title = 'my first task';
- const type = 'task';
+ const findTaskLink = () => wrapper.find('a.gfm-issue');
- const createThenClickOnTask = () => {
- findConvertToTaskButton().vm.$emit('click');
- findCreateWorkItem().vm.$emit('onCreate', { id, title, type });
- return wrapper.findByRole('button', { name: title }).trigger('click');
- };
-
- beforeEach(() => {
- createComponent({
- props: {
- descriptionHtml: descriptionHtmlWithCheckboxes,
- },
- provide: {
- glFeatures: { workItems: true },
- },
+ describe('when opening and closing', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ descriptionHtml: descriptionHtmlWithTask,
+ },
+ provide: {
+ glFeatures: { workItems: true },
+ },
+ });
+ return nextTick();
});
- return nextTick();
- });
- it('opens when task button is clicked', async () => {
- expect(findWorkItemDetailModal().props('visible')).toBe(false);
+ it('opens when task button is clicked', async () => {
+ expect(findWorkItemDetailModal().props('visible')).toBe(false);
- await createThenClickOnTask();
+ await findTaskLink().trigger('click');
- expect(findWorkItemDetailModal().props('visible')).toBe(true);
- });
+ expect(findWorkItemDetailModal().props('visible')).toBe(true);
+ expect(updateHistory).toHaveBeenCalledWith({
+ url: `${TEST_HOST}/?work_item_id=2`,
+ replace: true,
+ });
+ });
- it('closes from an open state', async () => {
- await createThenClickOnTask();
+ it('closes from an open state', async () => {
+ await findTaskLink().trigger('click');
- expect(findWorkItemDetailModal().props('visible')).toBe(true);
+ expect(findWorkItemDetailModal().props('visible')).toBe(true);
- findWorkItemDetailModal().vm.$emit('close');
- await nextTick();
+ findWorkItemDetailModal().vm.$emit('close');
+ await nextTick();
- expect(findWorkItemDetailModal().props('visible')).toBe(false);
- });
+ expect(findWorkItemDetailModal().props('visible')).toBe(false);
+ expect(updateHistory).toHaveBeenLastCalledWith({
+ url: `${TEST_HOST}/`,
+ replace: true,
+ });
+ });
- it('shows error on error', async () => {
- const message = 'I am error';
+ it('tracks when opened', async () => {
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- await createThenClickOnTask();
- findWorkItemDetailModal().vm.$emit('error', message);
+ await findTaskLink().trigger('click');
- expect(createFlash).toHaveBeenCalledWith({ message });
+ expect(trackingSpy).toHaveBeenCalledWith(
+ 'workItems:show',
+ 'viewed_work_item_from_modal',
+ {
+ category: 'workItems:show',
+ label: 'work_item_view',
+ property: 'type_task',
+ },
+ );
+ });
});
- it('tracks when opened', async () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- await createThenClickOnTask();
-
- expect(trackingSpy).toHaveBeenCalledWith('workItems:show', 'viewed_work_item_from_modal', {
- category: 'workItems:show',
- label: 'work_item_view',
- property: 'type_task',
- });
+ describe('when url query `work_item_id` exists', () => {
+ it.each`
+ behavior | workItemId | visible
+ ${'opens'} | ${'123'} | ${true}
+ ${'does not open'} | ${'123e'} | ${false}
+ ${'does not open'} | ${'12e3'} | ${false}
+ ${'does not open'} | ${'1e23'} | ${false}
+ ${'does not open'} | ${'x'} | ${false}
+ ${'does not open'} | ${'undefined'} | ${false}
+ `(
+ '$behavior when url contains `work_item_id=$workItemId`',
+ async ({ workItemId, visible }) => {
+ setWindowLocation(`?work_item_id=${workItemId}`);
+
+ createComponent({
+ props: { descriptionHtml: descriptionHtmlWithTask },
+ provide: { glFeatures: { workItems: true } },
+ });
+
+ expect(findWorkItemDetailModal().props('visible')).toBe(visible);
+ },
+ );
});
});
});
diff --git a/spec/frontend/issues/show/components/fields/description_spec.js b/spec/frontend/issues/show/components/fields/description_spec.js
index dd511c3945c..0dcd70ac19b 100644
--- a/spec/frontend/issues/show/components/fields/description_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_spec.js
@@ -14,9 +14,8 @@ describe('Description field component', () => {
propsData: {
markdownPreviewPath: '/',
markdownDocsPath: '/',
- formState: {
- description,
- },
+ quickActionsDocsPath: '/',
+ value: description,
},
stubs: {
MarkdownField,
diff --git a/spec/frontend/issues/show/components/fields/description_template_spec.js b/spec/frontend/issues/show/components/fields/description_template_spec.js
index abe2805e5b2..79a3bfa9840 100644
--- a/spec/frontend/issues/show/components/fields/description_template_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_template_spec.js
@@ -1,74 +1,65 @@
-import Vue from 'vue';
+import { shallowMount } from '@vue/test-utils';
import descriptionTemplate from '~/issues/show/components/fields/description_template.vue';
describe('Issue description template component with templates as hash', () => {
- let vm;
- let formState;
+ let wrapper;
+ const defaultOptions = {
+ propsData: {
+ value: 'test',
+ issuableTemplates: {
+ test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
+ },
+ projectId: 1,
+ projectPath: '/',
+ namespacePath: '/',
+ projectNamespace: '/',
+ },
+ };
- beforeEach(() => {
- const Component = Vue.extend(descriptionTemplate);
- formState = {
- description: 'test',
- };
+ const findIssuableSelector = () => wrapper.find('.js-issuable-selector');
- vm = new Component({
- propsData: {
- formState,
- issuableTemplates: {
- test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
- },
- projectId: 1,
- projectPath: '/',
- namespacePath: '/',
- projectNamespace: '/',
- },
- }).$mount();
+ const createComponent = (options = defaultOptions) => {
+ wrapper = shallowMount(descriptionTemplate, options);
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
});
it('renders templates as JSON hash in data attribute', () => {
- expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe(
+ createComponent();
+ expect(findIssuableSelector().attributes('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');
+ it('emits input event', () => {
+ createComponent();
+ wrapper.vm.issuableTemplate.editor.setValue('test new template');
- expect(formState.description).toBe('test new template');
+ expect(wrapper.emitted('input')).toEqual([['test new template']]);
});
- it('returns formState description with editor getValue', () => {
- formState.description = 'testing new template';
-
- expect(vm.issuableTemplate.editor.getValue()).toBe('testing new template');
+ it('returns value with editor getValue', () => {
+ createComponent();
+ expect(wrapper.vm.issuableTemplate.editor.getValue()).toBe('test');
});
-});
-
-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":"/"}]',
- );
+ describe('Issue description template component with templates as array', () => {
+ it('renders templates as JSON array in data attribute', () => {
+ createComponent({
+ propsData: {
+ value: 'test',
+ issuableTemplates: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
+ projectId: 1,
+ projectPath: '/',
+ namespacePath: '/',
+ projectNamespace: '/',
+ },
+ });
+ expect(findIssuableSelector().attributes('data-data')).toBe(
+ '[{"name":"test","id":"test","project_path":"/","namespace_path":"/"}]',
+ );
+ });
});
});
diff --git a/spec/frontend/issues/show/components/fields/title_spec.js b/spec/frontend/issues/show/components/fields/title_spec.js
index efd0b6fbd30..de04405d89b 100644
--- a/spec/frontend/issues/show/components/fields/title_spec.js
+++ b/spec/frontend/issues/show/components/fields/title_spec.js
@@ -12,9 +12,7 @@ describe('Title field component', () => {
wrapper = shallowMount(TitleField, {
propsData: {
- formState: {
- title: 'test',
- },
+ value: 'test',
},
});
});
diff --git a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
index 20c6cda33d4..35acca60de7 100644
--- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
@@ -34,8 +34,9 @@ describe('Incident Tabs component', () => {
provide: {
fullPath: '',
iid: '',
+ projectId: '',
uploadMetricsFeatureAvailable: true,
- glFeatures: { incidentTimelineEventTab: true, incidentTimelineEvents: true },
+ glFeatures: { incidentTimeline: true, incidentTimelineEvents: true },
},
data() {
return { alert: mockAlert, ...data };
@@ -57,7 +58,6 @@ describe('Incident Tabs component', () => {
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);
@@ -111,20 +111,6 @@ describe('Incident Tabs component', () => {
});
});
- describe('upload metrics feature available', () => {
- it('shows the metric tab when metrics are available', () => {
- mountComponent({}, { provide: { uploadMetricsFeatureAvailable: true } });
-
- expect(findMetricsTab().exists()).toBe(true);
- });
-
- it('hides the tab when metrics are not available', () => {
- mountComponent({}, { provide: { uploadMetricsFeatureAvailable: false } });
-
- expect(findMetricsTab().exists()).toBe(false);
- });
- });
-
describe('Snowplow tracking', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
diff --git a/spec/frontend/issues/show/mock_data/mock_data.js b/spec/frontend/issues/show/mock_data/mock_data.js
index 89653ff82b2..7b0b8ca686a 100644
--- a/spec/frontend/issues/show/mock_data/mock_data.js
+++ b/spec/frontend/issues/show/mock_data/mock_data.js
@@ -72,3 +72,18 @@ export const descriptionHtmlWithCheckboxes = `
</li>
</ul>
`;
+
+export const descriptionHtmlWithTask = `
+ <ul data-sourcepos="1:1-3:7" class="task-list" dir="auto">
+ <li data-sourcepos="1:1-1:10" class="task-list-item">
+ <input type="checkbox" class="task-list-item-checkbox" disabled>
+ <a href="/gitlab-org/gitlab-test/-/issues/48" data-original="#48+" data-link="false" data-link-reference="false" data-project="1" data-issue="2" data-reference-format="+" data-reference-type="task" data-container="body" data-placement="top" title="1" class="gfm gfm-issue has-tooltip">1 (#48)</a>
+ </li>
+ <li data-sourcepos="2:1-2:7" class="task-list-item">
+ <input type="checkbox" class="task-list-item-checkbox" disabled> 2
+ </li>
+ <li data-sourcepos="3:1-3:7" class="task-list-item">
+ <input type="checkbox" class="task-list-item-checkbox" disabled> 3
+ </li>
+ </ul>
+`;