debian-mirror-gitlab/spec/frontend/design_management/pages/design/index_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

396 lines
12 KiB
JavaScript
Raw Normal View History

2020-05-24 23:13:21 +05:30
import { GlAlert } from '@gitlab/ui';
2022-04-04 11:22:00 +05:30
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
2021-03-11 19:13:27 +05:30
import VueRouter from 'vue-router';
2021-02-22 17:27:13 +05:30
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import Api from '~/api';
2020-07-28 23:09:34 +05:30
import DesignPresentation from '~/design_management/components/design_presentation.vue';
2021-03-11 19:13:27 +05:30
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants';
2020-11-24 15:15:51 +05:30
import updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
2023-05-27 22:25:52 +05:30
import getDesignQuery from '~/design_management/graphql/queries/get_design.query.graphql';
2021-03-11 19:13:27 +05:30
import DesignIndex from '~/design_management/pages/design/index.vue';
import createRouter from '~/design_management/router';
import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
import * as utils from '~/design_management/utils/design_management_utils';
2020-05-24 23:13:21 +05:30
import {
DESIGN_NOT_FOUND_ERROR,
DESIGN_VERSION_NOT_EXIST_ERROR,
} from '~/design_management/utils/error_messages';
2021-02-22 17:27:13 +05:30
import {
DESIGN_TRACKING_PAGE_NAME,
DESIGN_SNOWPLOW_EVENT_TYPES,
2021-09-30 23:02:18 +05:30
DESIGN_SERVICE_PING_EVENT_TYPES,
2021-02-22 17:27:13 +05:30
} from '~/design_management/utils/tracking';
2023-05-27 22:25:52 +05:30
import { createAlert } from '~/alert';
import * as cacheUpdate from '~/design_management/utils/cache_update';
2021-03-11 19:13:27 +05:30
import mockAllVersions from '../../mock_data/all_versions';
import design from '../../mock_data/design';
2023-05-27 22:25:52 +05:30
import mockProject from '../../mock_data/project';
2021-03-11 19:13:27 +05:30
import mockResponseWithDesigns from '../../mock_data/designs';
import mockResponseNoDesigns from '../../mock_data/no_designs';
2023-05-27 22:25:52 +05:30
import { mockCreateImageNoteDiffResponse } from '../../mock_data/apollo_mock';
2020-05-24 23:13:21 +05:30
2023-05-27 22:25:52 +05:30
jest.mock('~/alert');
2021-02-22 17:27:13 +05:30
jest.mock('~/api.js');
2020-05-24 23:13:21 +05:30
2020-07-28 23:09:34 +05:30
const focusInput = jest.fn();
2023-05-27 22:25:52 +05:30
const mockCacheObject = {
readQuery: jest.fn().mockReturnValue(mockProject),
writeQuery: jest.fn(),
};
2021-01-29 00:20:46 +05:30
const mutate = jest.fn().mockResolvedValue();
const mockPageLayoutElement = {
classList: {
add: jest.fn(),
remove: jest.fn(),
},
};
2020-07-28 23:09:34 +05:30
const DesignReplyForm = {
template: '<div><textarea ref="textarea"></textarea></div>',
methods: {
focusInput,
},
};
2020-11-24 15:15:51 +05:30
const mockDesignNoDiscussions = {
...design,
discussions: {
nodes: [],
},
};
2023-05-27 22:25:52 +05:30
2021-01-29 00:20:46 +05:30
const annotationCoordinates = {
x: 10,
y: 10,
width: 100,
height: 100,
};
2020-07-28 23:09:34 +05:30
2022-04-04 11:22:00 +05:30
Vue.use(VueRouter);
2020-06-23 00:09:42 +05:30
2020-05-24 23:13:21 +05:30
describe('Design management design index page', () => {
let wrapper;
2020-06-23 00:09:42 +05:30
let router;
2023-05-27 22:25:52 +05:30
const findDesignReplyForm = () => wrapper.findComponent(DesignReplyForm);
2022-08-27 11:52:29 +05:30
const findSidebar = () => wrapper.findComponent(DesignSidebar);
const findDesignPresentation = () => wrapper.findComponent(DesignPresentation);
2020-05-24 23:13:21 +05:30
2021-02-22 17:27:13 +05:30
function createComponent(
{ loading = false } = {},
2022-07-23 23:45:48 +05:30
{
data = {},
intialRouteOptions = {},
provide = {},
2023-05-27 22:25:52 +05:30
stubs = { DesignSidebar, DesignReplyForm },
2022-07-23 23:45:48 +05:30
} = {},
2021-02-22 17:27:13 +05:30
) {
2020-05-24 23:13:21 +05:30
const $apollo = {
queries: {
design: {
loading,
},
},
mutate,
2023-05-27 22:25:52 +05:30
getClient() {
return {
cache: mockCacheObject,
};
},
2020-05-24 23:13:21 +05:30
};
2020-06-23 00:09:42 +05:30
router = createRouter();
2020-05-24 23:13:21 +05:30
2020-11-24 15:15:51 +05:30
router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions });
2020-05-24 23:13:21 +05:30
wrapper = shallowMount(DesignIndex, {
propsData: { id: '1' },
2020-06-23 00:09:42 +05:30
mocks: { $apollo },
2022-07-23 23:45:48 +05:30
stubs,
2020-10-24 23:57:45 +05:30
provide: {
issueIid: '1',
projectPath: 'project-path',
2021-02-22 17:27:13 +05:30
...provide,
2020-10-24 23:57:45 +05:30
},
2020-05-24 23:13:21 +05:30
data() {
return {
activeDiscussion: {
id: null,
source: null,
},
...data,
};
},
2020-06-23 00:09:42 +05:30
router,
2020-05-24 23:13:21 +05:30
});
}
2021-01-29 00:20:46 +05:30
describe('when navigating to component', () => {
it('applies fullscreen layout class', () => {
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
2022-07-23 23:45:48 +05:30
createComponent({}, { stubs: {} });
2020-06-23 00:09:42 +05:30
2021-01-29 00:20:46 +05:30
expect(mockPageLayoutElement.classList.add).toHaveBeenCalledTimes(1);
expect(mockPageLayoutElement.classList.add).toHaveBeenCalledWith(
...DESIGN_DETAIL_LAYOUT_CLASSLIST,
);
});
});
describe('when navigating within the component', () => {
it('`scale` prop of DesignPresentation component is 1', async () => {
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
2022-07-23 23:45:48 +05:30
createComponent({}, { data: { design, scale: 2 } });
2021-01-29 00:20:46 +05:30
2022-04-04 11:22:00 +05:30
await nextTick();
2021-01-29 00:20:46 +05:30
expect(findDesignPresentation().props('scale')).toBe(2);
DesignIndex.beforeRouteUpdate.call(wrapper.vm, {}, {}, jest.fn());
2022-04-04 11:22:00 +05:30
await nextTick();
2021-01-29 00:20:46 +05:30
expect(findDesignPresentation().props('scale')).toBe(1);
});
});
describe('when navigating away from component', () => {
it('removes fullscreen layout class', async () => {
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
createComponent({ loading: true });
wrapper.vm.$options.beforeRouteLeave[0].call(wrapper.vm, {}, {}, jest.fn());
expect(mockPageLayoutElement.classList.remove).toHaveBeenCalledTimes(1);
expect(mockPageLayoutElement.classList.remove).toHaveBeenCalledWith(
...DESIGN_DETAIL_LAYOUT_CLASSLIST,
);
2020-06-23 00:09:42 +05:30
});
});
2020-05-24 23:13:21 +05:30
it('sets loading state', () => {
2020-11-24 15:15:51 +05:30
createComponent({ loading: true });
2020-05-24 23:13:21 +05:30
2022-08-27 11:52:29 +05:30
expect(wrapper.findComponent(DesignPresentation).props('isLoading')).toBe(true);
expect(wrapper.findComponent(DesignSidebar).props('isLoading')).toBe(true);
2020-05-24 23:13:21 +05:30
});
it('renders design index', () => {
2020-11-24 15:15:51 +05:30
createComponent({ loading: false }, { data: { design } });
2020-05-24 23:13:21 +05:30
expect(wrapper.element).toMatchSnapshot();
2022-08-27 11:52:29 +05:30
expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
2020-05-24 23:13:21 +05:30
});
2020-06-23 00:09:42 +05:30
it('passes correct props to sidebar component', () => {
2020-11-24 15:15:51 +05:30
createComponent({ loading: false }, { data: { design } });
2020-05-24 23:13:21 +05:30
2020-06-23 00:09:42 +05:30
expect(findSidebar().props()).toEqual({
design,
2020-10-24 23:57:45 +05:30
markdownPreviewPath: '/project-path/preview_markdown?target_type=Issue',
2020-06-23 00:09:42 +05:30
resolvedDiscussionsExpanded: false,
2022-07-23 23:45:48 +05:30
isLoading: false,
2020-05-24 23:13:21 +05:30
});
});
2022-04-04 11:22:00 +05:30
it('opens a new discussion form', async () => {
2020-11-24 15:15:51 +05:30
createComponent(
{ loading: false },
{
data: {
design,
2020-05-24 23:13:21 +05:30
},
},
2020-11-24 15:15:51 +05:30
);
2020-05-24 23:13:21 +05:30
2020-07-28 23:09:34 +05:30
findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 });
2020-05-24 23:13:21 +05:30
2022-04-04 11:22:00 +05:30
await nextTick();
2023-05-27 22:25:52 +05:30
expect(findDesignReplyForm().exists()).toBe(true);
2020-05-24 23:13:21 +05:30
});
2020-07-28 23:09:34 +05:30
it('keeps new discussion form focused', () => {
2020-11-24 15:15:51 +05:30
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
2020-07-28 23:09:34 +05:30
},
},
2020-11-24 15:15:51 +05:30
);
2020-07-28 23:09:34 +05:30
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
expect(focusInput).toHaveBeenCalled();
});
2023-05-27 22:25:52 +05:30
it('sends a update and closes the form when mutation is completed', async () => {
2020-11-24 15:15:51 +05:30
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
2020-05-24 23:13:21 +05:30
},
},
2020-11-24 15:15:51 +05:30
);
2020-05-24 23:13:21 +05:30
2023-05-27 22:25:52 +05:30
const addImageDiffNoteToStore = jest.spyOn(cacheUpdate, 'updateStoreAfterAddImageDiffNote');
const mockDesignVariables = {
fullPath: 'project-path',
iid: '1',
filenames: ['gid::/gitlab/Design/1'],
atVersion: null,
};
findDesignReplyForm().vm.$emit('note-submit-complete', mockCreateImageNoteDiffResponse);
2020-05-24 23:13:21 +05:30
2022-04-04 11:22:00 +05:30
await nextTick();
2023-05-27 22:25:52 +05:30
expect(addImageDiffNoteToStore).toHaveBeenCalledWith(
mockCacheObject,
mockCreateImageNoteDiffResponse.data.createImageDiffNote,
getDesignQuery,
mockDesignVariables,
);
expect(findDesignReplyForm().exists()).toBe(false);
2020-05-24 23:13:21 +05:30
});
2022-04-04 11:22:00 +05:30
it('closes the form and clears the comment on canceling form', async () => {
2020-11-24 15:15:51 +05:30
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
2020-05-24 23:13:21 +05:30
},
},
2020-11-24 15:15:51 +05:30
);
2020-05-24 23:13:21 +05:30
2023-05-27 22:25:52 +05:30
findDesignReplyForm().vm.$emit('cancel-form');
2020-05-24 23:13:21 +05:30
2022-04-04 11:22:00 +05:30
await nextTick();
2023-05-27 22:25:52 +05:30
expect(findDesignReplyForm().exists()).toBe(false);
2020-05-24 23:13:21 +05:30
});
describe('with error', () => {
beforeEach(() => {
2020-11-24 15:15:51 +05:30
createComponent(
{ loading: false },
{
data: {
design: mockDesignNoDiscussions,
errorMessage: 'woops',
2020-05-24 23:13:21 +05:30
},
},
2020-11-24 15:15:51 +05:30
);
2020-05-24 23:13:21 +05:30
});
it('GlAlert is rendered in correct position with correct content', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('onDesignQueryResult', () => {
describe('with no designs', () => {
2022-04-04 11:22:00 +05:30
it('redirects to /designs', async () => {
2020-11-24 15:15:51 +05:30
createComponent({ loading: true });
2020-06-23 00:09:42 +05:30
router.push = jest.fn();
2020-05-24 23:13:21 +05:30
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
2022-04-04 11:22:00 +05:30
await nextTick();
2022-11-25 23:54:43 +05:30
expect(createAlert).toHaveBeenCalledTimes(1);
expect(createAlert).toHaveBeenCalledWith({ message: DESIGN_NOT_FOUND_ERROR });
2022-04-04 11:22:00 +05:30
expect(router.push).toHaveBeenCalledTimes(1);
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
2020-05-24 23:13:21 +05:30
});
});
describe('when no design exists for given version', () => {
2022-04-04 11:22:00 +05:30
it('redirects to /designs', async () => {
2020-11-24 15:15:51 +05:30
createComponent({ loading: true });
2022-03-02 08:16:31 +05:30
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
2020-05-24 23:13:21 +05:30
wrapper.setData({
allVersions: mockAllVersions,
});
2020-06-23 00:09:42 +05:30
// attempt to query for a version of the design that doesn't exist
router.push({ query: { version: '999' } });
router.push = jest.fn();
2020-05-24 23:13:21 +05:30
wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false });
2022-04-04 11:22:00 +05:30
await nextTick();
2022-11-25 23:54:43 +05:30
expect(createAlert).toHaveBeenCalledTimes(1);
expect(createAlert).toHaveBeenCalledWith({ message: DESIGN_VERSION_NOT_EXIST_ERROR });
2022-04-04 11:22:00 +05:30
expect(router.push).toHaveBeenCalledTimes(1);
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
2020-05-24 23:13:21 +05:30
});
});
});
2020-11-24 15:15:51 +05:30
describe('when hash present in current route', () => {
it('calls updateActiveDiscussion mutation', () => {
createComponent(
{ loading: false },
{
data: {
design,
},
intialRouteOptions: { hash: '#note_123' },
},
);
expect(mutate).toHaveBeenCalledTimes(1);
expect(mutate).toHaveBeenCalledWith({
mutation: updateActiveDiscussion,
variables: { id: 'gid://gitlab/DiffNote/123', source: 'url' },
});
});
});
2021-02-22 17:27:13 +05:30
describe('tracking', () => {
let trackingSpy;
beforeEach(() => {
trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
});
afterEach(() => {
unmockTracking();
});
describe('on mount', () => {
it('tracks design view in snowplow', () => {
createComponent({ loading: true });
expect(trackingSpy).toHaveBeenCalledTimes(1);
expect(trackingSpy).toHaveBeenCalledWith(
DESIGN_TRACKING_PAGE_NAME,
DESIGN_SNOWPLOW_EVENT_TYPES.VIEW_DESIGN,
{
context: {
data: {
'design-collection-owner': 'issue',
'design-is-current-version': true,
'design-version-number': 1,
'internal-object-referrer': 'issue-design-collection',
},
schema: 'iglu:com.gitlab/design_management_context/jsonschema/1-0-0',
},
label: DESIGN_SNOWPLOW_EVENT_TYPES.VIEW_DESIGN,
},
);
});
2021-11-11 11:23:49 +05:30
it('tracks design view service ping', () => {
createComponent({ loading: true });
2021-02-22 17:27:13 +05:30
2021-11-11 11:23:49 +05:30
expect(Api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith(
DESIGN_SERVICE_PING_EVENT_TYPES.DESIGN_ACTION,
);
2021-02-22 17:27:13 +05:30
});
});
});
2020-05-24 23:13:21 +05:30
});