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_detail_spec.js')
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js292
1 files changed, 100 insertions, 192 deletions
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index d8ba8ea74f2..7ceae935d2d 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -5,10 +5,11 @@ import {
GlSkeletonLoader,
GlButton,
GlEmptyState,
+ GlIntersectionObserver,
} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { isLoggedIn } from '~/lib/utils/common_utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -18,12 +19,8 @@ import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
import WorkItemDescription from '~/work_items/components/work_item_description.vue';
import WorkItemCreatedUpdated from '~/work_items/components/work_item_created_updated.vue';
-import WorkItemDueDate from '~/work_items/components/work_item_due_date.vue';
-import WorkItemState from '~/work_items/components/work_item_state.vue';
+import WorkItemAttributesWrapper from '~/work_items/components/work_item_attributes_wrapper.vue';
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
-import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
-import WorkItemLabels from '~/work_items/components/work_item_labels.vue';
-import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue';
import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue';
import WorkItemNotes from '~/work_items/components/work_item_notes.vue';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
@@ -31,20 +28,13 @@ import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_sel
import WorkItemTodos from '~/work_items/components/work_item_todos.vue';
import { i18n } from '~/work_items/constants';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
-import workItemDatesSubscription from '~/graphql_shared/subscriptions/work_item_dates.subscription.graphql';
-import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
-import workItemAssigneesSubscription from '~/work_items/graphql/work_item_assignees.subscription.graphql';
-import workItemMilestoneSubscription from '~/work_items/graphql/work_item_milestone.subscription.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import updateWorkItemTaskMutation from '~/work_items/graphql/update_work_item_task.mutation.graphql';
+import workItemUpdatedSubscription from '~/work_items/graphql/work_item_updated.subscription.graphql';
import {
mockParent,
- workItemDatesSubscriptionResponse,
workItemByIidResponseFactory,
- workItemTitleSubscriptionResponse,
- workItemAssigneesSubscriptionResponse,
- workItemMilestoneSubscriptionResponse,
objectiveType,
mockWorkItemCommentNote,
} from '../mock_data';
@@ -63,16 +53,11 @@ describe('WorkItemDetail component', () => {
canDelete: true,
});
const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
- const datesSubscriptionHandler = jest.fn().mockResolvedValue(workItemDatesSubscriptionResponse);
- const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
- const milestoneSubscriptionHandler = jest
- .fn()
- .mockResolvedValue(workItemMilestoneSubscriptionResponse);
- const assigneesSubscriptionHandler = jest
- .fn()
- .mockResolvedValue(workItemAssigneesSubscriptionResponse);
const showModalHandler = jest.fn();
const { id } = workItemQueryResponse.data.workspace.workItems.nodes[0];
+ const workItemUpdatedSubscriptionHandler = jest
+ .fn()
+ .mockResolvedValue({ data: { workItemUpdated: null } });
const findAlert = () => wrapper.findComponent(GlAlert);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
@@ -81,42 +66,39 @@ describe('WorkItemDetail component', () => {
const findWorkItemActions = () => wrapper.findComponent(WorkItemActions);
const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle);
const findCreatedUpdated = () => wrapper.findComponent(WorkItemCreatedUpdated);
- const findWorkItemState = () => wrapper.findComponent(WorkItemState);
const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription);
- const findWorkItemDueDate = () => wrapper.findComponent(WorkItemDueDate);
- const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
- const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels);
- const findWorkItemMilestone = () => wrapper.findComponent(WorkItemMilestone);
- const findParent = () => wrapper.find('[data-testid="work-item-parent"]');
+ const findWorkItemAttributesWrapper = () => wrapper.findComponent(WorkItemAttributesWrapper);
+ const findParent = () => wrapper.findByTestId('work-item-parent');
const findParentButton = () => findParent().findComponent(GlButton);
- const findCloseButton = () => wrapper.find('[data-testid="work-item-close"]');
- const findWorkItemType = () => wrapper.find('[data-testid="work-item-type"]');
+ const findCloseButton = () => wrapper.findByTestId('work-item-close');
+ const findWorkItemType = () => wrapper.findByTestId('work-item-type');
const findHierarchyTree = () => wrapper.findComponent(WorkItemTree);
const findNotesWidget = () => wrapper.findComponent(WorkItemNotes);
const findModal = () => wrapper.findComponent(WorkItemDetailModal);
const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector);
const findWorkItemTodos = () => wrapper.findComponent(WorkItemTodos);
+ const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
+ const findStickyHeader = () => wrapper.findByTestId('work-item-sticky-header');
+ const findWorkItemTwoColumnViewContainer = () => wrapper.findByTestId('work-item-overview');
+ const findRightSidebar = () => wrapper.findByTestId('work-item-overview-right-sidebar');
+ const triggerPageScroll = () => findIntersectionObserver().vm.$emit('disappear');
const createComponent = ({
isModal = false,
updateInProgress = false,
workItemIid = '1',
handler = successHandler,
- subscriptionHandler = titleSubscriptionHandler,
confidentialityMock = [updateWorkItemMutation, jest.fn()],
error = undefined,
workItemsMvc2Enabled = false,
} = {}) => {
const handlers = [
[workItemByIidQuery, handler],
- [workItemTitleSubscription, subscriptionHandler],
- [workItemDatesSubscription, datesSubscriptionHandler],
- [workItemAssigneesSubscription, assigneesSubscriptionHandler],
- [workItemMilestoneSubscription, milestoneSubscriptionHandler],
+ [workItemUpdatedSubscription, workItemUpdatedSubscriptionHandler],
confidentialityMock,
];
- wrapper = shallowMount(WorkItemDetail, {
+ wrapper = shallowMountExtended(WorkItemDetail, {
apolloProvider: createMockApollo(handlers),
isLoggedIn: isLoggedIn(),
propsData: {
@@ -163,13 +145,18 @@ describe('WorkItemDetail component', () => {
});
describe('when there is no `workItemIid` prop', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createComponent({ workItemIid: null });
+ await waitForPromises();
});
it('skips the work item query', () => {
expect(successHandler).not.toHaveBeenCalled();
});
+
+ it('skips the work item updated subscription', () => {
+ expect(workItemUpdatedSubscriptionHandler).not.toHaveBeenCalled();
+ });
});
describe('when loading', () => {
@@ -179,7 +166,6 @@ describe('WorkItemDetail component', () => {
it('renders skeleton loader', () => {
expect(findSkeleton().exists()).toBe(true);
- expect(findWorkItemState().exists()).toBe(false);
expect(findWorkItemTitle().exists()).toBe(false);
});
});
@@ -192,7 +178,6 @@ describe('WorkItemDetail component', () => {
it('does not render skeleton', () => {
expect(findSkeleton().exists()).toBe(false);
- expect(findWorkItemState().exists()).toBe(true);
expect(findWorkItemTitle().exists()).toBe(true);
});
@@ -203,6 +188,10 @@ describe('WorkItemDetail component', () => {
it('renders todos widget if logged in', () => {
expect(findWorkItemTodos().exists()).toBe(true);
});
+
+ it('calls the work item updated subscription', () => {
+ expect(workItemUpdatedSubscriptionHandler).toHaveBeenCalledWith({ id });
+ });
});
describe('close button', () => {
@@ -488,159 +477,6 @@ describe('WorkItemDetail component', () => {
expect(findAlert().text()).toBe(updateError);
});
- describe('subscriptions', () => {
- it('calls the title subscription', async () => {
- createComponent();
- await waitForPromises();
-
- expect(titleSubscriptionHandler).toHaveBeenCalledWith({ issuableId: id });
- });
-
- describe('assignees subscription', () => {
- describe('when the assignees widget exists', () => {
- it('calls the assignees subscription', async () => {
- createComponent();
- await waitForPromises();
-
- expect(assigneesSubscriptionHandler).toHaveBeenCalledWith({ issuableId: id });
- });
- });
-
- describe('when the assignees widget does not exist', () => {
- it('does not call the assignees subscription', async () => {
- const response = workItemByIidResponseFactory({ assigneesWidgetPresent: false });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(assigneesSubscriptionHandler).not.toHaveBeenCalled();
- });
- });
- });
-
- describe('dates subscription', () => {
- describe('when the due date widget exists', () => {
- it('calls the dates subscription', async () => {
- createComponent();
- await waitForPromises();
-
- expect(datesSubscriptionHandler).toHaveBeenCalledWith({ issuableId: id });
- });
- });
-
- describe('when the due date widget does not exist', () => {
- it('does not call the dates subscription', async () => {
- const response = workItemByIidResponseFactory({ datesWidgetPresent: false });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(datesSubscriptionHandler).not.toHaveBeenCalled();
- });
- });
- });
- });
-
- describe('assignees widget', () => {
- it('renders assignees component when widget is returned from the API', async () => {
- createComponent();
- await waitForPromises();
-
- expect(findWorkItemAssignees().exists()).toBe(true);
- });
-
- it('does not render assignees component when widget is not returned from the API', async () => {
- createComponent({
- handler: jest
- .fn()
- .mockResolvedValue(workItemByIidResponseFactory({ assigneesWidgetPresent: false })),
- });
- await waitForPromises();
-
- expect(findWorkItemAssignees().exists()).toBe(false);
- });
- });
-
- describe('labels widget', () => {
- it.each`
- description | labelsWidgetPresent | exists
- ${'renders when widget is returned from API'} | ${true} | ${true}
- ${'does not render when widget is not returned from API'} | ${false} | ${false}
- `('$description', async ({ labelsWidgetPresent, exists }) => {
- const response = workItemByIidResponseFactory({ labelsWidgetPresent });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(findWorkItemLabels().exists()).toBe(exists);
- });
- });
-
- describe('dates widget', () => {
- describe.each`
- description | datesWidgetPresent | exists
- ${'when widget is returned from API'} | ${true} | ${true}
- ${'when widget is not returned from API'} | ${false} | ${false}
- `('$description', ({ datesWidgetPresent, exists }) => {
- it(`${datesWidgetPresent ? 'renders' : 'does not render'} due date component`, async () => {
- const response = workItemByIidResponseFactory({ datesWidgetPresent });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(findWorkItemDueDate().exists()).toBe(exists);
- });
- });
-
- it('shows an error message when it emits an `error` event', async () => {
- createComponent();
- await waitForPromises();
- const updateError = 'Failed to update';
-
- findWorkItemDueDate().vm.$emit('error', updateError);
- await waitForPromises();
-
- expect(findAlert().text()).toBe(updateError);
- });
- });
-
- describe('milestone widget', () => {
- it.each`
- description | milestoneWidgetPresent | exists
- ${'renders when widget is returned from API'} | ${true} | ${true}
- ${'does not render when widget is not returned from API'} | ${false} | ${false}
- `('$description', async ({ milestoneWidgetPresent, exists }) => {
- const response = workItemByIidResponseFactory({ milestoneWidgetPresent });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(findWorkItemMilestone().exists()).toBe(exists);
- });
-
- describe('milestone subscription', () => {
- describe('when the milestone widget exists', () => {
- it('calls the milestone subscription', async () => {
- createComponent();
- await waitForPromises();
-
- expect(milestoneSubscriptionHandler).toHaveBeenCalledWith({ issuableId: id });
- });
- });
-
- describe('when the assignees widget does not exist', () => {
- it('does not call the milestone subscription', async () => {
- const response = workItemByIidResponseFactory({ milestoneWidgetPresent: false });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(milestoneSubscriptionHandler).not.toHaveBeenCalled();
- });
- });
- });
- });
-
it('calls the work item query', async () => {
createComponent();
await waitForPromises();
@@ -796,4 +632,76 @@ describe('WorkItemDetail component', () => {
expect(findWorkItemTodos().exists()).toBe(false);
});
});
+
+ describe('work item attributes wrapper', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('renders the work item attributes wrapper', () => {
+ expect(findWorkItemAttributesWrapper().exists()).toBe(true);
+ });
+
+ it('shows an error message when it emits an `error` event', async () => {
+ const updateError = 'Failed to update';
+
+ findWorkItemAttributesWrapper().vm.$emit('error', updateError);
+ await waitForPromises();
+
+ expect(findAlert().text()).toBe(updateError);
+ });
+ });
+
+ describe('work item two column view', () => {
+ describe('when `workItemsMvc2Enabled` is false', () => {
+ beforeEach(async () => {
+ createComponent({ workItemsMvc2Enabled: false });
+ await waitForPromises();
+ });
+
+ it('does not have the `work-item-overview` class', () => {
+ expect(findWorkItemTwoColumnViewContainer().classes()).not.toContain('work-item-overview');
+ });
+
+ it('does not have sticky header', () => {
+ expect(findIntersectionObserver().exists()).toBe(false);
+ expect(findStickyHeader().exists()).toBe(false);
+ });
+
+ it('does not have right sidebar', () => {
+ expect(findRightSidebar().exists()).toBe(false);
+ });
+ });
+
+ describe('when `workItemsMvc2Enabled` is true', () => {
+ beforeEach(async () => {
+ createComponent({ workItemsMvc2Enabled: true });
+ await waitForPromises();
+ });
+
+ it('has the `work-item-overview` class', () => {
+ expect(findWorkItemTwoColumnViewContainer().classes()).toContain('work-item-overview');
+ });
+
+ it('does not show sticky header by default', () => {
+ expect(findStickyHeader().exists()).toBe(false);
+ });
+
+ it('has the sticky header when the page is scrolled', async () => {
+ expect(findIntersectionObserver().exists()).toBe(true);
+
+ global.pageYOffset = 100;
+ triggerPageScroll();
+
+ await nextTick();
+
+ expect(findStickyHeader().exists()).toBe(true);
+ });
+
+ it('has the right sidebar', () => {
+ expect(findRightSidebar().exists()).toBe(true);
+ });
+ });
+ });
});