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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/work_items/components/work_item_ancestors')
-rw-r--r--spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_item_spec.js53
-rw-r--r--spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_spec.js99
-rw-r--r--spec/frontend/work_items/components/work_item_ancestors/mock_data.js197
-rw-r--r--spec/frontend/work_items/components/work_item_ancestors/work_item_ancestors_spec.js117
4 files changed, 466 insertions, 0 deletions
diff --git a/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_item_spec.js b/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_item_spec.js
new file mode 100644
index 00000000000..2cfe61654ad
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_item_spec.js
@@ -0,0 +1,53 @@
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import DisclosureHierarchyItem from '~/work_items/components/work_item_ancestors/disclosure_hierarchy_item.vue';
+import { mockDisclosureHierarchyItems } from './mock_data';
+
+describe('DisclosurePathItem', () => {
+ let wrapper;
+
+ const findIcon = () => wrapper.findComponent(GlIcon);
+
+ const createComponent = (props = {}, options = {}) => {
+ return shallowMount(DisclosureHierarchyItem, {
+ propsData: {
+ item: mockDisclosureHierarchyItems[0],
+ ...props,
+ },
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ describe('renders the item', () => {
+ it('renders the inline icon', () => {
+ expect(findIcon().exists()).toBe(true);
+ expect(findIcon().props('name')).toBe(mockDisclosureHierarchyItems[0].icon);
+ });
+ });
+
+ describe('item slot', () => {
+ beforeEach(() => {
+ wrapper = createComponent(null, {
+ scopedSlots: {
+ default: `
+ <div
+ data-testid="item-slot-content">
+ {{ props.item.title }}
+ </div>
+ `,
+ },
+ });
+ });
+
+ it('contains all elements passed into the additional slot', () => {
+ const item = wrapper.find('[data-testid="item-slot-content"]');
+
+ expect(item.text()).toBe(mockDisclosureHierarchyItems[0].title);
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_spec.js b/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_spec.js
new file mode 100644
index 00000000000..b808c13c3e7
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_ancestors/disclosure_hierarchy_spec.js
@@ -0,0 +1,99 @@
+import { shallowMount } from '@vue/test-utils';
+
+import { GlDisclosureDropdown, GlTooltip } from '@gitlab/ui';
+import DisclosureHierarchy from '~/work_items/components/work_item_ancestors//disclosure_hierarchy.vue';
+import DisclosureHierarchyItem from '~/work_items/components/work_item_ancestors/disclosure_hierarchy_item.vue';
+import { mockDisclosureHierarchyItems } from './mock_data';
+
+describe('DisclosurePath', () => {
+ let wrapper;
+
+ const createComponent = (props = {}, options = {}) => {
+ return shallowMount(DisclosureHierarchy, {
+ propsData: {
+ items: mockDisclosureHierarchyItems,
+ ...props,
+ },
+ ...options,
+ });
+ };
+
+ const listItems = () => wrapper.findAllComponents(DisclosureHierarchyItem);
+ const itemAt = (index) => listItems().at(index);
+ const itemTextAt = (index) => itemAt(index).props('item').title;
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ describe('renders the list of items', () => {
+ it('renders the correct number of items', () => {
+ expect(listItems().length).toBe(mockDisclosureHierarchyItems.length);
+ });
+
+ it('renders the items in the correct order', () => {
+ expect(itemTextAt(0)).toContain(mockDisclosureHierarchyItems[0].title);
+ expect(itemTextAt(4)).toContain(mockDisclosureHierarchyItems[4].title);
+ expect(itemTextAt(9)).toContain(mockDisclosureHierarchyItems[9].title);
+ });
+ });
+
+ describe('slots', () => {
+ beforeEach(() => {
+ wrapper = createComponent(null, {
+ scopedSlots: {
+ default: `
+ <div
+ :data-itemid="props.itemId"
+ data-testid="item-slot-content">
+ {{ props.item.title }}
+ </div>
+ `,
+ },
+ });
+ });
+
+ it('contains all elements passed into the default slot', () => {
+ mockDisclosureHierarchyItems.forEach((item, index) => {
+ const disclosureItem = wrapper.findAll('[data-testid="item-slot-content"]').at(index);
+
+ expect(disclosureItem.text()).toBe(item.title);
+ expect(disclosureItem.attributes('data-itemid')).toContain('disclosure-');
+ });
+ });
+ });
+
+ describe('with ellipsis', () => {
+ const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+ const findTooltip = () => wrapper.findComponent(GlTooltip);
+ const findTooltipText = () => findTooltip().text();
+ const tooltipText = 'Display more items';
+
+ beforeEach(() => {
+ wrapper = createComponent({ withEllipsis: true, ellipsisTooltipLabel: tooltipText });
+ });
+
+ describe('renders items and dropdown', () => {
+ it('renders 2 items', () => {
+ expect(listItems().length).toBe(2);
+ });
+
+ it('renders first and last items', () => {
+ expect(itemTextAt(0)).toContain(mockDisclosureHierarchyItems[0].title);
+ expect(itemTextAt(1)).toContain(
+ mockDisclosureHierarchyItems[mockDisclosureHierarchyItems.length - 1].title,
+ );
+ });
+
+ it('renders dropdown with the rest of the items passed down', () => {
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdown().props('items').length).toBe(mockDisclosureHierarchyItems.length - 2);
+ });
+
+ it('renders tooltip with text passed as prop', () => {
+ expect(findTooltip().exists()).toBe(true);
+ expect(findTooltipText()).toBe(tooltipText);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_ancestors/mock_data.js b/spec/frontend/work_items/components/work_item_ancestors/mock_data.js
new file mode 100644
index 00000000000..8e7f99658de
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_ancestors/mock_data.js
@@ -0,0 +1,197 @@
+export const mockDisclosureHierarchyItems = [
+ {
+ title: 'First',
+ icon: 'epic',
+ href: '#',
+ },
+ {
+ title: 'Second',
+ icon: 'epic',
+ href: '#',
+ },
+ {
+ title: 'Third',
+ icon: 'epic',
+ href: '#',
+ },
+ {
+ title: 'Fourth',
+ icon: 'epic',
+ href: '#',
+ },
+ {
+ title: 'Fifth',
+ icon: 'issues',
+ href: '#',
+ },
+ {
+ title: 'Sixth',
+ icon: 'issues',
+ href: '#',
+ },
+ {
+ title: 'Seventh',
+ icon: 'issues',
+ href: '#',
+ },
+ {
+ title: 'Eighth',
+ icon: 'issue-type-task',
+ href: '#',
+ disabled: true,
+ },
+ {
+ title: 'Ninth',
+ icon: 'issue-type-task',
+ href: '#',
+ },
+ {
+ title: 'Tenth',
+ icon: 'issue-type-task',
+ href: '#',
+ },
+];
+
+export const workItemAncestorsQueryResponse = {
+ data: {
+ workItem: {
+ __typename: 'WorkItem',
+ id: 'gid://gitlab/WorkItem/1',
+ title: 'Test',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetHierarchy',
+ type: 'HIERARCHY',
+ parent: {
+ id: 'gid://gitlab/Issue/1',
+ },
+ ancestors: {
+ nodes: [
+ {
+ id: 'gid://gitlab/WorkItem/444',
+ iid: '4',
+ reference: '#40',
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ confidential: false,
+ title: '123',
+ state: 'OPEN',
+ webUrl: '/gitlab-org/gitlab-test/-/work_items/4',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+};
+
+export const workItemThreeAncestorsQueryResponse = {
+ data: {
+ workItem: {
+ __typename: 'WorkItem',
+ id: 'gid://gitlab/WorkItem/1',
+ title: 'Test',
+ workItemType: {
+ __typename: 'WorkItemType',
+ id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ },
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetHierarchy',
+ type: 'HIERARCHY',
+ parent: {
+ id: 'gid://gitlab/Issue/1',
+ },
+ ancestors: {
+ nodes: [
+ {
+ id: 'gid://gitlab/WorkItem/444',
+ iid: '4',
+ reference: '#40',
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ confidential: false,
+ title: '123',
+ state: 'OPEN',
+ webUrl: '/gitlab-org/gitlab-test/-/work_items/4',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
+ },
+ {
+ id: 'gid://gitlab/WorkItem/445',
+ iid: '5',
+ reference: '#41',
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ confidential: false,
+ title: '1234',
+ state: 'OPEN',
+ webUrl: '/gitlab-org/gitlab-test/-/work_items/5',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
+ },
+ {
+ id: 'gid://gitlab/WorkItem/446',
+ iid: '6',
+ reference: '#42',
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ confidential: false,
+ title: '12345',
+ state: 'OPEN',
+ webUrl: '/gitlab-org/gitlab-test/-/work_items/6',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/2',
+ name: 'Issue',
+ iconName: 'issue-type-issue',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+};
+
+export const workItemEmptyAncestorsQueryResponse = {
+ data: {
+ workItem: {
+ __typename: 'WorkItem',
+ id: 'gid://gitlab/WorkItem/1',
+ title: 'Test',
+ workItemType: {
+ __typename: 'WorkItemType',
+ id: 'gid://gitlab/WorkItems::Type/5',
+ name: 'Task',
+ iconName: 'issue-type-task',
+ },
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetHierarchy',
+ type: 'HIERARCHY',
+ parent: {
+ id: null,
+ },
+ ancestors: {
+ nodes: [],
+ },
+ },
+ ],
+ },
+ },
+};
diff --git a/spec/frontend/work_items/components/work_item_ancestors/work_item_ancestors_spec.js b/spec/frontend/work_items/components/work_item_ancestors/work_item_ancestors_spec.js
new file mode 100644
index 00000000000..a9f66b20f06
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_ancestors/work_item_ancestors_spec.js
@@ -0,0 +1,117 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlPopover } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import { createAlert } from '~/alert';
+import DisclosureHierarchy from '~/work_items/components/work_item_ancestors/disclosure_hierarchy.vue';
+import WorkItemAncestors from '~/work_items/components/work_item_ancestors/work_item_ancestors.vue';
+import workItemAncestorsQuery from '~/work_items/graphql/work_item_ancestors.query.graphql';
+import { formatAncestors } from '~/work_items/utils';
+
+import { workItemTask } from '../../mock_data';
+import {
+ workItemAncestorsQueryResponse,
+ workItemEmptyAncestorsQueryResponse,
+ workItemThreeAncestorsQueryResponse,
+} from './mock_data';
+
+Vue.use(VueApollo);
+jest.mock('~/alert');
+
+describe('WorkItemAncestors', () => {
+ let wrapper;
+ let mockApollo;
+
+ const workItemAncestorsQueryHandler = jest.fn().mockResolvedValue(workItemAncestorsQueryResponse);
+ const workItemEmptyAncestorsQueryHandler = jest
+ .fn()
+ .mockResolvedValue(workItemEmptyAncestorsQueryResponse);
+ const workItemThreeAncestorsQueryHandler = jest
+ .fn()
+ .mockResolvedValue(workItemThreeAncestorsQueryResponse);
+ const workItemAncestorsFailureHandler = jest.fn().mockRejectedValue(new Error());
+
+ const findDisclosureHierarchy = () => wrapper.findComponent(DisclosureHierarchy);
+ const findPopover = () => wrapper.findComponent(GlPopover);
+
+ const createComponent = ({
+ props = {},
+ options = {},
+ ancestorsQueryHandler = workItemAncestorsQueryHandler,
+ } = {}) => {
+ mockApollo = createMockApollo([[workItemAncestorsQuery, ancestorsQueryHandler]]);
+ return mountExtended(WorkItemAncestors, {
+ apolloProvider: mockApollo,
+ propsData: {
+ workItem: workItemTask,
+ ...props,
+ },
+ ...options,
+ });
+ };
+
+ beforeEach(async () => {
+ createAlert.mockClear();
+ wrapper = createComponent();
+ await waitForPromises();
+ });
+
+ it('fetches work item ancestors', () => {
+ expect(workItemAncestorsQueryHandler).toHaveBeenCalled();
+ });
+
+ it('displays DisclosureHierarchy component with ancestors when work item has at least one ancestor', () => {
+ expect(findDisclosureHierarchy().exists()).toBe(true);
+ expect(findDisclosureHierarchy().props('items')).toEqual(
+ expect.objectContaining(formatAncestors(workItemAncestorsQueryResponse.data.workItem)),
+ );
+ });
+
+ it('does not display DisclosureHierarchy component when work item has no ancestor', async () => {
+ wrapper = createComponent({ ancestorsQueryHandler: workItemEmptyAncestorsQueryHandler });
+ await waitForPromises();
+
+ expect(findDisclosureHierarchy().exists()).toBe(false);
+ });
+
+ it('displays work item info in popover on hover and focus', () => {
+ expect(findPopover().exists()).toBe(true);
+ expect(findPopover().props('triggers')).toBe('hover focus');
+
+ const ancestor = findDisclosureHierarchy().props('items')[0];
+
+ expect(findPopover().text()).toContain(ancestor.title);
+ expect(findPopover().text()).toContain(ancestor.reference);
+ });
+
+ describe('when work item has less than 3 ancestors', () => {
+ it('does not activate ellipsis option for DisclosureHierarchy component', () => {
+ expect(findDisclosureHierarchy().props('withEllipsis')).toBe(false);
+ });
+ });
+
+ describe('when work item has at least 3 ancestors', () => {
+ beforeEach(async () => {
+ wrapper = createComponent({ ancestorsQueryHandler: workItemThreeAncestorsQueryHandler });
+ await waitForPromises();
+ });
+
+ it('activates ellipsis option for DisclosureHierarchy component', () => {
+ expect(findDisclosureHierarchy().props('withEllipsis')).toBe(true);
+ });
+ });
+
+ it('creates alert when the query fails', async () => {
+ createComponent({ ancestorsQueryHandler: workItemAncestorsFailureHandler });
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ captureError: true,
+ error: expect.any(Object),
+ message: 'Something went wrong while fetching ancestors.',
+ });
+ });
+});