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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-20 18:19:03 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-20 18:19:03 +0300
commit14bd84b61276ef29b97d23642d698de769bacfd2 (patch)
treef9eba90140c1bd874211dea17750a0d422c04080 /spec/frontend/issues
parent891c388697b2db0d8ee0c8358a9bdbf6dc56d581 (diff)
Add latest changes from gitlab-org/gitlab@15-10-stable-eev15.10.0-rc42
Diffstat (limited to 'spec/frontend/issues')
-rw-r--r--spec/frontend/issues/create_merge_request_dropdown_spec.js8
-rw-r--r--spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js20
-rw-r--r--spec/frontend/issues/list/components/issue_card_time_info_spec.js4
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js80
-rw-r--r--spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js5
-rw-r--r--spec/frontend/issues/list/mock_data.js20
-rw-r--r--spec/frontend/issues/list/utils_spec.js5
-rw-r--r--spec/frontend/issues/new/components/title_suggestions_item_spec.js4
-rw-r--r--spec/frontend/issues/new/components/title_suggestions_spec.js123
-rw-r--r--spec/frontend/issues/new/components/type_popover_spec.js4
-rw-r--r--spec/frontend/issues/new/mock_data.js64
-rw-r--r--spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js1
-rw-r--r--spec/frontend/issues/related_merge_requests/store/actions_spec.js4
-rw-r--r--spec/frontend/issues/show/components/app_spec.js579
-rw-r--r--spec/frontend/issues/show/components/delete_issue_modal_spec.js4
-rw-r--r--spec/frontend/issues/show/components/description_spec.js182
-rw-r--r--spec/frontend/issues/show/components/edit_actions_spec.js4
-rw-r--r--spec/frontend/issues/show/components/edited_spec.js4
-rw-r--r--spec/frontend/issues/show/components/fields/description_spec.js28
-rw-r--r--spec/frontend/issues/show/components/fields/description_template_spec.js4
-rw-r--r--spec/frontend/issues/show/components/fields/title_spec.js5
-rw-r--r--spec/frontend/issues/show/components/fields/type_spec.js8
-rw-r--r--spec/frontend/issues/show/components/form_spec.js4
-rw-r--r--spec/frontend/issues/show/components/header_actions_spec.js47
-rw-r--r--spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js5
-rw-r--r--spec/frontend/issues/show/components/incidents/incident_tabs_spec.js55
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js5
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js8
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js4
-rw-r--r--spec/frontend/issues/show/components/incidents/utils_spec.js4
-rw-r--r--spec/frontend/issues/show/components/locked_warning_spec.js5
-rw-r--r--spec/frontend/issues/show/components/task_list_item_actions_spec.js2
-rw-r--r--spec/frontend/issues/show/components/title_spec.js89
-rw-r--r--spec/frontend/issues/show/mock_data/mock_data.js57
34 files changed, 607 insertions, 838 deletions
diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js
index cc2ee84348a..21ae844e2dd 100644
--- a/spec/frontend/issues/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js
@@ -65,6 +65,14 @@ describe('CreateMergeRequestDropdown', () => {
expect(dropdown.createMrPath).toBe(
`${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`,
);
+
+ expect(dropdown.wrapperEl.dataset.createBranchPath).toBe(
+ `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`,
+ );
+
+ expect(dropdown.wrapperEl.dataset.createMrPath).toBe(
+ `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`,
+ );
});
});
diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
index 77d5a0579a4..ebf4771e97f 100644
--- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
+++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js
@@ -18,6 +18,7 @@ import {
setSortPreferenceMutationResponse,
setSortPreferenceMutationResponseWithErrors,
} from 'jest/issues/list/mock_data';
+import { STATUS_ALL, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants';
import IssuesDashboardApp from '~/issues/dashboard/components/issues_dashboard_app.vue';
import getIssuesCountsQuery from '~/issues/dashboard/queries/get_issues_counts.query.graphql';
import { CREATED_DESC, i18n, UPDATED_DESC, urlSortParams } from '~/issues/list/constants';
@@ -36,7 +37,6 @@ import {
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
-import { IssuableStates } from '~/vue_shared/issuable/list/constants';
import {
emptyIssuesQueryResponse,
issuesCountsQueryResponse,
@@ -124,7 +124,7 @@ describe('IssuesDashboardApp component', () => {
// eslint-disable-next-line jest/no-disabled-tests
it.skip('renders IssuableList component', () => {
expect(findIssuableList().props()).toMatchObject({
- currentTab: IssuableStates.Opened,
+ currentTab: STATUS_OPEN,
hasNextPage: true,
hasPreviousPage: false,
hasScopedLabelsFeature: defaultProvide.hasScopedLabelsFeature,
@@ -148,7 +148,7 @@ describe('IssuesDashboardApp component', () => {
tabs: IssuesDashboardApp.IssuableListTabs,
urlParams: {
sort: urlSortParams[CREATED_DESC],
- state: IssuableStates.Opened,
+ state: STATUS_OPEN,
},
useKeysetPagination: true,
});
@@ -283,7 +283,7 @@ describe('IssuesDashboardApp component', () => {
describe('state', () => {
it('is set from the url params', () => {
- const initialState = IssuableStates.All;
+ const initialState = STATUS_ALL;
setWindowLocation(`?state=${initialState}`);
mountComponent();
@@ -337,11 +337,9 @@ describe('IssuesDashboardApp component', () => {
username: 'root',
avatar_url: 'avatar/url',
};
- const originalGon = window.gon;
beforeEach(() => {
window.gon = {
- ...originalGon,
current_user_id: mockCurrentUser.id,
current_user_fullname: mockCurrentUser.name,
current_username: mockCurrentUser.username,
@@ -350,10 +348,6 @@ describe('IssuesDashboardApp component', () => {
mountComponent();
});
- afterEach(() => {
- window.gon = originalGon;
- });
-
it('renders all tokens alphabetically', () => {
const preloadedUsers = [{ ...mockCurrentUser, id: mockCurrentUser.id }];
@@ -375,16 +369,16 @@ describe('IssuesDashboardApp component', () => {
beforeEach(() => {
mountComponent();
- findIssuableList().vm.$emit('click-tab', IssuableStates.Closed);
+ findIssuableList().vm.$emit('click-tab', STATUS_CLOSED);
});
it('updates ui to the new tab', () => {
- expect(findIssuableList().props('currentTab')).toBe(IssuableStates.Closed);
+ expect(findIssuableList().props('currentTab')).toBe(STATUS_CLOSED);
});
it('updates url to the new tab', () => {
expect(findIssuableList().props('urlParams')).toMatchObject({
- state: IssuableStates.Closed,
+ state: STATUS_CLOSED,
});
});
});
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 ab4d023ee39..e80ffea0591 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
@@ -45,10 +45,6 @@ describe('CE IssueCardTimeInfo component', () => {
},
});
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('milestone', () => {
it('renders', () => {
wrapper = mountComponent();
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 8281ce0ed1a..b28a08e2fce 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -15,19 +15,21 @@ import waitForPromises from 'helpers/wait_for_promises';
import {
getIssuesCountsQueryResponse,
getIssuesQueryResponse,
+ getIssuesQueryEmptyResponse,
filteredTokens,
locationSearch,
setSortPreferenceMutationResponse,
setSortPreferenceMutationResponseWithErrors,
urlParams,
} from 'jest/issues/list/mock_data';
-import { createAlert, VARIANT_INFO } from '~/flash';
+import { createAlert, VARIANT_INFO } from '~/alert';
import { TYPENAME_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { STATUS_ALL, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
-import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
+import { IssuableListTabs } from '~/vue_shared/issuable/list/constants';
import EmptyStateWithAnyIssues from '~/issues/list/components/empty_state_with_any_issues.vue';
import EmptyStateWithoutAnyIssues from '~/issues/list/components/empty_state_without_any_issues.vue';
import IssuesListApp from '~/issues/list/components/issues_list_app.vue';
@@ -70,7 +72,7 @@ import('~/issuable');
import('~/users_select');
jest.mock('@sentry/browser');
-jest.mock('~/flash');
+jest.mock('~/alert');
jest.mock('~/lib/utils/scroll_utils', () => ({ scrollUp: jest.fn() }));
describe('CE IssuesListApp component', () => {
@@ -154,7 +156,24 @@ describe('CE IssuesListApp component', () => {
router = new VueRouter({ mode: 'history' });
return mountFn(IssuesListApp, {
- apolloProvider: createMockApollo(requestHandlers),
+ apolloProvider: createMockApollo(
+ requestHandlers,
+ {},
+ {
+ typePolicies: {
+ Query: {
+ fields: {
+ project: {
+ merge: true,
+ },
+ group: {
+ merge: true,
+ },
+ },
+ },
+ },
+ },
+ ),
router,
provide: {
...defaultProvide,
@@ -174,13 +193,11 @@ describe('CE IssuesListApp component', () => {
afterEach(() => {
axiosMock.reset();
- wrapper.destroy();
});
describe('IssuableList', () => {
beforeEach(() => {
wrapper = mountComponent();
- jest.runOnlyPendingTimers();
return waitForPromises();
});
@@ -197,7 +214,7 @@ describe('CE IssuesListApp component', () => {
initialSortBy: CREATED_DESC,
issuables: getIssuesQueryResponse.data.project.issues.nodes,
tabs: IssuableListTabs,
- currentTab: IssuableStates.Opened,
+ currentTab: STATUS_OPEN,
tabCounts: {
opened: 1,
closed: 1,
@@ -247,7 +264,6 @@ describe('CE IssuesListApp component', () => {
mountFn: mount,
});
- jest.runOnlyPendingTimers();
return waitForPromises();
});
@@ -416,7 +432,7 @@ describe('CE IssuesListApp component', () => {
describe('state', () => {
it('is set from the url params', () => {
- const initialState = IssuableStates.All;
+ const initialState = STATUS_ALL;
setWindowLocation(`?state=${initialState}`);
wrapper = mountComponent();
@@ -477,7 +493,12 @@ describe('CE IssuesListApp component', () => {
describe('empty states', () => {
describe('when there are issues', () => {
beforeEach(() => {
- wrapper = mountComponent({ provide: { hasAnyIssues: true }, mountFn: mount });
+ wrapper = mountComponent({
+ provide: { hasAnyIssues: true },
+ mountFn: mount,
+ issuesQueryResponse: getIssuesQueryEmptyResponse,
+ });
+ return waitForPromises();
});
it('shows EmptyStateWithAnyIssues empty state', () => {
@@ -543,11 +564,8 @@ describe('CE IssuesListApp component', () => {
});
describe('when all tokens are available', () => {
- const originalGon = window.gon;
-
beforeEach(() => {
window.gon = {
- ...originalGon,
current_user_id: mockCurrentUser.id,
current_user_fullname: mockCurrentUser.name,
current_username: mockCurrentUser.username,
@@ -563,10 +581,6 @@ describe('CE IssuesListApp component', () => {
});
});
- afterEach(() => {
- window.gon = originalGon;
- });
-
it('renders all tokens alphabetically', () => {
const preloadedUsers = [
{ ...mockCurrentUser, id: convertToGraphQLId(TYPENAME_USER, mockCurrentUser.id) },
@@ -599,7 +613,6 @@ describe('CE IssuesListApp component', () => {
wrapper = mountComponent({
[mountOption]: jest.fn().mockRejectedValue(new Error('ERROR')),
});
- jest.runOnlyPendingTimers();
return waitForPromises();
});
@@ -620,20 +633,21 @@ describe('CE IssuesListApp component', () => {
describe('events', () => {
describe('when "click-tab" event is emitted by IssuableList', () => {
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = mountComponent();
+ await waitForPromises();
router.push = jest.fn();
- findIssuableList().vm.$emit('click-tab', IssuableStates.Closed);
+ findIssuableList().vm.$emit('click-tab', STATUS_CLOSED);
});
it('updates ui to the new tab', () => {
- expect(findIssuableList().props('currentTab')).toBe(IssuableStates.Closed);
+ expect(findIssuableList().props('currentTab')).toBe(STATUS_CLOSED);
});
it('updates url to the new tab', () => {
expect(router.push).toHaveBeenCalledWith({
- query: expect.objectContaining({ state: IssuableStates.Closed }),
+ query: expect.objectContaining({ state: STATUS_CLOSED }),
});
});
});
@@ -641,19 +655,25 @@ describe('CE IssuesListApp component', () => {
describe.each`
event | params
${'next-page'} | ${{
- page_after: 'endCursor',
+ page_after: 'endcursor',
page_before: undefined,
first_page_size: 20,
last_page_size: undefined,
+ search: undefined,
+ sort: 'created_date',
+ state: 'opened',
}}
${'previous-page'} | ${{
page_after: undefined,
- page_before: 'startCursor',
+ page_before: 'startcursor',
first_page_size: undefined,
last_page_size: 20,
+ search: undefined,
+ sort: 'created_date',
+ state: 'opened',
}}
`('when "$event" event is emitted by IssuableList', ({ event, params }) => {
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = mountComponent({
data: {
pageInfo: {
@@ -662,6 +682,7 @@ describe('CE IssuesListApp component', () => {
},
},
});
+ await waitForPromises();
router.push = jest.fn();
findIssuableList().vm.$emit(event);
@@ -735,7 +756,6 @@ describe('CE IssuesListApp component', () => {
provide: { isProject },
issuesQueryResponse: jest.fn().mockResolvedValue(response(isProject)),
});
- jest.runOnlyPendingTimers();
return waitForPromises();
});
@@ -761,7 +781,6 @@ describe('CE IssuesListApp component', () => {
wrapper = mountComponent({
issuesQueryResponse: jest.fn().mockResolvedValue(response()),
});
- jest.runOnlyPendingTimers();
return waitForPromises();
});
@@ -793,8 +812,6 @@ describe('CE IssuesListApp component', () => {
router.push = jest.fn();
findIssuableList().vm.$emit('sort', sortKey);
- jest.runOnlyPendingTimers();
- await nextTick();
expect(router.push).toHaveBeenCalledWith({
query: expect.objectContaining({ sort: urlSortParams[sortKey] }),
@@ -914,13 +931,13 @@ describe('CE IssuesListApp component', () => {
${'shows users when public visibility is not restricted and is signed in'} | ${false} | ${true} | ${false}
${'hides users when public visibility is restricted and is not signed in'} | ${true} | ${false} | ${true}
${'shows users when public visibility is restricted and is signed in'} | ${true} | ${true} | ${false}
- `('$description', ({ isPublicVisibilityRestricted, isSignedIn, hideUsers }) => {
+ `('$description', async ({ isPublicVisibilityRestricted, isSignedIn, hideUsers }) => {
const mockQuery = jest.fn().mockResolvedValue(defaultQueryResponse);
wrapper = mountComponent({
provide: { isPublicVisibilityRestricted, isSignedIn },
issuesQueryResponse: mockQuery,
});
- jest.runOnlyPendingTimers();
+ await waitForPromises();
expect(mockQuery).toHaveBeenCalledWith(expect.objectContaining({ hideUsers }));
});
@@ -929,7 +946,6 @@ describe('CE IssuesListApp component', () => {
describe('fetching issues', () => {
beforeEach(() => {
wrapper = mountComponent();
- jest.runOnlyPendingTimers();
});
it('fetches issue, incident, test case, and task types', () => {
diff --git a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
index 406b1fbc1af..7bbb5a954ae 100644
--- a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
+++ b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
@@ -38,11 +38,6 @@ describe('JiraIssuesImportStatus', () => {
},
});
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
describe('when Jira import is neither in progress nor finished', () => {
beforeEach(() => {
wrapper = mountComponent();
diff --git a/spec/frontend/issues/list/mock_data.js b/spec/frontend/issues/list/mock_data.js
index 1e8a81116f3..0332f68ddb6 100644
--- a/spec/frontend/issues/list/mock_data.js
+++ b/spec/frontend/issues/list/mock_data.js
@@ -101,6 +101,26 @@ export const getIssuesQueryResponse = {
},
};
+export const getIssuesQueryEmptyResponse = {
+ data: {
+ project: {
+ id: '1',
+ __typename: 'Project',
+ issues: {
+ __persist: true,
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ startCursor: 'startcursor',
+ endCursor: 'endcursor',
+ },
+ nodes: [],
+ },
+ },
+ },
+};
+
export const getIssuesCountsQueryResponse = {
data: {
project: {
diff --git a/spec/frontend/issues/list/utils_spec.js b/spec/frontend/issues/list/utils_spec.js
index a281ed1c989..e4ecdc6c29e 100644
--- a/spec/frontend/issues/list/utils_spec.js
+++ b/spec/frontend/issues/list/utils_spec.js
@@ -10,7 +10,7 @@ import {
urlParams,
urlParamsWithSpecialValues,
} from 'jest/issues/list/mock_data';
-import { PAGE_SIZE, urlSortParams } from '~/issues/list/constants';
+import { urlSortParams } from '~/issues/list/constants';
import {
convertToApiParams,
convertToSearchQuery,
@@ -22,10 +22,11 @@ import {
isSortKey,
} from '~/issues/list/utils';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
+import { DEFAULT_PAGE_SIZE } from '~/vue_shared/issuable/list/constants';
describe('getInitialPageParams', () => {
it('returns page params with a default page size when no arguments are given', () => {
- expect(getInitialPageParams()).toEqual({ firstPageSize: PAGE_SIZE });
+ expect(getInitialPageParams()).toEqual({ firstPageSize: DEFAULT_PAGE_SIZE });
});
it('returns page params with the given page size', () => {
diff --git a/spec/frontend/issues/new/components/title_suggestions_item_spec.js b/spec/frontend/issues/new/components/title_suggestions_item_spec.js
index c54a762440f..4454ef81416 100644
--- a/spec/frontend/issues/new/components/title_suggestions_item_spec.js
+++ b/spec/frontend/issues/new/components/title_suggestions_item_spec.js
@@ -25,10 +25,6 @@ describe('Issue title suggestions item component', () => {
const findTooltip = () => wrapper.findComponent(GlTooltip);
const findUserAvatar = () => wrapper.findComponent(UserAvatarImage);
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders title', () => {
createComponent();
diff --git a/spec/frontend/issues/new/components/title_suggestions_spec.js b/spec/frontend/issues/new/components/title_suggestions_spec.js
index 1cd6576967a..343bdbba301 100644
--- a/spec/frontend/issues/new/components/title_suggestions_spec.js
+++ b/spec/frontend/issues/new/components/title_suggestions_spec.js
@@ -1,106 +1,95 @@
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import TitleSuggestions from '~/issues/new/components/title_suggestions.vue';
import TitleSuggestionsItem from '~/issues/new/components/title_suggestions_item.vue';
+import getIssueSuggestionsQuery from '~/issues/new/queries/issues.query.graphql';
+import { mockIssueSuggestionResponse } from '../mock_data';
+
+Vue.use(VueApollo);
+
+const MOCK_PROJECT_PATH = 'project';
+const MOCK_ISSUES_COUNT = mockIssueSuggestionResponse.data.project.issues.edges.length;
describe('Issue title suggestions component', () => {
let wrapper;
+ let mockApollo;
+
+ function createComponent({
+ search = 'search',
+ queryResponse = jest.fn().mockResolvedValue(mockIssueSuggestionResponse),
+ } = {}) {
+ mockApollo = createMockApollo([[getIssueSuggestionsQuery, queryResponse]]);
- function createComponent(search = 'search') {
wrapper = shallowMount(TitleSuggestions, {
propsData: {
search,
- projectPath: 'project',
+ projectPath: MOCK_PROJECT_PATH,
},
+ apolloProvider: mockApollo,
});
}
- beforeEach(() => {
- createComponent();
- });
+ const waitForDebounce = () => {
+ jest.runOnlyPendingTimers();
+ return waitForPromises();
+ };
afterEach(() => {
- wrapper.destroy();
+ mockApollo = null;
});
it('does not render with empty search', async () => {
- wrapper.setProps({ search: '' });
+ createComponent({ search: '' });
+ await waitForDebounce();
- await nextTick();
expect(wrapper.isVisible()).toBe(false);
});
- describe('with data', () => {
- let data;
-
- beforeEach(() => {
- data = { issues: [{ id: 1 }, { id: 2 }] };
- });
-
- it('renders component', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData(data);
-
- await nextTick();
- expect(wrapper.findAll('li').length).toBe(data.issues.length);
- });
+ it('does not render when loading', () => {
+ createComponent();
+ expect(wrapper.isVisible()).toBe(false);
+ });
- it('does not render with empty search', async () => {
- wrapper.setProps({ search: '' });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData(data);
+ it('does not render with empty issues data', async () => {
+ const emptyIssuesResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/1',
+ issues: {
+ edges: [],
+ },
+ },
+ },
+ };
- await nextTick();
- expect(wrapper.isVisible()).toBe(false);
- });
+ createComponent({ queryResponse: jest.fn().mockResolvedValue(emptyIssuesResponse) });
+ await waitForDebounce();
- it('does not render when loading', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- ...data,
- loading: 1,
- });
+ expect(wrapper.isVisible()).toBe(false);
+ });
- await nextTick();
- expect(wrapper.isVisible()).toBe(false);
+ describe('with data', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForDebounce();
});
- it('does not render with empty issues data', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ issues: [] });
-
- await nextTick();
- expect(wrapper.isVisible()).toBe(false);
+ it('renders component', () => {
+ expect(wrapper.findAll('li').length).toBe(MOCK_ISSUES_COUNT);
});
- it('renders list of issues', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData(data);
-
- await nextTick();
- expect(wrapper.findAllComponents(TitleSuggestionsItem).length).toBe(2);
+ it('renders list of issues', () => {
+ expect(wrapper.findAllComponents(TitleSuggestionsItem).length).toBe(MOCK_ISSUES_COUNT);
});
- it('adds margin class to first item', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData(data);
-
- await nextTick();
+ it('adds margin class to first item', () => {
expect(wrapper.findAll('li').at(0).classes()).toContain('gl-mb-3');
});
- it('does not add margin class to last item', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData(data);
-
- await nextTick();
+ it('does not add margin class to last item', () => {
expect(wrapper.findAll('li').at(1).classes()).not.toContain('gl-mb-3');
});
});
diff --git a/spec/frontend/issues/new/components/type_popover_spec.js b/spec/frontend/issues/new/components/type_popover_spec.js
index fe3d5207516..1ae150797c3 100644
--- a/spec/frontend/issues/new/components/type_popover_spec.js
+++ b/spec/frontend/issues/new/components/type_popover_spec.js
@@ -8,10 +8,6 @@ describe('Issue type info popover', () => {
wrapper = shallowMount(TypePopover);
}
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders', () => {
createComponent();
diff --git a/spec/frontend/issues/new/mock_data.js b/spec/frontend/issues/new/mock_data.js
index 74b569d9833..0d2a388cd86 100644
--- a/spec/frontend/issues/new/mock_data.js
+++ b/spec/frontend/issues/new/mock_data.js
@@ -26,3 +26,67 @@ export default () => ({
webUrl: `${TEST_HOST}/author`,
},
});
+
+export const mockIssueSuggestionResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/278964',
+ issues: {
+ edges: [
+ {
+ node: {
+ id: 'gid://gitlab/Issue/123725957',
+ iid: '696',
+ title: 'Remove unused MR widget extension expand success, failed, warning events',
+ confidential: false,
+ userNotesCount: 16,
+ upvotes: 0,
+ webUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/696',
+ state: 'opened',
+ closedAt: null,
+ createdAt: '2023-02-15T12:29:59Z',
+ updatedAt: '2023-03-01T19:38:22Z',
+ author: {
+ id: 'gid://gitlab/User/325',
+ name: 'User Name',
+ username: 'user-name',
+ avatarUrl: '/uploads/-/system/user/avatar/325/avatar.png',
+ webUrl: 'https://gitlab.com/user-name',
+ __typename: 'UserCore',
+ },
+ __typename: 'Issue',
+ },
+ __typename: 'IssueEdge',
+ },
+ {
+ node: {
+ id: 'gid://gitlab/Issue/123',
+ iid: '391',
+ title: 'Remove unused MR widget extension expand success, failed, warning events',
+ confidential: false,
+ userNotesCount: 16,
+ upvotes: 0,
+ webUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391',
+ state: 'opened',
+ closedAt: null,
+ createdAt: '2023-02-15T12:29:59Z',
+ updatedAt: '2023-03-01T19:38:22Z',
+ author: {
+ id: 'gid://gitlab/User/2080',
+ name: 'User Name',
+ username: 'user-name',
+ avatarUrl: '/uploads/-/system/user/avatar/2080/avatar.png',
+ webUrl: 'https://gitlab.com/user-name',
+ __typename: 'UserCore',
+ },
+ __typename: 'Issue',
+ },
+ __typename: 'IssueEdge',
+ },
+ ],
+ __typename: 'IssueConnection',
+ },
+ __typename: 'Project',
+ },
+ },
+};
diff --git a/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js b/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
index 010c719bd84..c5507c88fd7 100644
--- a/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
+++ b/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
@@ -34,7 +34,6 @@ describe('RelatedMergeRequests', () => {
});
afterEach(() => {
- wrapper.destroy();
mock.restore();
});
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 7339372a8d1..31c96265f8d 100644
--- a/spec/frontend/issues/related_merge_requests/store/actions_spec.js
+++ b/spec/frontend/issues/related_merge_requests/store/actions_spec.js
@@ -1,12 +1,12 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as actions from '~/issues/related_merge_requests/store/actions';
import * as types from '~/issues/related_merge_requests/store/mutation_types';
-jest.mock('~/flash');
+jest.mock('~/alert');
describe('RelatedMergeRequest store actions', () => {
let state;
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js
index 9fa0ce6f93d..1006f54eeaf 100644
--- a/spec/frontend/issues/show/components/app_spec.js
+++ b/spec/frontend/issues/show/components/app_spec.js
@@ -1,11 +1,10 @@
import { GlIcon, GlIntersectionObserver } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { setHTMLFixture } from 'helpers/fixtures';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import {
IssuableStatusText,
STATUS_CLOSED,
@@ -21,29 +20,27 @@ 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';
import eventHub from '~/issues/show/event_hub';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import { HTTP_STATUS_OK, HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import {
appProps,
initialRequest,
publishedIncidentUrl,
+ putRequest,
secondRequest,
zoomMeetingUrl,
} from '../mock_data/mock_data';
-jest.mock('~/flash');
-jest.mock('~/issues/show/event_hub');
+jest.mock('~/alert');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/behaviors/markdown/render_gfm');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
describe('Issuable output', () => {
- let mock;
- let realtimeRequestCount = 0;
+ let axiosMock;
let wrapper;
const findStickyHeader = () => wrapper.findByTestId('issue-sticky-header');
@@ -57,15 +54,14 @@ describe('Issuable output', () => {
const findForm = () => wrapper.findComponent(FormComponent);
const findPinnedLinks = () => wrapper.findComponent(PinnedLinks);
- const mountComponent = (props = {}, options = {}, data = {}) => {
+ const createComponent = ({ props = {}, options = {}, data = {} } = {}) => {
wrapper = shallowMountExtended(IssuableApp, {
directives: {
- GlTooltip: createMockDirective(),
+ GlTooltip: createMockDirective('gl-tooltip'),
},
propsData: { ...appProps, ...props },
provide: {
fullPath: 'gitlab-org/incidents',
- iid: '19',
uploadMetricsFeatureAvailable: false,
},
stubs: {
@@ -79,6 +75,28 @@ describe('Issuable output', () => {
},
...options,
});
+
+ jest.advanceTimersToNextTimer(2);
+ return waitForPromises();
+ };
+
+ const emitHubEvent = (event) => {
+ eventHub.$emit(event);
+ return waitForPromises();
+ };
+
+ const openForm = () => {
+ return emitHubEvent('open.form');
+ };
+
+ const updateIssuable = () => {
+ return emitHubEvent('update.issuable');
+ };
+
+ const advanceToNextPoll = () => {
+ // We get new data through the HTTP request.
+ jest.advanceTimersToNextTimer();
+ return waitForPromises();
};
beforeEach(() => {
@@ -98,79 +116,100 @@ describe('Issuable output', () => {
</div>
`);
- mock = new MockAdapter(axios);
- mock
- .onGet('/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes')
- .reply(() => {
- const res = Promise.resolve([HTTP_STATUS_OK, REALTIME_REQUEST_STACK[realtimeRequestCount]]);
- realtimeRequestCount += 1;
- return res;
- });
+ jest.spyOn(eventHub, '$emit');
- mountComponent();
+ axiosMock = new MockAdapter(axios);
+ const endpoint = '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes';
- jest.advanceTimersByTime(2);
+ axiosMock.onGet(endpoint).replyOnce(HTTP_STATUS_OK, REALTIME_REQUEST_STACK[0], {
+ 'POLL-INTERVAL': '1',
+ });
+ axiosMock.onGet(endpoint).reply(HTTP_STATUS_OK, REALTIME_REQUEST_STACK[1], {
+ 'POLL-INTERVAL': '-1',
+ });
+ axiosMock.onPut().reply(HTTP_STATUS_OK, putRequest);
});
- afterEach(() => {
- mock.restore();
- realtimeRequestCount = 0;
- wrapper.vm.poll.stop();
- wrapper.destroy();
- resetHTMLFixture();
- });
+ describe('update', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
- it('should render a title/description/edited and update title/description/edited on update', () => {
- return axios
- .waitForAll()
- .then(() => {
- expect(findTitle().props('titleText')).toContain('this is a title');
- expect(findDescription().props('descriptionText')).toContain('this is a description');
-
- expect(findEdited().exists()).toBe(true);
- expect(findEdited().props('updatedByPath')).toMatch(/\/some_user$/);
- expect(findEdited().props('updatedAt')).toBe(initialRequest.updated_at);
- expect(wrapper.vm.state.lock_version).toBe(initialRequest.lock_version);
- })
- .then(() => {
- wrapper.vm.poll.makeRequest();
- return axios.waitForAll();
- })
- .then(() => {
- expect(findTitle().props('titleText')).toContain('2');
- expect(findDescription().props('descriptionText')).toContain('42');
-
- expect(findEdited().exists()).toBe(true);
- expect(findEdited().props('updatedByName')).toBe('Other User');
- expect(findEdited().props('updatedByPath')).toMatch(/\/other_user$/);
- expect(findEdited().props('updatedAt')).toBe(secondRequest.updated_at);
- });
- });
+ it('should render a title/description/edited and update title/description/edited on update', async () => {
+ expect(findTitle().props('titleText')).toContain(initialRequest.title_text);
+ expect(findDescription().props('descriptionText')).toContain('this is a description');
- it('shows actions if permissions are correct', async () => {
- wrapper.vm.showForm = true;
+ expect(findEdited().exists()).toBe(true);
+ expect(findEdited().props('updatedByPath')).toMatch(/\/some_user$/);
+ expect(findEdited().props('updatedAt')).toBe(initialRequest.updated_at);
+ expect(findDescription().props().lockVersion).toBe(initialRequest.lock_version);
- await nextTick();
- expect(findForm().exists()).toBe(true);
- });
+ await advanceToNextPoll();
- it('does not show actions if permissions are incorrect', async () => {
- wrapper.vm.showForm = true;
- wrapper.setProps({ canUpdate: false });
+ expect(findTitle().props('titleText')).toContain('2');
+ expect(findDescription().props('descriptionText')).toContain('42');
- await nextTick();
- expect(findForm().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')).toBe(secondRequest.updated_at);
+ });
});
- it('does not update formState if form is already open', async () => {
- wrapper.vm.updateAndShowForm();
+ describe('with permissions', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
- wrapper.vm.state.titleText = 'testing 123';
+ it('shows actions on `open.form` event', async () => {
+ expect(findForm().exists()).toBe(false);
- wrapper.vm.updateAndShowForm();
+ await openForm();
- await nextTick();
- expect(wrapper.vm.store.formState.title).not.toBe('testing 123');
+ expect(findForm().exists()).toBe(true);
+ });
+
+ it('update formState if form is not open', async () => {
+ const titleValue = initialRequest.title_text;
+
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(titleValue);
+
+ await advanceToNextPoll();
+
+ // The title component has the new data, so the state was updated
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(secondRequest.title_text);
+ });
+
+ it('does not update formState if form is already open', async () => {
+ const titleValue = initialRequest.title_text;
+
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(titleValue);
+
+ await openForm();
+
+ // Opening the form, the data has not changed
+ expect(findForm().props().formState.title).toBe(titleValue);
+
+ await advanceToNextPoll();
+
+ // We expect the prop value not to have changed after another API call
+ expect(findForm().props().formState.title).toBe(titleValue);
+ });
+ });
+
+ describe('without permissions', () => {
+ beforeEach(async () => {
+ await createComponent({ props: { canUpdate: false } });
+ });
+
+ it('does not show actions if permissions are incorrect', async () => {
+ await openForm();
+
+ expect(findForm().exists()).toBe(false);
+ });
});
describe('Pinned links propagated', () => {
@@ -178,288 +217,130 @@ describe('Issuable output', () => {
prop | value
${'zoomMeetingUrl'} | ${zoomMeetingUrl}
${'publishedIncidentUrl'} | ${publishedIncidentUrl}
- `('sets the $prop correctly on underlying pinned links', ({ prop, value }) => {
+ `('sets the $prop correctly on underlying pinned links', async ({ prop, value }) => {
+ await createComponent();
+
expect(findPinnedLinks().props(prop)).toBe(value);
});
});
- describe('updateIssuable', () => {
+ describe('updating an issue', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
it('fetches new data after update', async () => {
- 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 },
- });
+ await advanceToNextPoll();
- await wrapper.vm.updateIssuable();
- expect(updateStoreSpy).toHaveBeenCalled();
- expect(getDataSpy).toHaveBeenCalled();
+ await updateIssuable();
+
+ expect(axiosMock.history.put).toHaveLength(1);
+ // The call was made with the new data
+ expect(axiosMock.history.put[0].data.title).toEqual(findTitle().props().title);
});
- it('correctly updates issuable data', async () => {
- const spy = jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: { web_url: window.location.pathname },
- });
+ it('closes the form after fetching data', async () => {
+ await updateIssuable();
- await wrapper.vm.updateIssuable();
- expect(spy).toHaveBeenCalledWith(wrapper.vm.formState);
expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
});
it('does not redirect if issue has not moved', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: window.location.pathname,
- confidential: wrapper.vm.isConfidential,
- },
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ confidential: appProps.isConfidential,
});
- await wrapper.vm.updateIssuable();
+ await updateIssuable();
+
expect(visitUrl).not.toHaveBeenCalled();
});
it('does not redirect if issue has not moved and user has switched tabs', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: '',
- confidential: wrapper.vm.isConfidential,
- },
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ web_url: '',
+ confidential: appProps.isConfidential,
});
- await wrapper.vm.updateIssuable();
+ await updateIssuable();
+
expect(visitUrl).not.toHaveBeenCalled();
});
it('redirects if returned web_url has changed', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: '/testing-issue-move',
- confidential: wrapper.vm.isConfidential,
- },
- });
-
- wrapper.vm.updateIssuable();
-
- await wrapper.vm.updateIssuable();
- expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move');
- });
-
- describe('shows dialog when issue has unsaved changed', () => {
- it('confirms on title change', async () => {
- wrapper.vm.showForm = true;
- wrapper.vm.state.titleText = 'title has changed';
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
-
- await nextTick();
- expect(e.returnValue).not.toBeNull();
- });
-
- it('confirms on description change', async () => {
- wrapper.vm.showForm = true;
- wrapper.vm.state.descriptionText = 'description has changed';
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
+ const webUrl = '/testing-issue-move';
- await nextTick();
- expect(e.returnValue).not.toBeNull();
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ web_url: webUrl,
+ confidential: appProps.isConfidential,
});
- it('does nothing when nothing has changed', async () => {
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
+ await updateIssuable();
- await nextTick();
- expect(e.returnValue).toBeNull();
- });
+ expect(visitUrl).toHaveBeenCalledWith(webUrl);
});
describe('error when updating', () => {
- it('closes form on error', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue();
+ it('closes form', async () => {
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED);
- await wrapper.vm.updateIssuable();
- expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(createAlert).toHaveBeenCalledWith({ message: `Error updating issue` });
- });
+ await updateIssuable();
- it('returns the correct error message for issuableType', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue();
- wrapper.setProps({ issuableType: 'merge request' });
-
- await nextTick();
- await wrapper.vm.updateIssuable();
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(createAlert).toHaveBeenCalledWith({ message: `Error updating merge request` });
- });
-
- it('shows error message from backend if exists', async () => {
- const msg = 'Custom error message from backend';
- jest
- .spyOn(wrapper.vm.service, 'updateIssuable')
- .mockRejectedValue({ response: { data: { errors: [msg] } } });
-
- await wrapper.vm.updateIssuable();
expect(createAlert).toHaveBeenCalledWith({
- message: `${wrapper.vm.defaultErrorMessage}. ${msg}`,
+ message: `Error updating issue. Request failed with status code 401`,
});
});
- });
- });
-
- describe('updateAndShowForm', () => {
- it('shows locked warning if form is open & data is different', async () => {
- await nextTick();
- wrapper.vm.updateAndShowForm();
-
- wrapper.vm.poll.makeRequest();
- await new Promise((resolve) => {
- wrapper.vm.$watch('formState.lockedWarningVisible', (value) => {
- if (value) {
- resolve();
- }
- });
- });
-
- expect(wrapper.vm.formState.lockedWarningVisible).toBe(true);
- expect(wrapper.vm.formState.lock_version).toBe(1);
- });
- });
-
- 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([HTTP_STATUS_OK, mockData]));
-
- return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(formSpy).toHaveBeenCalledWith(mockData);
- });
- });
+ it('returns the correct error message for issuableType', async () => {
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED);
- 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([HTTP_STATUS_OK, mockData]));
+ await updateIssuable();
- 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')));
+ wrapper.setProps({ issuableType: 'merge request' });
- return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(createAlert).toHaveBeenCalledWith({ message: 'Error updating issue' });
+ await updateIssuable();
- expect(formSpy).toHaveBeenCalledWith();
+ expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
+ expect(createAlert).toHaveBeenCalledWith({
+ message: `Error updating merge request. Request failed with status code 401`,
+ });
});
- });
- });
-
- describe('show inline edit button', () => {
- it('should render by default', () => {
- expect(findTitle().props('showInlineEditButton')).toBe(true);
- });
-
- it('should render if showInlineEditButton', async () => {
- wrapper.setProps({ showInlineEditButton: true });
-
- await nextTick();
- 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);
- });
- });
-
- 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('shows error message from backend if exists', async () => {
+ const msg = 'Custom error message from backend';
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED, { errors: [msg] });
- it('should show error message if store update fails', () => {
- jest.spyOn(wrapper.vm.service, 'getData').mockRejectedValue();
- wrapper.setProps({ issuableType: 'merge request' });
+ await updateIssuable();
- return wrapper.vm.updateStoreState().then(() => {
expect(createAlert).toHaveBeenCalledWith({
- message: `Error updating ${wrapper.vm.issuableType}`,
+ message: `Error updating issue. ${msg}`,
});
});
});
});
- 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);
+ describe('Locked warning', () => {
+ beforeEach(async () => {
+ await createComponent();
});
- 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 });
+ it('shows locked warning if form is open & data is different', async () => {
+ await openForm();
+ await advanceToNextPoll();
- expect(wrapper.vm.issueChanged).toBe(false);
+ expect(findForm().props().formState.lockedWarningVisible).toBe(true);
+ expect(findForm().props().formState.lock_version).toBe(1);
});
});
describe('sticky header', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
describe('when title is in view', () => {
it('is not shown', () => {
expect(findStickyHeader().exists()).toBe(false);
@@ -467,21 +348,18 @@ describe('Issuable output', () => {
});
describe('when title is not in view', () => {
- beforeEach(() => {
- wrapper.vm.state.titleText = 'Sticky header title';
+ beforeEach(async () => {
wrapper.findComponent(GlIntersectionObserver).vm.$emit('disappear');
});
it('shows with title', () => {
- expect(findStickyHeader().text()).toContain('Sticky header title');
+ expect(findStickyHeader().text()).toContain(initialRequest.title_text);
});
it('shows with title for an epic', async () => {
- wrapper.setProps({ issuableType: 'epic' });
-
- await nextTick();
+ await wrapper.setProps({ issuableType: 'epic' });
- expect(findStickyHeader().text()).toContain('Sticky header title');
+ expect(findStickyHeader().text()).toContain(' this is a title');
});
it.each`
@@ -493,9 +371,7 @@ describe('Issuable output', () => {
`(
'shows with state icon "$statusIcon" for $issuableType when status is $issuableStatus',
async ({ issuableType, issuableStatus, statusIcon }) => {
- wrapper.setProps({ issuableType, issuableStatus });
-
- await nextTick();
+ await wrapper.setProps({ issuableType, issuableStatus });
expect(findStickyHeader().findComponent(GlIcon).props('name')).toBe(statusIcon);
},
@@ -507,9 +383,7 @@ describe('Issuable output', () => {
${'shows with Closed when status is closed'} | ${STATUS_CLOSED}
${'shows with Open when status is reopened'} | ${STATUS_REOPENED}
`('$title', async ({ state }) => {
- wrapper.setProps({ issuableStatus: state });
-
- await nextTick();
+ await wrapper.setProps({ issuableStatus: state });
expect(findStickyHeader().text()).toContain(IssuableStatusText[state]);
});
@@ -519,9 +393,7 @@ describe('Issuable output', () => {
${'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();
+ await wrapper.setProps({ isConfidential });
const confidentialEl = findConfidentialBadge();
expect(confidentialEl.exists()).toBe(isConfidential);
@@ -538,9 +410,7 @@ describe('Issuable output', () => {
${'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();
+ await wrapper.setProps({ isLocked });
expect(findLockedBadge().exists()).toBe(isLocked);
});
@@ -550,9 +420,7 @@ describe('Issuable output', () => {
${'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();
+ await wrapper.setProps({ isHidden });
const hiddenBadge = findHiddenBadge();
@@ -569,6 +437,10 @@ describe('Issuable output', () => {
});
describe('Composable description component', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
const findIncidentTabs = () => wrapper.findComponent(IncidentTabs);
const borderClass = 'gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-mb-6';
@@ -587,13 +459,13 @@ describe('Issuable output', () => {
});
describe('when using incident tabs description wrapper', () => {
- beforeEach(() => {
- mountComponent(
- {
+ beforeEach(async () => {
+ await createComponent({
+ props: {
descriptionComponent: IncidentTabs,
showTitleBorder: false,
},
- {
+ options: {
mocks: {
$apollo: {
queries: {
@@ -604,7 +476,7 @@ describe('Issuable output', () => {
},
},
},
- );
+ });
});
it('does not the description component', () => {
@@ -622,48 +494,77 @@ describe('Issuable output', () => {
});
describe('taskListUpdateStarted', () => {
- it('stops polling', () => {
- jest.spyOn(wrapper.vm.poll, 'stop');
+ beforeEach(async () => {
+ await createComponent();
+ });
+
+ it('stops polling', async () => {
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+
+ findDescription().vm.$emit('taskListUpdateStarted');
- wrapper.vm.taskListUpdateStarted();
+ await advanceToNextPoll();
- expect(wrapper.vm.poll.stop).toHaveBeenCalled();
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
});
});
describe('taskListUpdateSucceeded', () => {
- it('enables polling', () => {
- jest.spyOn(wrapper.vm.poll, 'enable');
- jest.spyOn(wrapper.vm.poll, 'makeDelayedRequest');
+ beforeEach(async () => {
+ await createComponent();
+ findDescription().vm.$emit('taskListUpdateStarted');
+ });
+
+ it('enables polling', async () => {
+ // Ensure that polling is not working before
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+ await advanceToNextPoll();
+
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
- wrapper.vm.taskListUpdateSucceeded();
+ // Enable Polling an move forward
+ findDescription().vm.$emit('taskListUpdateSucceeded');
+ await advanceToNextPoll();
- expect(wrapper.vm.poll.enable).toHaveBeenCalled();
- expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY);
+ // Title has changed: polling works!
+ expect(findTitle().props().titleText).toBe(secondRequest.title_text);
});
});
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');
+ beforeEach(async () => {
+ await createComponent();
+ findDescription().vm.$emit('taskListUpdateStarted');
+ });
+
+ it('enables polling and calls updateStoreState', async () => {
+ // Ensure that polling is not working before
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+ await advanceToNextPoll();
- wrapper.vm.taskListUpdateFailed();
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
- expect(wrapper.vm.poll.enable).toHaveBeenCalled();
- expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY);
- expect(wrapper.vm.updateStoreState).toHaveBeenCalled();
+ // Enable Polling an move forward
+ findDescription().vm.$emit('taskListUpdateFailed');
+ await advanceToNextPoll();
+
+ // Title has changed: polling works!
+ expect(findTitle().props().titleText).toBe(secondRequest.title_text);
});
});
describe('saveDescription event', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
it('makes request to update issue', async () => {
const description = 'I have been updated!';
findDescription().vm.$emit('saveDescription', description);
+
await waitForPromises();
- expect(mock.history.put[0].data).toContain(description);
+ expect(axiosMock.history.put[0].data).toContain(description);
});
});
});
diff --git a/spec/frontend/issues/show/components/delete_issue_modal_spec.js b/spec/frontend/issues/show/components/delete_issue_modal_spec.js
index 97a091a1748..b8adeb24005 100644
--- a/spec/frontend/issues/show/components/delete_issue_modal_spec.js
+++ b/spec/frontend/issues/show/components/delete_issue_modal_spec.js
@@ -20,10 +20,6 @@ describe('DeleteIssueModal component', () => {
const mountComponent = (props = {}) =>
shallowMount(DeleteIssueModal, { propsData: { ...defaultProps, ...props } });
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('modal', () => {
it('renders', () => {
wrapper = mountComponent();
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index da51372dd3d..740b2f782e4 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -1,25 +1,19 @@
import $ from 'jquery';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import { GlModal } from '@gitlab/ui';
import getIssueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql';
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 createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import Description from '~/issues/show/components/description.vue';
import eventHub from '~/issues/show/event_hub';
-import { updateHistory } from '~/lib/utils/url_utility';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
import workItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
import TaskList from '~/task_list';
-import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
-import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import {
createWorkItemMutationErrorResponse,
@@ -27,14 +21,9 @@ import {
getIssueDetailsResponse,
projectWorkItemTypesQueryResponse,
} from 'jest/work_items/mock_data';
-import {
- descriptionProps as initialProps,
- descriptionHtmlWithList,
- descriptionHtmlWithCheckboxes,
- descriptionHtmlWithTask,
-} from '../mock_data/mock_data';
+import { descriptionProps as initialProps, descriptionHtmlWithList } from '../mock_data/mock_data';
-jest.mock('~/flash');
+jest.mock('~/alert');
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
updateHistory: jest.fn(),
@@ -43,9 +32,6 @@ jest.mock('~/task_list');
jest.mock('~/behaviors/markdown/render_gfm');
const mockSpriteIcons = '/icons.svg';
-const showModal = jest.fn();
-const hideModal = jest.fn();
-const showDetailsModal = jest.fn();
const $toast = {
show: jest.fn(),
};
@@ -62,7 +48,6 @@ const workItemTypesQueryHandler = jest.fn().mockResolvedValue(projectWorkItemTyp
describe('Description component', () => {
let wrapper;
- let originalGon;
Vue.use(VueApollo);
@@ -70,21 +55,16 @@ describe('Description component', () => {
const findTextarea = () => wrapper.find('[data-testid="textarea"]');
const findListItems = () => findGfmContent().findAll('ul > li');
const findTaskActionButtons = () => wrapper.findAll('.task-list-item-actions');
- const findTaskLink = () => wrapper.find('a.gfm-issue');
- const findModal = () => wrapper.findComponent(GlModal);
- const findWorkItemDetailModal = () => wrapper.findComponent(WorkItemDetailModal);
function createComponent({
props = {},
provide,
issueDetailsQueryHandler = jest.fn().mockResolvedValue(issueDetailsResponse),
createWorkItemMutationHandler,
- ...options
} = {}) {
wrapper = shallowMountExtended(Description, {
propsData: {
issueId: 1,
- issueIid: 1,
...initialProps,
...props,
},
@@ -102,25 +82,10 @@ describe('Description component', () => {
mocks: {
$toast,
},
- stubs: {
- GlModal: stubComponent(GlModal, {
- methods: {
- show: showModal,
- hide: hideModal,
- },
- }),
- WorkItemDetailModal: stubComponent(WorkItemDetailModal, {
- methods: {
- show: showDetailsModal,
- },
- }),
- },
- ...options,
});
}
beforeEach(() => {
- originalGon = window.gon;
window.gon = { sprite_icons: mockSpriteIcons };
setWindowLocation(TEST_HOST);
@@ -136,8 +101,6 @@ describe('Description component', () => {
});
afterAll(() => {
- window.gon = originalGon;
-
$('.issuable-meta .flash-container').remove();
});
@@ -285,7 +248,6 @@ describe('Description component', () => {
props: {
descriptionHtml: descriptionHtmlWithList,
},
- attachTo: document.body,
});
await nextTick();
});
@@ -325,33 +287,6 @@ describe('Description component', () => {
});
});
- describe('description with checkboxes', () => {
- beforeEach(() => {
- createComponent({
- props: {
- descriptionHtml: descriptionHtmlWithCheckboxes,
- },
- });
- return nextTick();
- });
-
- it('renders a list of hidden buttons corresponding to checkboxes in description HTML', () => {
- expect(findTaskActionButtons()).toHaveLength(3);
- });
-
- it('does not show a modal by default', () => {
- expect(findModal().exists()).toBe(false);
- });
-
- it('shows toast after delete success', async () => {
- const newDesc = 'description';
- findWorkItemDetailModal().vm.$emit('workItemDeleted', newDesc);
-
- expect(wrapper.emitted('updateDescription')).toEqual([[newDesc]]);
- expect($toast.show).toHaveBeenCalledWith('Task deleted');
- });
- });
-
describe('task list item actions', () => {
describe('converting the task list item to a task', () => {
describe('when successful', () => {
@@ -391,11 +326,7 @@ describe('Description component', () => {
});
it('calls a mutation to create a task', () => {
- const {
- confidential,
- iteration,
- milestone,
- } = issueDetailsResponse.data.workspace.issuable;
+ const { confidential, iteration, milestone } = issueDetailsResponse.data.issue;
expect(createWorkItemMutationHandler).toHaveBeenCalledWith({
input: {
confidential,
@@ -468,109 +399,4 @@ describe('Description component', () => {
});
});
});
-
- describe('work items detail', () => {
- describe('when opening and closing', () => {
- beforeEach(() => {
- createComponent({
- props: {
- descriptionHtml: descriptionHtmlWithTask,
- },
- });
- return nextTick();
- });
-
- it('opens when task button is clicked', async () => {
- await findTaskLink().trigger('click');
-
- expect(showDetailsModal).toHaveBeenCalled();
- expect(updateHistory).toHaveBeenCalledWith({
- url: `${TEST_HOST}/?work_item_id=2`,
- replace: true,
- });
- });
-
- it('closes from an open state', async () => {
- await findTaskLink().trigger('click');
-
- findWorkItemDetailModal().vm.$emit('close');
- await nextTick();
-
- expect(updateHistory).toHaveBeenLastCalledWith({
- url: `${TEST_HOST}/`,
- replace: true,
- });
- });
-
- it('tracks when opened', async () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- await findTaskLink().trigger('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(
- TRACKING_CATEGORY_SHOW,
- 'viewed_work_item_from_modal',
- {
- category: TRACKING_CATEGORY_SHOW,
- label: 'work_item_view',
- property: 'type_task',
- },
- );
- });
- });
-
- describe('when url query `work_item_id` exists', () => {
- it.each`
- behavior | workItemId | modalOpened
- ${'opens'} | ${'2'} | ${1}
- ${'does not open'} | ${'123'} | ${0}
- ${'does not open'} | ${'123e'} | ${0}
- ${'does not open'} | ${'12e3'} | ${0}
- ${'does not open'} | ${'1e23'} | ${0}
- ${'does not open'} | ${'x'} | ${0}
- ${'does not open'} | ${'undefined'} | ${0}
- `(
- '$behavior when url contains `work_item_id=$workItemId`',
- async ({ workItemId, modalOpened }) => {
- setWindowLocation(`?work_item_id=${workItemId}`);
-
- createComponent({
- props: { descriptionHtml: descriptionHtmlWithTask },
- });
-
- expect(showDetailsModal).toHaveBeenCalledTimes(modalOpened);
- },
- );
- });
- });
-
- describe('when hovering task links', () => {
- beforeEach(() => {
- createComponent({
- props: {
- descriptionHtml: descriptionHtmlWithTask,
- },
- });
- return nextTick();
- });
-
- it('prefetches work item detail after work item link is hovered for 150ms', async () => {
- await findTaskLink().trigger('mouseover');
- jest.advanceTimersByTime(150);
- await waitForPromises();
-
- expect(queryHandler).toHaveBeenCalledWith({
- id: 'gid://gitlab/WorkItem/2',
- });
- });
-
- it('does not work item detail after work item link is hovered for less than 150ms', async () => {
- await findTaskLink().trigger('mouseover');
- await findTaskLink().trigger('mouseout');
- jest.advanceTimersByTime(150);
- await waitForPromises();
-
- expect(queryHandler).not.toHaveBeenCalled();
- });
- });
});
diff --git a/spec/frontend/issues/show/components/edit_actions_spec.js b/spec/frontend/issues/show/components/edit_actions_spec.js
index 11c43ea4388..ca561149806 100644
--- a/spec/frontend/issues/show/components/edit_actions_spec.js
+++ b/spec/frontend/issues/show/components/edit_actions_spec.js
@@ -56,10 +56,6 @@ describe('Edit Actions component', () => {
createComponent();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders all buttons as enabled', () => {
const buttons = findEditButtons().wrappers;
buttons.forEach((button) => {
diff --git a/spec/frontend/issues/show/components/edited_spec.js b/spec/frontend/issues/show/components/edited_spec.js
index aa6e0a9dceb..a509627c347 100644
--- a/spec/frontend/issues/show/components/edited_spec.js
+++ b/spec/frontend/issues/show/components/edited_spec.js
@@ -15,10 +15,6 @@ describe('Edited component', () => {
const mountComponent = (propsData) => mount(Edited, { propsData });
const updatedAt = '2017-05-15T12:31:04.428Z';
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders an edited at+by string', () => {
wrapper = mountComponent({
updatedAt,
diff --git a/spec/frontend/issues/show/components/fields/description_spec.js b/spec/frontend/issues/show/components/fields/description_spec.js
index 273ddfdd5d4..5c145ed4707 100644
--- a/spec/frontend/issues/show/components/fields/description_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_spec.js
@@ -33,11 +33,6 @@ describe('Description field component', () => {
jest.spyOn(eventHub, '$emit');
});
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
it('renders markdown field with description', () => {
wrapper = mountComponent();
@@ -80,17 +75,18 @@ describe('Description field component', () => {
});
it('uses the MarkdownEditor component to edit markdown', () => {
- expect(findMarkdownEditor().props()).toEqual(
- expect.objectContaining({
- value: 'test',
- renderMarkdownPath: '/',
- markdownDocsPath: '/',
- quickActionsDocsPath: expect.any(String),
- autofocus: true,
- supportsQuickActions: true,
- enableAutocomplete: true,
- }),
- );
+ expect(findMarkdownEditor().props()).toMatchObject({
+ value: 'test',
+ renderMarkdownPath: '/',
+ autofocus: true,
+ supportsQuickActions: true,
+ quickActionsDocsPath: expect.any(String),
+ });
+
+ expect(findMarkdownEditor().vm.$attrs).toMatchObject({
+ 'enable-autocomplete': true,
+ 'markdown-docs-path': '/',
+ });
});
it('triggers update with meta+enter', () => {
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 79a3bfa9840..1e8d5e2dd95 100644
--- a/spec/frontend/issues/show/components/fields/description_template_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_template_spec.js
@@ -22,10 +22,6 @@ describe('Issue description template component with templates as hash', () => {
wrapper = shallowMount(descriptionTemplate, options);
};
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders templates as JSON hash in data attribute', () => {
createComponent();
expect(findIssuableSelector().attributes('data-data')).toBe(
diff --git a/spec/frontend/issues/show/components/fields/title_spec.js b/spec/frontend/issues/show/components/fields/title_spec.js
index a5fa96d8d64..b28762f1520 100644
--- a/spec/frontend/issues/show/components/fields/title_spec.js
+++ b/spec/frontend/issues/show/components/fields/title_spec.js
@@ -17,11 +17,6 @@ describe('Title field component', () => {
});
});
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
it('renders form control with formState title', () => {
expect(findInput().element.value).toBe('test');
});
diff --git a/spec/frontend/issues/show/components/fields/type_spec.js b/spec/frontend/issues/show/components/fields/type_spec.js
index 27ac0e1baf3..e655cf3b37d 100644
--- a/spec/frontend/issues/show/components/fields/type_spec.js
+++ b/spec/frontend/issues/show/components/fields/type_spec.js
@@ -1,4 +1,4 @@
-import { GlFormGroup, GlListbox, GlIcon } from '@gitlab/ui';
+import { GlFormGroup, GlCollapsibleListbox, GlIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@@ -32,7 +32,7 @@ describe('Issue type field component', () => {
},
};
- const findListBox = () => wrapper.findComponent(GlListbox);
+ const findListBox = () => wrapper.findComponent(GlCollapsibleListbox);
const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const findAllIssueItems = () => wrapper.findAll('[data-testid="issue-type-list-item"]');
const findIssueItemAt = (at) => findAllIssueItems().at(at);
@@ -60,10 +60,6 @@ describe('Issue type field component', () => {
mockIssueStateData = jest.fn();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
it.each`
at | text | icon
${0} | ${issuableTypes[0].text} | ${issuableTypes[0].icon}
diff --git a/spec/frontend/issues/show/components/form_spec.js b/spec/frontend/issues/show/components/form_spec.js
index aedb974cbd0..b8ed33801f2 100644
--- a/spec/frontend/issues/show/components/form_spec.js
+++ b/spec/frontend/issues/show/components/form_spec.js
@@ -30,10 +30,6 @@ describe('Inline edit form component', () => {
projectNamespace: '/',
};
- afterEach(() => {
- wrapper.destroy();
- });
-
const createComponent = (props) => {
wrapper = shallowMount(formComponent, {
propsData: {
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js
index 3d9dad3a721..58ec7387851 100644
--- a/spec/frontend/issues/show/components/header_actions_spec.js
+++ b/spec/frontend/issues/show/components/header_actions_spec.js
@@ -1,20 +1,22 @@
import Vue, { nextTick } from 'vue';
-import { GlButton, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
+import { GlDropdownItem, GlLink, GlModal, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { mockTracking } from 'helpers/tracking_helper';
-import { createAlert, VARIANT_SUCCESS } from '~/flash';
-import { IssueType, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants';
+import { createAlert, VARIANT_SUCCESS } from '~/alert';
+import { STATUS_CLOSED, STATUS_OPEN, TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants';
import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue';
import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
import HeaderActions from '~/issues/show/components/header_actions.vue';
import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants';
+import issuesEventHub from '~/issues/show/event_hub';
import promoteToEpicMutation from '~/issues/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');
+jest.mock('~/alert');
+jest.mock('~/issues/show/event_hub', () => ({ $emit: jest.fn() }));
describe('HeaderActions component', () => {
let dispatchEventSpy;
@@ -36,7 +38,7 @@ describe('HeaderActions component', () => {
iid: '32',
isIssueAuthor: true,
issuePath: 'gitlab-org/gitlab-test/-/issues/1',
- issueType: IssueType.Issue,
+ issueType: TYPE_ISSUE,
newIssuePath: 'gitlab-org/gitlab-test/-/issues/new',
projectPath: 'gitlab-org/gitlab-test',
reportAbusePath: '-/abuse_reports/add_category',
@@ -67,7 +69,8 @@ describe('HeaderActions component', () => {
},
};
- const findToggleIssueStateButton = () => wrapper.findComponent(GlButton);
+ const findToggleIssueStateButton = () => wrapper.find(`[data-testid="toggle-button"]`);
+ const findEditButton = () => wrapper.find(`[data-testid="edit-button"]`);
const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`);
const findMobileDropdown = () => findDropdownBy('mobile-dropdown');
@@ -103,6 +106,9 @@ describe('HeaderActions component', () => {
mutate: mutateMock,
},
},
+ stubs: {
+ GlButton,
+ },
});
};
@@ -113,13 +119,12 @@ describe('HeaderActions component', () => {
if (visitUrlSpy) {
visitUrlSpy.mockRestore();
}
- wrapper.destroy();
});
describe.each`
issueType
- ${IssueType.Issue}
- ${IssueType.Incident}
+ ${TYPE_ISSUE}
+ ${TYPE_INCIDENT}
`('when issue type is $issueType', ({ issueType }) => {
describe('close/reopen button', () => {
describe.each`
@@ -240,6 +245,30 @@ describe('HeaderActions component', () => {
});
});
});
+
+ describe(`show edit button ${issueType}`, () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ canUpdateIssue: true,
+ canCreateIssue: false,
+ isIssueAuthor: true,
+ issueType,
+ canReportSpam: false,
+ canPromoteToEpic: false,
+ },
+ });
+ });
+ it(`shows the edit button`, () => {
+ expect(findEditButton().exists()).toBe(true);
+ });
+
+ it('should trigger "open.form" event when clicked', async () => {
+ expect(issuesEventHub.$emit).not.toHaveBeenCalled();
+ await findEditButton().trigger('click');
+ expect(issuesEventHub.$emit).toHaveBeenCalledWith('open.form');
+ });
+ });
});
describe('delete issue button', () => {
diff --git a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
index 6c923cae0cc..6b68e7a0da6 100644
--- a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
+++ b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
@@ -9,7 +9,7 @@ import createTimelineEventMutation from '~/issues/show/components/incidents/grap
import getTimelineEvents from '~/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql';
import { timelineFormI18n } from '~/issues/show/components/incidents/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { useFakeDate } from 'helpers/fake_date';
import {
timelineEventsCreateEventResponse,
@@ -19,7 +19,7 @@ import {
Vue.use(VueApollo);
-jest.mock('~/flash');
+jest.mock('~/alert');
const fakeDate = '2020-07-08T00:00:00.000Z';
@@ -99,7 +99,6 @@ describe('Create Timeline events', () => {
afterEach(() => {
createAlert.mockReset();
- wrapper.destroy();
});
describe('createIncidentTimelineEvent', () => {
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 33a3a6eddfc..0f4fb02a40b 100644
--- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
@@ -1,4 +1,5 @@
import merge from 'lodash/merge';
+import { nextTick } from 'vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import DescriptionComponent from '~/issues/show/components/description.vue';
@@ -11,6 +12,11 @@ import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import { descriptionProps } from '../../mock_data/mock_data';
+const push = jest.fn();
+const $router = {
+ push,
+};
+
const mockAlert = {
__typename: 'AlertManagementAlert',
detailsUrl: INVALID_URL,
@@ -28,12 +34,20 @@ const defaultMocks = {
},
},
},
+ $route: { params: {} },
+ $router,
};
describe('Incident Tabs component', () => {
let wrapper;
- const mountComponent = ({ data = {}, options = {}, mount = shallowMountExtended } = {}) => {
+ const mountComponent = ({
+ data = {},
+ options = {},
+ mount = shallowMountExtended,
+ hasLinkedAlerts = false,
+ mocks = {},
+ } = {}) => {
wrapper = mount(
IncidentTabs,
merge(
@@ -54,11 +68,12 @@ describe('Incident Tabs component', () => {
slaFeatureAvailable: true,
canUpdate: true,
canUpdateTimelineEvent: true,
+ hasLinkedAlerts,
},
data() {
return { alert: mockAlert, ...data };
},
- mocks: defaultMocks,
+ mocks: { ...defaultMocks, ...mocks },
},
options,
),
@@ -102,11 +117,13 @@ describe('Incident Tabs component', () => {
});
it('renders the alert details tab', () => {
+ mountComponent({ hasLinkedAlerts: true });
expect(findAlertDetailsTab().exists()).toBe(true);
expect(findAlertDetailsTab().attributes('title')).toBe('Alert details');
});
it('renders the alert details table with the correct props', () => {
+ mountComponent({ hasLinkedAlerts: true });
const alert = { iid: mockAlert.iid };
expect(findAlertDetailsComponent().props('alert')).toMatchObject(alert);
@@ -156,6 +173,40 @@ describe('Incident Tabs component', () => {
expect(findActiveTabs()).toHaveLength(1);
expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle);
+ expect(push).toHaveBeenCalledWith('/timeline');
+ });
+ });
+
+ describe('loading page with tab', () => {
+ it('shows the timeline tab when timeline path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'timeline' } } },
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle);
+ });
+
+ it('shows the alerts tab when timeline path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'alerts' } } },
+ hasLinkedAlerts: true,
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.alertsTitle);
+ });
+
+ it('shows the metrics tab when metrics path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'metrics' } } },
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.metricsTitle);
});
});
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
index e352f9708e4..af01fd34336 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
@@ -10,12 +10,12 @@ import {
TIMELINE_EVENT_TAGS,
timelineEventTagsI18n,
} from '~/issues/show/components/incidents/constants';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { useFakeDate } from 'helpers/fake_date';
Vue.use(VueApollo);
-jest.mock('~/flash');
+jest.mock('~/alert');
const fakeDate = '2020-07-08T00:00:00.000Z';
@@ -51,7 +51,6 @@ describe('Timeline events form', () => {
afterEach(() => {
createAlert.mockReset();
- wrapper.destroy();
});
const findMarkdownField = () => wrapper.findComponent(MarkdownField);
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
index 26fda877089..8d79dece888 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js
@@ -11,7 +11,7 @@ import deleteTimelineEventMutation from '~/issues/show/components/incidents/grap
import editTimelineEventMutation from '~/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useFakeDate } from 'helpers/fake_date';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import {
mockEvents,
timelineEventsDeleteEventResponse,
@@ -26,7 +26,7 @@ import {
Vue.use(VueApollo);
-jest.mock('~/flash');
+jest.mock('~/alert');
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
const mockConfirmAction = ({ confirmed }) => {
@@ -77,10 +77,6 @@ describe('IncidentTimelineEventList', () => {
mountComponent();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('template', () => {
it('groups items correctly', () => {
expect(findTimelineEventGroups()).toHaveLength(2);
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
index 63474070701..48c3f0984a0 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
@@ -8,13 +8,13 @@ import IncidentTimelineEventsList from '~/issues/show/components/incidents/timel
import CreateTimelineEvent from '~/issues/show/components/incidents/create_timeline_event.vue';
import timelineEventsQuery from '~/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { timelineTabI18n } from '~/issues/show/components/incidents/constants';
import { timelineEventsQueryListResponse, timelineEventsQueryEmptyResponse } from './mock_data';
Vue.use(VueApollo);
-jest.mock('~/flash');
+jest.mock('~/alert');
const graphQLError = new Error('GraphQL error');
const listResponse = jest.fn().mockResolvedValue(timelineEventsQueryListResponse);
diff --git a/spec/frontend/issues/show/components/incidents/utils_spec.js b/spec/frontend/issues/show/components/incidents/utils_spec.js
index 75be17f9889..8ee0d906dd4 100644
--- a/spec/frontend/issues/show/components/incidents/utils_spec.js
+++ b/spec/frontend/issues/show/components/incidents/utils_spec.js
@@ -5,10 +5,10 @@ import {
getUtcShiftedDate,
getPreviousEventTags,
} from '~/issues/show/components/incidents/utils';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { mockTimelineEventTags } from './mock_data';
-jest.mock('~/flash');
+jest.mock('~/alert');
describe('incident utils', () => {
describe('display and log error', () => {
diff --git a/spec/frontend/issues/show/components/locked_warning_spec.js b/spec/frontend/issues/show/components/locked_warning_spec.js
index dd3c7c58380..f8a8c999632 100644
--- a/spec/frontend/issues/show/components/locked_warning_spec.js
+++ b/spec/frontend/issues/show/components/locked_warning_spec.js
@@ -13,11 +13,6 @@ describe('LockedWarning component', () => {
});
};
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
const findAlert = () => wrapper.findComponent(GlAlert);
const findLink = () => wrapper.findComponent(GlLink);
diff --git a/spec/frontend/issues/show/components/task_list_item_actions_spec.js b/spec/frontend/issues/show/components/task_list_item_actions_spec.js
index d52f9d57453..8caa5236796 100644
--- a/spec/frontend/issues/show/components/task_list_item_actions_spec.js
+++ b/spec/frontend/issues/show/components/task_list_item_actions_spec.js
@@ -17,7 +17,7 @@ describe('TaskListItemActions component', () => {
document.body.appendChild(li);
wrapper = shallowMount(TaskListItemActions, {
- provide: { canUpdate: true, toggleClass: 'task-list-item-actions' },
+ provide: { canUpdate: true },
attachTo: document.querySelector('div'),
});
};
diff --git a/spec/frontend/issues/show/components/title_spec.js b/spec/frontend/issues/show/components/title_spec.js
index 7560b733ae6..16ac675e12c 100644
--- a/spec/frontend/issues/show/components/title_spec.js
+++ b/spec/frontend/issues/show/components/title_spec.js
@@ -1,96 +1,59 @@
-import Vue, { nextTick } from 'vue';
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import titleComponent from '~/issues/show/components/title.vue';
-import eventHub from '~/issues/show/event_hub';
-import Store from '~/issues/show/stores';
+import Title from '~/issues/show/components/title.vue';
describe('Title component', () => {
- let vm;
- beforeEach(() => {
+ let wrapper;
+
+ const getTitleHeader = () => wrapper.findByTestId('issue-title');
+
+ const createWrapper = (props) => {
setHTMLFixture(`<title />`);
- const Component = Vue.extend(titleComponent);
- const store = new Store({
- titleHtml: '',
- descriptionHtml: '',
- issuableRef: '',
- });
- vm = new Component({
+ wrapper = shallowMountExtended(Title, {
propsData: {
issuableRef: '#1',
titleHtml: 'Testing <img />',
titleText: 'Testing',
- showForm: false,
- formState: store.formState,
+ ...props,
},
- }).$mount();
- });
+ });
+ };
afterEach(() => {
resetHTMLFixture();
});
it('renders title HTML', () => {
- expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>');
- });
-
- it('updates page title when changing titleHtml', async () => {
- const spy = jest.spyOn(vm, 'setPageTitle');
- vm.titleHtml = 'test';
+ createWrapper();
- await nextTick();
- expect(spy).toHaveBeenCalled();
+ expect(getTitleHeader().element.innerHTML.trim()).toBe('Testing <img>');
});
it('animates title changes', async () => {
- vm.titleHtml = 'test';
+ createWrapper();
- await nextTick();
+ await wrapper.setProps({
+ titleHtml: 'test',
+ });
- expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-pre-pulse');
- jest.runAllTimers();
+ expect(getTitleHeader().classes('issue-realtime-pre-pulse')).toBe(true);
+ jest.runAllTimers();
await nextTick();
- expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-trigger-pulse');
+ expect(getTitleHeader().classes('issue-realtime-trigger-pulse')).toBe(true);
});
it('updates page title after changing title', async () => {
- vm.titleHtml = 'changed';
- vm.titleText = 'changed';
-
- await nextTick();
- expect(document.querySelector('title').textContent.trim()).toContain('changed');
- });
+ createWrapper();
- describe('inline edit button', () => {
- it('should not show by default', () => {
- expect(vm.$el.querySelector('.btn-edit')).toBeNull();
+ await wrapper.setProps({
+ titleHtml: 'changed',
+ titleText: 'changed',
});
- 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', async () => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- vm.showInlineEditButton = true;
- vm.canUpdate = true;
-
- await nextTick();
- vm.$el.querySelector('.btn-edit').click();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('open.form');
- });
+ expect(document.querySelector('title').textContent.trim()).toContain('changed');
});
});
diff --git a/spec/frontend/issues/show/mock_data/mock_data.js b/spec/frontend/issues/show/mock_data/mock_data.js
index 9f0b6fb1148..86d09665947 100644
--- a/spec/frontend/issues/show/mock_data/mock_data.js
+++ b/spec/frontend/issues/show/mock_data/mock_data.js
@@ -24,6 +24,19 @@ export const secondRequest = {
lock_version: 2,
};
+export const putRequest = {
+ web_url: window.location.pathname,
+ title: '<p>PUT</p>',
+ title_text: 'PUT',
+ description: '<p>PUT_DESC</p>',
+ description_text: 'PUT_DESC',
+ 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',
@@ -66,47 +79,3 @@ export const descriptionHtmlWithList = `
<li data-sourcepos="3:1-3:8">todo 3</li>
</ul>
`;
-
-export const descriptionHtmlWithCheckboxes = `
- <ul dir="auto" class="task-list" data-sourcepos"3:1-5:12">
- <li class="task-list-item" data-sourcepos="3:1-3:11">
- <input class="task-list-item-checkbox" type="checkbox"> todo 1
- </li>
- <li class="task-list-item" data-sourcepos="4:1-4:12">
- <input class="task-list-item-checkbox" type="checkbox"> todo 2
- </li>
- <li class="task-list-item" data-sourcepos="5:1-5:12">
- <input class="task-list-item-checkbox" type="checkbox"> todo 3
- </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" data-issue-type="task">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>
-`;
-
-export const descriptionHtmlWithIssue = `
- <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" data-issue-type="issue">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>
-`;