786 lines
28 KiB
JavaScript
786 lines
28 KiB
JavaScript
import {
|
|
NEW_WORK_ITEM_IID,
|
|
STATE_CLOSED,
|
|
STATE_OPEN,
|
|
WIDGET_TYPE_DESCRIPTION,
|
|
WIDGET_TYPE_ASSIGNEES,
|
|
WIDGET_TYPE_HIERARCHY,
|
|
WORK_ITEM_TYPE_ENUM_EPIC,
|
|
WORK_ITEM_TYPE_ENUM_INCIDENT,
|
|
WORK_ITEM_TYPE_ENUM_ISSUE,
|
|
WORK_ITEM_TYPE_ENUM_KEY_RESULT,
|
|
WORK_ITEM_TYPE_ENUM_OBJECTIVE,
|
|
WORK_ITEM_TYPE_ENUM_REQUIREMENTS,
|
|
WORK_ITEM_TYPE_ENUM_TASK,
|
|
WORK_ITEM_TYPE_ENUM_TEST_CASE,
|
|
WORK_ITEM_TYPE_ENUM_TICKET,
|
|
WORK_ITEM_TYPE_NAME_EPIC,
|
|
WORK_ITEM_TYPE_NAME_INCIDENT,
|
|
WORK_ITEM_TYPE_NAME_ISSUE,
|
|
WORK_ITEM_TYPE_NAME_KEY_RESULT,
|
|
WORK_ITEM_TYPE_NAME_OBJECTIVE,
|
|
WORK_ITEM_TYPE_NAME_REQUIREMENTS,
|
|
WORK_ITEM_TYPE_NAME_TASK,
|
|
WORK_ITEM_TYPE_NAME_TEST_CASE,
|
|
WORK_ITEM_TYPE_NAME_TICKET,
|
|
} from '~/work_items/constants';
|
|
import {
|
|
autocompleteDataSources,
|
|
convertTypeEnumToName,
|
|
formatLabelForListbox,
|
|
formatUserForListbox,
|
|
markdownPreviewPath,
|
|
newWorkItemPath,
|
|
isReference,
|
|
workItemRoadmapPath,
|
|
saveToggleToLocalStorage,
|
|
getToggleFromLocalStorage,
|
|
makeDrawerUrlParam,
|
|
makeDrawerItemFullPath,
|
|
getItems,
|
|
canRouterNav,
|
|
formatSelectOptionForCustomField,
|
|
preserveDetailsState,
|
|
getParentGroupName,
|
|
createBranchMRApiPathHelper,
|
|
getNewWorkItemAutoSaveKey,
|
|
getNewWorkItemWidgetsAutoSaveKey,
|
|
getWorkItemWidgets,
|
|
updateDraftWorkItemType,
|
|
getDraftWorkItemType,
|
|
} from '~/work_items/utils';
|
|
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
|
|
import { TYPE_EPIC } from '~/issues/constants';
|
|
import { workItemQueryResponse } from './mock_data';
|
|
|
|
describe('formatLabelForListbox', () => {
|
|
const label = {
|
|
__typename: 'Label',
|
|
id: 'gid://gitlab/Label/1',
|
|
title: 'Label 1',
|
|
description: '',
|
|
color: '#f00',
|
|
textColor: '#00f',
|
|
};
|
|
|
|
it('formats as expected', () => {
|
|
expect(formatLabelForListbox(label)).toEqual({
|
|
text: 'Label 1',
|
|
value: 'gid://gitlab/Label/1',
|
|
color: '#f00',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('formatUserForListbox', () => {
|
|
const user = {
|
|
__typename: 'UserCore',
|
|
id: 'gid://gitlab/User/1',
|
|
avatarUrl: '',
|
|
webUrl: '',
|
|
webPath: '/doe_I',
|
|
name: 'John Doe',
|
|
username: 'doe_I',
|
|
};
|
|
|
|
it('formats as expected', () => {
|
|
expect(formatUserForListbox(user)).toEqual({
|
|
__typename: 'UserCore',
|
|
id: 'gid://gitlab/User/1',
|
|
avatarUrl: '',
|
|
webUrl: '',
|
|
webPath: '/doe_I',
|
|
name: 'John Doe',
|
|
username: 'doe_I',
|
|
text: 'John Doe',
|
|
value: 'gid://gitlab/User/1',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('autocompleteDataSources', () => {
|
|
beforeEach(() => {
|
|
gon.relative_url_root = '/foobar';
|
|
});
|
|
|
|
it('returns correct data sources for new work item in project context', () => {
|
|
expect(
|
|
autocompleteDataSources({
|
|
fullPath: 'project/group',
|
|
iid: NEW_WORK_ITEM_IID,
|
|
workItemTypeId: 2,
|
|
}),
|
|
).toEqual({
|
|
commands:
|
|
'/foobar/project/group/-/autocomplete_sources/commands?type=WorkItem&work_item_type_id=2',
|
|
labels:
|
|
'/foobar/project/group/-/autocomplete_sources/labels?type=WorkItem&work_item_type_id=2',
|
|
members:
|
|
'/foobar/project/group/-/autocomplete_sources/members?type=WorkItem&work_item_type_id=2',
|
|
issues:
|
|
'/foobar/project/group/-/autocomplete_sources/issues?type=WorkItem&work_item_type_id=2',
|
|
mergeRequests:
|
|
'/foobar/project/group/-/autocomplete_sources/merge_requests?type=WorkItem&work_item_type_id=2',
|
|
epics: '/foobar/project/group/-/autocomplete_sources/epics?type=WorkItem&work_item_type_id=2',
|
|
milestones:
|
|
'/foobar/project/group/-/autocomplete_sources/milestones?type=WorkItem&work_item_type_id=2',
|
|
iterations:
|
|
'/foobar/project/group/-/autocomplete_sources/iterations?type=WorkItem&work_item_type_id=2',
|
|
contacts:
|
|
'/foobar/project/group/-/autocomplete_sources/contacts?type=WorkItem&work_item_type_id=2',
|
|
snippets:
|
|
'/foobar/project/group/-/autocomplete_sources/snippets?type=WorkItem&work_item_type_id=2',
|
|
vulnerabilities:
|
|
'/foobar/project/group/-/autocomplete_sources/vulnerabilities?type=WorkItem&work_item_type_id=2',
|
|
wikis: '/foobar/project/group/-/autocomplete_sources/wikis?type=WorkItem&work_item_type_id=2',
|
|
});
|
|
});
|
|
|
|
it('returns correct data sources', () => {
|
|
expect(autocompleteDataSources({ fullPath: 'project/group', iid: '2' })).toEqual({
|
|
commands: '/foobar/project/group/-/autocomplete_sources/commands?type=WorkItem&type_id=2',
|
|
labels: '/foobar/project/group/-/autocomplete_sources/labels?type=WorkItem&type_id=2',
|
|
members: '/foobar/project/group/-/autocomplete_sources/members?type=WorkItem&type_id=2',
|
|
issues: '/foobar/project/group/-/autocomplete_sources/issues?type=WorkItem&type_id=2',
|
|
mergeRequests:
|
|
'/foobar/project/group/-/autocomplete_sources/merge_requests?type=WorkItem&type_id=2',
|
|
epics: '/foobar/project/group/-/autocomplete_sources/epics?type=WorkItem&type_id=2',
|
|
milestones: '/foobar/project/group/-/autocomplete_sources/milestones?type=WorkItem&type_id=2',
|
|
iterations: '/foobar/project/group/-/autocomplete_sources/iterations?type=WorkItem&type_id=2',
|
|
contacts: '/foobar/project/group/-/autocomplete_sources/contacts?type=WorkItem&type_id=2',
|
|
snippets: '/foobar/project/group/-/autocomplete_sources/snippets?type=WorkItem&type_id=2',
|
|
vulnerabilities:
|
|
'/foobar/project/group/-/autocomplete_sources/vulnerabilities?type=WorkItem&type_id=2',
|
|
wikis: '/foobar/project/group/-/autocomplete_sources/wikis?type=WorkItem&type_id=2',
|
|
});
|
|
});
|
|
|
|
it('returns correct data sources for new work item in group context', () => {
|
|
expect(
|
|
autocompleteDataSources({
|
|
fullPath: 'group',
|
|
iid: NEW_WORK_ITEM_IID,
|
|
isGroup: true,
|
|
workItemTypeId: 2,
|
|
}),
|
|
).toEqual({
|
|
commands:
|
|
'/foobar/groups/group/-/autocomplete_sources/commands?type=WorkItem&work_item_type_id=2',
|
|
labels:
|
|
'/foobar/groups/group/-/autocomplete_sources/labels?type=WorkItem&work_item_type_id=2',
|
|
members:
|
|
'/foobar/groups/group/-/autocomplete_sources/members?type=WorkItem&work_item_type_id=2',
|
|
issues:
|
|
'/foobar/groups/group/-/autocomplete_sources/issues?type=WorkItem&work_item_type_id=2',
|
|
mergeRequests:
|
|
'/foobar/groups/group/-/autocomplete_sources/merge_requests?type=WorkItem&work_item_type_id=2',
|
|
epics: '/foobar/groups/group/-/autocomplete_sources/epics?type=WorkItem&work_item_type_id=2',
|
|
milestones:
|
|
'/foobar/groups/group/-/autocomplete_sources/milestones?type=WorkItem&work_item_type_id=2',
|
|
iterations:
|
|
'/foobar/groups/group/-/autocomplete_sources/iterations?type=WorkItem&work_item_type_id=2',
|
|
vulnerabilities:
|
|
'/foobar/groups/group/-/autocomplete_sources/vulnerabilities?type=WorkItem&work_item_type_id=2',
|
|
wikis: '/foobar/groups/group/-/autocomplete_sources/wikis?type=WorkItem&work_item_type_id=2',
|
|
});
|
|
});
|
|
|
|
it('returns correct data sources with group context', () => {
|
|
expect(
|
|
autocompleteDataSources({
|
|
fullPath: 'group',
|
|
iid: '2',
|
|
isGroup: true,
|
|
}),
|
|
).toEqual({
|
|
commands: '/foobar/groups/group/-/autocomplete_sources/commands?type=WorkItem&type_id=2',
|
|
labels: '/foobar/groups/group/-/autocomplete_sources/labels?type=WorkItem&type_id=2',
|
|
members: '/foobar/groups/group/-/autocomplete_sources/members?type=WorkItem&type_id=2',
|
|
issues: '/foobar/groups/group/-/autocomplete_sources/issues?type=WorkItem&type_id=2',
|
|
mergeRequests:
|
|
'/foobar/groups/group/-/autocomplete_sources/merge_requests?type=WorkItem&type_id=2',
|
|
epics: '/foobar/groups/group/-/autocomplete_sources/epics?type=WorkItem&type_id=2',
|
|
milestones: '/foobar/groups/group/-/autocomplete_sources/milestones?type=WorkItem&type_id=2',
|
|
iterations: '/foobar/groups/group/-/autocomplete_sources/iterations?type=WorkItem&type_id=2',
|
|
vulnerabilities:
|
|
'/foobar/groups/group/-/autocomplete_sources/vulnerabilities?type=WorkItem&type_id=2',
|
|
wikis: '/foobar/groups/group/-/autocomplete_sources/wikis?type=WorkItem&type_id=2',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('markdownPreviewPath', () => {
|
|
beforeEach(() => {
|
|
gon.relative_url_root = '/foobar';
|
|
});
|
|
|
|
it('returns correct data sources', () => {
|
|
expect(markdownPreviewPath({ fullPath: 'project/group', iid: '2' })).toBe(
|
|
'/foobar/project/group/-/preview_markdown?target_type=WorkItem&target_id=2',
|
|
);
|
|
});
|
|
|
|
it('returns correct data sources with group context', () => {
|
|
expect(markdownPreviewPath({ fullPath: 'group', iid: '2', isGroup: true })).toBe(
|
|
'/foobar/groups/group/-/preview_markdown?target_type=WorkItem&target_id=2',
|
|
);
|
|
});
|
|
|
|
it('returns correct data sources with NEW_WORK_ITEM_IID', () => {
|
|
expect(markdownPreviewPath({ fullPath: 'group', iid: NEW_WORK_ITEM_IID, isGroup: true })).toBe(
|
|
'/foobar/groups/group/-/preview_markdown?target_type=WorkItem',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('newWorkItemPath', () => {
|
|
beforeEach(() => {
|
|
gon.relative_url_root = '/foobar';
|
|
});
|
|
|
|
it('returns correct path', () => {
|
|
expect(newWorkItemPath({ fullPath: 'group/project' })).toBe(
|
|
'/foobar/group/project/-/work_items/new',
|
|
);
|
|
});
|
|
|
|
it('returns correct path for workItemType', () => {
|
|
expect(
|
|
newWorkItemPath({ fullPath: 'group/project', workItemType: WORK_ITEM_TYPE_NAME_ISSUE }),
|
|
).toBe('/foobar/group/project/-/issues/new');
|
|
});
|
|
|
|
it('returns correct data sources with group context', () => {
|
|
expect(
|
|
newWorkItemPath({
|
|
fullPath: 'group',
|
|
isGroup: true,
|
|
workItemType: WORK_ITEM_TYPE_NAME_EPIC,
|
|
}),
|
|
).toBe('/foobar/groups/group/-/epics/new');
|
|
});
|
|
|
|
it('appends a query string to the path', () => {
|
|
expect(newWorkItemPath({ fullPath: 'project', query: '?foo=bar' })).toBe(
|
|
'/foobar/project/-/work_items/new?foo=bar',
|
|
);
|
|
});
|
|
|
|
it('returns `work_items` path for group issues', () => {
|
|
expect(
|
|
newWorkItemPath({
|
|
fullPath: 'my-group',
|
|
isGroup: true,
|
|
workItemType: WORK_ITEM_TYPE_NAME_ISSUE,
|
|
}),
|
|
).toBe('/foobar/groups/my-group/-/work_items/new');
|
|
});
|
|
});
|
|
|
|
describe('convertTypeEnumToName', () => {
|
|
it.each`
|
|
name | enumValue
|
|
${WORK_ITEM_TYPE_NAME_EPIC} | ${WORK_ITEM_TYPE_ENUM_EPIC}
|
|
${WORK_ITEM_TYPE_NAME_INCIDENT} | ${WORK_ITEM_TYPE_ENUM_INCIDENT}
|
|
${WORK_ITEM_TYPE_NAME_ISSUE} | ${WORK_ITEM_TYPE_ENUM_ISSUE}
|
|
${WORK_ITEM_TYPE_NAME_KEY_RESULT} | ${WORK_ITEM_TYPE_ENUM_KEY_RESULT}
|
|
${WORK_ITEM_TYPE_NAME_OBJECTIVE} | ${WORK_ITEM_TYPE_ENUM_OBJECTIVE}
|
|
${WORK_ITEM_TYPE_NAME_REQUIREMENTS} | ${WORK_ITEM_TYPE_ENUM_REQUIREMENTS}
|
|
${WORK_ITEM_TYPE_NAME_TASK} | ${WORK_ITEM_TYPE_ENUM_TASK}
|
|
${WORK_ITEM_TYPE_NAME_TEST_CASE} | ${WORK_ITEM_TYPE_ENUM_TEST_CASE}
|
|
${WORK_ITEM_TYPE_NAME_TICKET} | ${WORK_ITEM_TYPE_ENUM_TICKET}
|
|
`('returns %name when given the enum %enumValue', ({ name, enumValue }) => {
|
|
expect(convertTypeEnumToName(enumValue)).toBe(name);
|
|
});
|
|
});
|
|
|
|
describe('isReference', () => {
|
|
it.each`
|
|
referenceId | result
|
|
${'#101'} | ${true}
|
|
${'&101'} | ${true}
|
|
${'101'} | ${false}
|
|
${'#'} | ${false}
|
|
${'&'} | ${false}
|
|
${' &101'} | ${false}
|
|
${'gitlab-org&101'} | ${true}
|
|
${'gitlab-org/project-path#101'} | ${true}
|
|
${'gitlab-org/sub-group/project-path#101'} | ${true}
|
|
${'gitlab-org'} | ${false}
|
|
${'gitlab-org101#'} | ${false}
|
|
${'gitlab-org101&'} | ${false}
|
|
${'#gitlab-org101'} | ${false}
|
|
${'&gitlab-org101'} | ${false}
|
|
`('returns $result for $referenceId', ({ referenceId, result }) => {
|
|
expect(isReference(referenceId)).toBe(result);
|
|
});
|
|
});
|
|
|
|
describe('workItemRoadmapPath', () => {
|
|
it('constructs a path to the roadmap page', () => {
|
|
const path = workItemRoadmapPath('project/group', '2');
|
|
expect(path).toBe(
|
|
'/groups/project/group/-/roadmap?epic_iid=2&layout=MONTHS&timeframe_range_type=CURRENT_YEAR',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('utils for remembering user showLabel preferences', () => {
|
|
useLocalStorageSpy();
|
|
|
|
afterEach(() => {
|
|
localStorage.clear();
|
|
});
|
|
|
|
describe('saveToggleToLocalStorage', () => {
|
|
it('saves the value to localStorage', () => {
|
|
const TEST_KEY = `test-key-${new Date().getTime}`;
|
|
|
|
expect(localStorage.getItem(TEST_KEY)).toBe(null);
|
|
|
|
saveToggleToLocalStorage(TEST_KEY, true);
|
|
expect(localStorage.setItem).toHaveBeenCalled();
|
|
expect(localStorage.getItem(TEST_KEY)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('getToggleFromLocalStorage', () => {
|
|
it('defaults to true when there is no value from localStorage and no default value is passed', () => {
|
|
const TEST_KEY = `test-key-${new Date().getTime}`;
|
|
|
|
expect(localStorage.getItem(TEST_KEY)).toBe(null);
|
|
|
|
const result = getToggleFromLocalStorage(TEST_KEY);
|
|
expect(localStorage.getItem).toHaveBeenCalled();
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('returns the default boolean value passed when there is no value from localStorage', () => {
|
|
const TEST_KEY = `test-key-${new Date().getTime}`;
|
|
const DEFAULT_VALUE = false;
|
|
|
|
expect(localStorage.getItem(TEST_KEY)).toBe(null);
|
|
|
|
const result = getToggleFromLocalStorage(TEST_KEY, DEFAULT_VALUE);
|
|
expect(localStorage.getItem).toHaveBeenCalled();
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('returns the boolean value from localStorage if it exists', () => {
|
|
const TEST_KEY = `test-key-${new Date().getTime}`;
|
|
const DEFAULT_VALUE = true;
|
|
|
|
localStorage.setItem(TEST_KEY, 'false');
|
|
|
|
const newResult = getToggleFromLocalStorage(TEST_KEY, DEFAULT_VALUE);
|
|
expect(localStorage.getItem).toHaveBeenCalled();
|
|
expect(newResult).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('`makeDrawerItemFullPath`', () => {
|
|
it('returns the items `fullPath` if present', () => {
|
|
const result = makeDrawerItemFullPath(
|
|
{ fullPath: 'this/should/be/returned' },
|
|
'this/should/not',
|
|
);
|
|
expect(result).toBe('this/should/be/returned');
|
|
});
|
|
it('returns the fallback `fullPath` if `activeItem` does not have a `referencePath`', () => {
|
|
const result = makeDrawerItemFullPath({}, 'this/should/be/returned');
|
|
expect(result).toBe('this/should/be/returned');
|
|
});
|
|
describe('when `activeItem` has a `referencePath`', () => {
|
|
it('handles the default `issuableType` of `ISSUE`', () => {
|
|
const result = makeDrawerItemFullPath(
|
|
{ referencePath: 'this/should/be/returned#100' },
|
|
'this/should/not',
|
|
);
|
|
expect(result).toBe('this/should/be/returned');
|
|
});
|
|
it('handles case where `issuableType` is an `EPIC`', () => {
|
|
const result = makeDrawerItemFullPath(
|
|
{ referencePath: 'this/should/be/returned&100' },
|
|
'this/should/not',
|
|
TYPE_EPIC,
|
|
);
|
|
expect(result).toBe('this/should/be/returned');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('`makeDrawerUrlParam`', () => {
|
|
it('returns iid, full_path, and id', () => {
|
|
const result = makeDrawerUrlParam(
|
|
{ id: 'gid://gitlab/Issue/1', iid: '123', fullPath: 'gitlab-org/gitlab' },
|
|
'gitlab-org/gitlab',
|
|
);
|
|
expect(result).toEqual(
|
|
btoa(JSON.stringify({ iid: '123', full_path: 'gitlab-org/gitlab', id: 1 })),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getNewWorkItemAutoSaveKey', () => {
|
|
let originalWindowLocation;
|
|
|
|
beforeEach(() => {
|
|
originalWindowLocation = window.location;
|
|
delete window.location;
|
|
window.location = new URL('https://gitlab.example.com');
|
|
});
|
|
|
|
afterEach(() => {
|
|
window.location = originalWindowLocation;
|
|
});
|
|
|
|
it('returns autosave key for a new work item', () => {
|
|
const autosaveKey = getNewWorkItemAutoSaveKey({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
workItemType: 'issue',
|
|
});
|
|
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-draft');
|
|
});
|
|
|
|
it.each`
|
|
locationSearch | expectedAutosaveKey
|
|
${'vulnerability_id=1'} | ${'new-gitlab-org/gitlab-issue-vulnerability_id=1-draft'}
|
|
${'discussion_to_resolve=2'} | ${'new-gitlab-org/gitlab-issue-discussion_to_resolve=2-draft'}
|
|
${'issue[issue_type]=Issue'} | ${'new-gitlab-org/gitlab-issue-issue%5Bissue_type%5D=Issue-draft'}
|
|
${'issuable_template=FeatureIssue'} | ${'new-gitlab-org/gitlab-issue-issuable_template=FeatureIssue-draft'}
|
|
${'discussion_to_resolve=2&state=opened'} | ${'new-gitlab-org/gitlab-issue-discussion_to_resolve=2-draft'}
|
|
`(
|
|
'returns autosave key with query params $locationSearch',
|
|
({ locationSearch, expectedAutosaveKey }) => {
|
|
window.location.search = locationSearch;
|
|
const autosaveKey = getNewWorkItemAutoSaveKey({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
workItemType: 'issue',
|
|
});
|
|
|
|
expect(autosaveKey).toEqual(expectedAutosaveKey);
|
|
},
|
|
);
|
|
|
|
it('returns autosave key for new related item', () => {
|
|
const autosaveKey = getNewWorkItemAutoSaveKey({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
workItemType: 'issue',
|
|
relatedItemId: 'gid://gitlab/WorkItem/22',
|
|
});
|
|
|
|
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-related-22-draft');
|
|
});
|
|
});
|
|
|
|
describe('getNewWorkItemWidgetsAutoSaveKey', () => {
|
|
it('returns autosave key for a new work item', () => {
|
|
const autosaveKey = getNewWorkItemWidgetsAutoSaveKey({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
});
|
|
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-widgets-draft');
|
|
});
|
|
|
|
it('returns autosave key for new related item', () => {
|
|
const autosaveKey = getNewWorkItemWidgetsAutoSaveKey({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
relatedItemId: 'gid://gitlab/WorkItem/22',
|
|
});
|
|
|
|
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-related-22-widgets-draft');
|
|
});
|
|
});
|
|
|
|
describe('getWorkItemWidgets', () => {
|
|
it('returns the correct widgets for a work item', () => {
|
|
const result = getWorkItemWidgets({
|
|
workspace: {
|
|
workItem: workItemQueryResponse.data.workItem,
|
|
},
|
|
});
|
|
|
|
const { widgets } = workItemQueryResponse.data.workItem;
|
|
expect(result).toEqual({
|
|
TITLE: workItemQueryResponse.data.workItem.title,
|
|
TYPE: workItemQueryResponse.data.workItem.workItemType,
|
|
[WIDGET_TYPE_DESCRIPTION]: widgets.find((widget) => widget.type === WIDGET_TYPE_DESCRIPTION),
|
|
[WIDGET_TYPE_ASSIGNEES]: widgets.find((widget) => widget.type === WIDGET_TYPE_ASSIGNEES),
|
|
[WIDGET_TYPE_HIERARCHY]: widgets.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('updateDraftWorkItemType', () => {
|
|
useLocalStorageSpy();
|
|
|
|
const workItemWidgetsAutosaveKey = 'autosave/new-gitlab-org/gitlab-widgets-draft';
|
|
const workItemType = {
|
|
id: 'gid://gitlab/WorkItemType/1',
|
|
name: WORK_ITEM_TYPE_NAME_ISSUE,
|
|
iconName: 'issue-type-issue',
|
|
};
|
|
|
|
afterEach(() => {
|
|
localStorage.clear();
|
|
});
|
|
|
|
it('sets `TYPE` with workItemType to localStorage widgets drafts key when it does not exist', () => {
|
|
updateDraftWorkItemType({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
workItemType,
|
|
});
|
|
|
|
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
workItemWidgetsAutosaveKey,
|
|
JSON.stringify({ TYPE: workItemType }),
|
|
);
|
|
});
|
|
|
|
it('updates `TYPE` with workItemType to localStorage widgets drafts key when it already exists', () => {
|
|
localStorage.setItem(workItemWidgetsAutosaveKey, JSON.stringify({ TITLE: 'Some work item' }));
|
|
|
|
updateDraftWorkItemType({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
workItemType,
|
|
});
|
|
|
|
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
workItemWidgetsAutosaveKey,
|
|
JSON.stringify({ TITLE: 'Some work item', TYPE: workItemType }),
|
|
);
|
|
});
|
|
|
|
it('updates `TYPE` with workItemType to localStorage widgets for related item drafts key when it already exists', () => {
|
|
const workItemWidgetsKey = 'autosave/new-gitlab-org/gitlab-related-22-widgets-draft';
|
|
localStorage.setItem(workItemWidgetsKey, JSON.stringify({ TITLE: 'Some work item' }));
|
|
|
|
updateDraftWorkItemType({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
relatedItemId: 'gid://gitlab/WorkItem/22',
|
|
workItemType,
|
|
});
|
|
|
|
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
workItemWidgetsKey,
|
|
JSON.stringify({ TITLE: 'Some work item', TYPE: workItemType }),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getDraftWorkItemType', () => {
|
|
afterEach(() => {
|
|
localStorage.clear();
|
|
});
|
|
|
|
it('gets `TYPE` from localStorage widgets draft when it exists', () => {
|
|
localStorage.setItem(
|
|
'autosave/new-gitlab-org/gitlab-widgets-draft',
|
|
JSON.stringify({ TYPE: 'Issue' }),
|
|
);
|
|
const workItemType = getDraftWorkItemType({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
});
|
|
|
|
expect(workItemType).toBe('Issue');
|
|
});
|
|
|
|
it('gets `TYPE` from localStorage widgets for related item draft when it exists', () => {
|
|
localStorage.setItem(
|
|
'autosave/new-gitlab-org/gitlab-related-22-widgets-draft',
|
|
JSON.stringify({ TYPE: 'Issue' }),
|
|
);
|
|
const workItemType = getDraftWorkItemType({
|
|
fullPath: 'gitlab-org/gitlab',
|
|
relatedItemId: 'gid://gitlab/WorkItem/22',
|
|
});
|
|
|
|
expect(workItemType).toBe('Issue');
|
|
});
|
|
});
|
|
|
|
describe('`getItems`', () => {
|
|
it('returns all children when showClosed flag is on', () => {
|
|
const children = [
|
|
{ id: 1, state: STATE_OPEN },
|
|
{ id: 2, state: STATE_CLOSED },
|
|
];
|
|
const result = getItems(true)(children);
|
|
expect(result).toEqual(children);
|
|
});
|
|
|
|
it('returns only open children when showClosed flag is off', () => {
|
|
const openChildren = [
|
|
{ id: 1, state: STATE_OPEN },
|
|
{ id: 2, state: STATE_OPEN },
|
|
];
|
|
const closedChildren = [{ id: 3, state: STATE_CLOSED }];
|
|
const children = openChildren.concat(closedChildren);
|
|
const result = getItems(false)(children);
|
|
expect(result).toEqual(openChildren);
|
|
});
|
|
});
|
|
|
|
describe('canRouterNav', () => {
|
|
const projectFullPath = 'gitlab-org/gitlab';
|
|
const groupFullPath = 'gitlab-org';
|
|
const projectWebUrl = (fullPath = projectFullPath) => `/${fullPath}/-/issues/1`;
|
|
const groupWebUrl = (fullPath = groupFullPath) => `/groups/${fullPath}/-/epics/1`;
|
|
it.each`
|
|
contextFullPath | targetWebUrl | contextIsGroup | issueAsWorkItem | shouldRouterNav
|
|
${projectFullPath} | ${projectWebUrl()} | ${false} | ${false} | ${false}
|
|
${projectFullPath} | ${projectWebUrl()} | ${false} | ${true} | ${true}
|
|
${projectFullPath} | ${projectWebUrl('gitlab-org/gitlab-other')} | ${false} | ${false} | ${false}
|
|
${projectFullPath} | ${projectWebUrl('gitlab-org/gitlab-other')} | ${false} | ${true} | ${false}
|
|
${groupFullPath} | ${groupWebUrl()} | ${true} | ${false} | ${true}
|
|
${groupFullPath} | ${groupWebUrl()} | ${true} | ${true} | ${true}
|
|
${groupFullPath} | ${groupWebUrl('gitlab-other')} | ${true} | ${false} | ${false}
|
|
${groupFullPath} | ${groupWebUrl('gitlab-other')} | ${true} | ${true} | ${false}
|
|
`(
|
|
`returns $shouldRouterNav when fullPath is $contextFullPath, webUrl is $targetWebUrl, isGroup is $contextIsGroup, and issueAsWorkItem is $issueAsWorkItem`,
|
|
({ contextFullPath, targetWebUrl, contextIsGroup, issueAsWorkItem, shouldRouterNav }) => {
|
|
expect(
|
|
canRouterNav({
|
|
fullPath: contextFullPath,
|
|
webUrl: targetWebUrl,
|
|
isGroup: contextIsGroup,
|
|
issueAsWorkItem,
|
|
}),
|
|
).toBe(shouldRouterNav);
|
|
},
|
|
);
|
|
});
|
|
|
|
describe('formatSelectOptionForCustomField', () => {
|
|
it('returns object with text and value properties', () => {
|
|
const data = {
|
|
id: 1,
|
|
value: 'test',
|
|
};
|
|
const result = {
|
|
text: 'test',
|
|
value: 1,
|
|
};
|
|
|
|
expect(formatSelectOptionForCustomField(data)).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getParentGroupName', () => {
|
|
it('returns parent group name from namespace', () => {
|
|
const namespaceFullName = 'Flightjs / Flight';
|
|
expect(getParentGroupName(namespaceFullName)).toEqual('Flightjs');
|
|
});
|
|
});
|
|
|
|
describe('preserveDetailsState', () => {
|
|
const descriptionHtml = '<details><summary>Test</summary><p>Content</p></details>';
|
|
let element;
|
|
|
|
beforeEach(() => {
|
|
element = document.createElement('div');
|
|
});
|
|
|
|
it('returns null when there are no open details elements', () => {
|
|
element.innerHTML = '<details><summary>Test</summary><p>Content</p></details>';
|
|
|
|
expect(preserveDetailsState(element, descriptionHtml)).toBe(null);
|
|
});
|
|
|
|
it('returns null when number of details elements does not match', () => {
|
|
element.innerHTML = '<details open><summary>Test</summary><p>Content</p></details>';
|
|
const newDescriptionHtml =
|
|
'<details><summary>Test</summary><p>Content</p></details><details><summary>Test 2</summary><p>Content 2</p></details>';
|
|
|
|
expect(preserveDetailsState(element, newDescriptionHtml)).toBe(null);
|
|
});
|
|
|
|
it('preserves open state of details elements', () => {
|
|
element.innerHTML = '<details open><summary>Test</summary><p>Content</p></details>';
|
|
|
|
expect(preserveDetailsState(element, descriptionHtml)).toBe(
|
|
'<details open="true"><summary>Test</summary><p>Content</p></details>',
|
|
);
|
|
});
|
|
|
|
it('handles multiple details elements', () => {
|
|
element.innerHTML = `
|
|
<details open><summary>Test 1</summary><p>Content 1</p></details>
|
|
<details><summary>Test 2</summary><p>Content 2</p></details>
|
|
`;
|
|
const newDescriptionHtml = `
|
|
<details><summary>Test 1</summary><p>Content 1</p></details>
|
|
<details><summary>Test 2</summary><p>Content 2</p></details>
|
|
`;
|
|
|
|
expect(preserveDetailsState(element, newDescriptionHtml)).toBe(`
|
|
<details open="true"><summary>Test 1</summary><p>Content 1</p></details>
|
|
<details><summary>Test 2</summary><p>Content 2</p></details>
|
|
`);
|
|
});
|
|
});
|
|
|
|
describe('createBranch', () => {
|
|
it('returns a "create branch" path when given fullPath', () => {
|
|
expect(createBranchMRApiPathHelper.createBranch('myGroup/myProject')).toBe(
|
|
'/myGroup/myProject/-/branches',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('createMR', () => {
|
|
const fullPath = 'gitlab-org/gitlab';
|
|
const workItemIID = '12';
|
|
const sourceBranch = '12-fix';
|
|
const targetBranch = 'main';
|
|
|
|
it('returns MR url with target branch', () => {
|
|
const path = createBranchMRApiPathHelper.createMR({
|
|
fullPath,
|
|
workItemIid: workItemIID,
|
|
sourceBranch,
|
|
targetBranch,
|
|
});
|
|
expect(path).toBe(
|
|
'/gitlab-org/gitlab/-/merge_requests/new?merge_request%5Bissue_iid%5D=12&merge_request%5Bsource_branch%5D=12-fix&merge_request%5Btarget_branch%5D=main',
|
|
);
|
|
});
|
|
|
|
it('returns MR url without target branch', () => {
|
|
const path = createBranchMRApiPathHelper.createMR({
|
|
fullPath,
|
|
workItemIid: workItemIID,
|
|
sourceBranch,
|
|
});
|
|
expect(path).toBe(
|
|
'/gitlab-org/gitlab/-/merge_requests/new?merge_request%5Bissue_iid%5D=12&merge_request%5Bsource_branch%5D=12-fix',
|
|
);
|
|
});
|
|
|
|
it('returns MR url with relative url', () => {
|
|
gon.relative_url_root = '/foobar';
|
|
|
|
const path = createBranchMRApiPathHelper.createMR({
|
|
fullPath,
|
|
workItemIid: workItemIID,
|
|
sourceBranch,
|
|
});
|
|
expect(path).toBe(
|
|
'/foobar/gitlab-org/gitlab/-/merge_requests/new?merge_request%5Bissue_iid%5D=12&merge_request%5Bsource_branch%5D=12-fix',
|
|
);
|
|
});
|
|
|
|
it('returns url with encoded branch names', () => {
|
|
const path = createBranchMRApiPathHelper.createMR({
|
|
fullPath,
|
|
workItemIid: workItemIID,
|
|
sourceBranch: 'source-branch#1',
|
|
targetBranch: 'target-branch#1',
|
|
});
|
|
|
|
expect(path).toBe(
|
|
'/gitlab-org/gitlab/-/merge_requests/new?merge_request%5Bissue_iid%5D=12&merge_request%5Bsource_branch%5D=source-branch%231&merge_request%5Btarget_branch%5D=target-branch%231',
|
|
);
|
|
});
|
|
});
|