Commit 976bd89d authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '267503-use-mock-apollo-client-for-testing-in-requirement_root_spec-js' into 'master'

Use mock apollo client in requirement_root_spec.js

See merge request gitlab-org/gitlab!56730
parents ccd12b43 27464ebd
......@@ -725,6 +725,7 @@ export default {
@drawer-close="handleNewRequirementCancel"
/>
<requirement-edit-form
data-testid="edit-form"
:drawer-open="showRequirementViewDrawer"
:requirement="editedRequirement"
:enable-requirement-edit="enableRequirementEdit"
......
import { GlPagination } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlPagination, GlIcon } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import VueApollo from 'vue-apollo';
import RequirementItem from 'ee/requirements/components/requirement_item.vue';
import RequirementStatusBadge from 'ee/requirements/components/requirement_status_badge.vue';
import RequirementsEmptyState from 'ee/requirements/components/requirements_empty_state.vue';
import RequirementsLoading from 'ee/requirements/components/requirements_loading.vue';
import RequirementsRoot from 'ee/requirements/components/requirements_root.vue';
import RequirementsTabs from 'ee/requirements/components/requirements_tabs.vue';
import { TestReportStatus } from 'ee/requirements/constants';
import createRequirement from 'ee/requirements/queries/createRequirement.mutation.graphql';
import exportRequirement from 'ee/requirements/queries/exportRequirements.mutation.graphql';
import projectRequirements from 'ee/requirements/queries/projectRequirements.query.graphql';
import projectRequirementsCount from 'ee/requirements/queries/projectRequirementsCount.query.graphql';
import updateRequirement from 'ee/requirements/queries/updateRequirement.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import FilteredSearchBarRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
......@@ -24,12 +34,19 @@ import {
mockFilters,
mockAuthorToken,
mockStatusToken,
mockInitialRequirementCounts,
mockProjectRequirementCounts,
mockProjectRequirements,
mockUpdateRequirementTitle,
mockUpdateRequirementToFailed,
mockProjectRequirementPassed,
} from '../mock_data';
jest.mock('ee/requirements/constants', () => ({
DEFAULT_PAGE_SIZE: 2,
FilterState: jest.requireActual('ee/requirements/constants').FilterState,
AvailableSortOptions: jest.requireActual('ee/requirements/constants').AvailableSortOptions,
TestReportStatus: jest.requireActual('ee/requirements/constants').TestReportStatus,
}));
jest.mock('~/flash');
......@@ -38,29 +55,26 @@ const $toast = {
show: jest.fn(),
};
const createComponent = ({
projectPath = 'gitlab-org/gitlab-shell',
initialFilterBy = FilterState.opened,
initialRequirementsCount = mockRequirementsCount,
showCreateRequirement = false,
emptyStatePath = '/assets/illustrations/empty-state/requirements.svg',
loading = false,
canCreateRequirement = true,
requirementsWebUrl = '/gitlab-org/gitlab-shell/-/requirements',
importCsvPath = '/gitlab-org/gitlab-shell/-/requirements/import_csv',
currentUserEmail = 'admin@example.com',
} = {}) =>
const localVue = createLocalVue();
const defaultProps = {
projectPath: 'gitlab-org/gitlab-shell',
initialFilterBy: FilterState.opened,
initialRequirementsCount: mockRequirementsCount,
showCreateRequirement: false,
emptyStatePath: '/assets/illustrations/empty-state/requirements.svg',
canCreateRequirement: true,
requirementsWebUrl: '/gitlab-org/gitlab-shell/-/requirements',
importCsvPath: '/gitlab-org/gitlab-shell/-/requirements/import_csv',
currentUserEmail: 'admin@example.com',
};
const createComponent = ({ props = {}, loading = false } = {}) =>
extendedWrapper(
shallowMount(RequirementsRoot, {
propsData: {
projectPath,
initialFilterBy,
initialRequirementsCount,
showCreateRequirement,
emptyStatePath,
canCreateRequirement,
requirementsWebUrl,
importCsvPath,
currentUserEmail,
...defaultProps,
...props,
},
mocks: {
$apollo: {
......@@ -72,7 +86,7 @@ const createComponent = ({
refetch: jest.fn(),
},
requirementsCount: {
...initialRequirementsCount,
...defaultProps.initialRequirementsCount,
refetch: jest.fn(),
},
},
......@@ -80,12 +94,54 @@ const createComponent = ({
},
$toast,
},
});
}),
);
const createComponentWithApollo = ({ props = {}, requestHandlers = [] } = {}) => {
localVue.use(VueApollo);
const mockApollo = createMockApollo(
[
[projectRequirements, jest.fn().mockResolvedValue(mockProjectRequirements)],
[projectRequirementsCount, jest.fn().mockResolvedValue(mockProjectRequirementCounts)],
...requestHandlers,
],
{},
{
dataIdFromObject: (object) =>
// eslint-disable-next-line no-underscore-dangle
object.__typename === 'Requirement' ? object.iid : defaultDataIdFromObject(object),
},
);
return extendedWrapper(
shallowMount(RequirementsRoot, {
localVue,
apolloProvider: mockApollo,
propsData: {
...defaultProps,
initialRequirementsCount: mockInitialRequirementCounts,
...props,
},
mocks: {
$toast,
},
stubs: {
RequirementItem,
RequirementStatusBadge,
GlIcon,
},
}),
);
};
describe('RequirementsRoot', () => {
let wrapper;
let trackingSpy;
const findRequirementEditForm = () => wrapper.findByTestId('edit-form');
const findFailedStatusIcon = () => wrapper.findByTestId('status_failed-icon');
beforeEach(() => {
wrapper = createComponent();
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
......@@ -427,51 +483,6 @@ describe('RequirementsRoot', () => {
);
});
describe('when `lastTestReportState` is included in object param', () => {
beforeEach(() => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdateMutationResult);
});
it('calls `$apollo.mutate` with `lastTestReportState` when it is not null', () => {
wrapper.vm.updateRequirement({
iid: '1',
lastTestReportState: 'PASSED',
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith(
expect.objectContaining({
mutation: updateRequirement,
variables: {
updateRequirementInput: {
projectPath: 'gitlab-org/gitlab-shell',
iid: '1',
lastTestReportState: 'PASSED',
},
},
}),
);
});
it('calls `$apollo.mutate` without `lastTestReportState` when it is null', () => {
wrapper.vm.updateRequirement({
iid: '1',
lastTestReportState: null,
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith(
expect.objectContaining({
mutation: updateRequirement,
variables: {
updateRequirementInput: {
projectPath: 'gitlab-org/gitlab-shell',
iid: '1',
},
},
}),
);
});
});
it('calls `createFlash` with provided `errorFlashMessage` param when request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(new Error({}));
......@@ -1025,4 +1036,89 @@ describe('RequirementsRoot', () => {
});
});
});
describe('with apollo mock', () => {
describe('when requirement is edited', () => {
let updateRequirementSpy;
describe('when user changes the requirement\'s status to "FAILED" from "SUCCESS"', () => {
const editRequirementToFailed = () => {
findRequirementEditForm().vm.$emit('save', {
description: mockProjectRequirementPassed.description,
iid: mockProjectRequirementPassed.iid,
title: mockProjectRequirementPassed.title,
lastTestReportState: TestReportStatus.Failed,
});
};
beforeEach(() => {
updateRequirementSpy = jest.fn().mockResolvedValue(mockUpdateRequirementToFailed);
wrapper = createComponentWithApollo({
requestHandlers: [[updateRequirement, updateRequirementSpy]],
});
});
it('calls `updateRequirement` mutation with correct parameters', () => {
editRequirementToFailed();
expect(updateRequirementSpy).toHaveBeenCalledWith({
updateRequirementInput: {
projectPath: 'gitlab-org/gitlab-shell',
iid: mockProjectRequirementPassed.iid,
lastTestReportState: TestReportStatus.Failed,
title: mockProjectRequirementPassed.title,
},
});
});
it('renders a failed badge after the update', async () => {
expect(findFailedStatusIcon().exists()).toBe(false);
editRequirementToFailed();
await waitForPromises();
expect(findFailedStatusIcon().exists()).toBe(true);
});
});
describe('when user changes the title of a requirement', () => {
const editRequirementTitle = () => {
findRequirementEditForm().vm.$emit('save', {
description: mockProjectRequirementPassed.description,
iid: mockProjectRequirementPassed.iid,
title: 'edited title',
lastTestReportState: null,
});
};
beforeEach(async () => {
updateRequirementSpy = jest.fn().mockResolvedValue(mockUpdateRequirementTitle);
wrapper = createComponentWithApollo({
requestHandlers: [[updateRequirement, updateRequirementSpy]],
});
});
it('calls `updateRequirement` mutation with correct parameters without `lastTestReport`', () => {
editRequirementTitle();
expect(updateRequirementSpy).toHaveBeenCalledWith({
updateRequirementInput: {
projectPath: 'gitlab-org/gitlab-shell',
iid: mockProjectRequirementPassed.iid,
title: 'edited title',
},
});
});
it('renders the edited title', async () => {
editRequirementTitle();
await waitForPromises();
expect(wrapper.find('.issue-title-text').text()).toContain('edited title');
});
});
});
});
});
......@@ -174,3 +174,136 @@ export const mockStatusToken = {
token: StatusToken,
operators: [{ value: '=', description: 'is', default: 'true' }],
};
/*
Mock data used for testing with mock apollo client
*/
export const mockInitialRequirementCounts = {
ARCHIVED: 0,
OPENED: 1,
ALL: 1,
};
export const mockProjectRequirementCounts = {
data: {
project: {
requirementStatesCount: {
opened: mockInitialRequirementCounts.OPENED,
archived: mockInitialRequirementCounts.ARCHIVED,
__typename: 'RequirementStatesCount',
},
__typename: 'Project',
},
},
};
const mockUser = {
...mockAuthor,
id: 'gid://gitlab/User/1',
__typename: 'User',
};
export const mockTestReportConnectionPassed = {
nodes: [mockTestReport],
__typename: 'TestReportConnection',
};
export const mockTestReportConnectionFailed = {
nodes: [mockTestReportFailed],
__typename: 'TestReportConnection',
};
export const mockEmptyTestReportConnection = {
nodes: [],
__typename: 'TestReportConnection',
};
const projectRequirementBase = {
__typename: 'Requirement',
iid: '1',
title: 'Requirement 1',
titleHtml: 'Requirement 1',
description: '',
descriptionHtml: '',
createdAt: '2021-03-15T05:24:32Z',
updatedAt: '2021-03-15T05:24:32Z',
state: 'OPENED',
userPermissions: {
updateRequirement: true,
adminRequirement: true,
__typename: 'RequirementPermissions',
},
author: {
...mockUser,
},
};
export const mockProjectRequirementFailed = {
...projectRequirementBase,
lastTestReportState: 'FAILED',
lastTestReportManuallyCreated: true,
testReports: {
...mockTestReportConnectionFailed,
},
};
export const mockProjectRequirementPassed = {
...projectRequirementBase,
lastTestReportState: 'PASSED',
lastTestReportManuallyCreated: true,
testReports: {
...mockTestReportConnectionPassed,
},
};
export const mockUpdateRequirementTitle = {
data: {
updateRequirement: {
clientMutationId: null,
errors: [],
requirement: {
...mockProjectRequirementPassed,
title: 'edited title',
},
__typename: 'UpdateRequirementPayload',
},
},
};
export const mockUpdateRequirementToFailed = {
data: {
updateRequirement: {
clientMutationId: null,
errors: [],
requirement: {
...mockProjectRequirementFailed,
},
__typename: 'UpdateRequirementPayload',
},
},
};
const mockRequirementConnection = {
nodes: [],
pageInfo: {
__typename: 'PageInfo',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'eyJpZCI6',
endCursor: 'eyJpZCI6I',
},
__typename: 'RequirementConnection',
};
export const mockProjectRequirements = {
data: {
project: {
requirements: {
...mockRequirementConnection,
nodes: [{ ...mockProjectRequirementPassed }],
},
__typename: 'Project',
},
},
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment