2020-06-01 05:08:28 -04:00
|
|
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
|
|
|
import VueRouter from 'vue-router';
|
2020-05-07 08:09:46 -04:00
|
|
|
import { GlAlert } from '@gitlab/ui';
|
|
|
|
import { ApolloMutation } from 'vue-apollo';
|
2020-08-20 05:09:55 -04:00
|
|
|
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
2020-05-07 08:09:46 -04:00
|
|
|
import DesignIndex from '~/design_management/pages/design/index.vue';
|
2020-06-03 14:08:28 -04:00
|
|
|
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
|
2020-06-17 11:08:36 -04:00
|
|
|
import DesignPresentation from '~/design_management/components/design_presentation.vue';
|
2020-06-18 08:09:25 -04:00
|
|
|
import createImageDiffNoteMutation from '~/design_management/graphql/mutations/create_image_diff_note.mutation.graphql';
|
2020-08-31 23:10:22 -04:00
|
|
|
import updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
|
2020-05-07 08:09:46 -04:00
|
|
|
import {
|
|
|
|
DESIGN_NOT_FOUND_ERROR,
|
|
|
|
DESIGN_VERSION_NOT_EXIST_ERROR,
|
|
|
|
} from '~/design_management/utils/error_messages';
|
2020-08-31 23:10:22 -04:00
|
|
|
import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
|
2020-06-01 05:08:28 -04:00
|
|
|
import createRouter from '~/design_management/router';
|
|
|
|
import * as utils from '~/design_management/utils/design_management_utils';
|
|
|
|
import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants';
|
2020-08-31 23:10:22 -04:00
|
|
|
import design from '../../mock_data/design';
|
|
|
|
import mockResponseWithDesigns from '../../mock_data/designs';
|
|
|
|
import mockResponseNoDesigns from '../../mock_data/no_designs';
|
|
|
|
import mockAllVersions from '../../mock_data/all_versions';
|
2020-05-07 08:09:46 -04:00
|
|
|
|
|
|
|
jest.mock('~/flash');
|
|
|
|
|
2020-06-17 11:08:36 -04:00
|
|
|
const focusInput = jest.fn();
|
2020-10-22 05:08:26 -04:00
|
|
|
const mutate = jest.fn().mockResolvedValue();
|
|
|
|
const mockPageLayoutElement = {
|
|
|
|
classList: {
|
|
|
|
add: jest.fn(),
|
|
|
|
remove: jest.fn(),
|
|
|
|
},
|
|
|
|
};
|
2020-06-17 11:08:36 -04:00
|
|
|
const DesignReplyForm = {
|
|
|
|
template: '<div><textarea ref="textarea"></textarea></div>',
|
|
|
|
methods: {
|
|
|
|
focusInput,
|
|
|
|
},
|
|
|
|
};
|
2020-08-31 23:10:22 -04:00
|
|
|
const mockDesignNoDiscussions = {
|
|
|
|
...design,
|
|
|
|
discussions: {
|
|
|
|
nodes: [],
|
|
|
|
},
|
|
|
|
};
|
2020-10-22 05:08:26 -04:00
|
|
|
const newComment = 'new comment';
|
|
|
|
const annotationCoordinates = {
|
|
|
|
x: 10,
|
|
|
|
y: 10,
|
|
|
|
width: 100,
|
|
|
|
height: 100,
|
|
|
|
};
|
|
|
|
const createDiscussionMutationVariables = {
|
|
|
|
mutation: createImageDiffNoteMutation,
|
|
|
|
update: expect.anything(),
|
|
|
|
variables: {
|
|
|
|
input: {
|
|
|
|
body: newComment,
|
|
|
|
noteableId: design.id,
|
|
|
|
position: {
|
|
|
|
headSha: 'headSha',
|
|
|
|
baseSha: 'baseSha',
|
|
|
|
startSha: 'startSha',
|
|
|
|
paths: {
|
|
|
|
newPath: 'full-design-path',
|
|
|
|
},
|
|
|
|
...annotationCoordinates,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
2020-06-17 11:08:36 -04:00
|
|
|
|
2020-06-01 05:08:28 -04:00
|
|
|
const localVue = createLocalVue();
|
|
|
|
localVue.use(VueRouter);
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
describe('Design management design index page', () => {
|
|
|
|
let wrapper;
|
2020-06-01 05:08:28 -04:00
|
|
|
let router;
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
const findDiscussionForm = () => wrapper.find(DesignReplyForm);
|
2020-06-03 14:08:28 -04:00
|
|
|
const findSidebar = () => wrapper.find(DesignSidebar);
|
2020-06-17 11:08:36 -04:00
|
|
|
const findDesignPresentation = () => wrapper.find(DesignPresentation);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-08-31 23:10:22 -04:00
|
|
|
function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) {
|
2020-05-07 08:09:46 -04:00
|
|
|
const $apollo = {
|
|
|
|
queries: {
|
|
|
|
design: {
|
|
|
|
loading,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mutate,
|
|
|
|
};
|
|
|
|
|
2020-06-01 05:08:28 -04:00
|
|
|
router = createRouter();
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-08-31 23:10:22 -04:00
|
|
|
router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions });
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
wrapper = shallowMount(DesignIndex, {
|
|
|
|
propsData: { id: '1' },
|
2020-06-01 05:08:28 -04:00
|
|
|
mocks: { $apollo },
|
2020-05-07 08:09:46 -04:00
|
|
|
stubs: {
|
|
|
|
ApolloMutation,
|
2020-06-03 14:08:28 -04:00
|
|
|
DesignSidebar,
|
2020-06-17 11:08:36 -04:00
|
|
|
DesignReplyForm,
|
2020-05-15 02:08:40 -04:00
|
|
|
},
|
2020-07-28 08:09:49 -04:00
|
|
|
provide: {
|
|
|
|
issueIid: '1',
|
|
|
|
projectPath: 'project-path',
|
|
|
|
},
|
2020-05-15 02:08:40 -04:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
activeDiscussion: {
|
|
|
|
id: null,
|
|
|
|
source: null,
|
|
|
|
},
|
|
|
|
...data,
|
|
|
|
};
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
2020-06-01 05:08:28 -04:00
|
|
|
localVue,
|
|
|
|
router,
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
wrapper.destroy();
|
|
|
|
});
|
|
|
|
|
2020-10-22 05:08:26 -04:00
|
|
|
describe('when navigating to component', () => {
|
|
|
|
it('applies fullscreen layout class', () => {
|
|
|
|
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
|
|
|
|
createComponent({ loading: true });
|
|
|
|
|
|
|
|
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);
|
|
|
|
createComponent({ loading: false }, { data: { design, scale: 2 } });
|
|
|
|
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
expect(findDesignPresentation().props('scale')).toBe(2);
|
|
|
|
|
|
|
|
DesignIndex.beforeRouteUpdate.call(wrapper.vm, {}, {}, jest.fn());
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
|
|
|
expect(findDesignPresentation().props('scale')).toBe(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when navigating away from component', () => {
|
|
|
|
it('removes fullscreen layout class', async () => {
|
|
|
|
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: true });
|
2020-06-01 05:08:28 -04:00
|
|
|
|
2020-10-22 05:08:26 -04:00
|
|
|
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-01 05:08:28 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
it('sets loading state', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: true });
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-05-15 02:08:40 -04:00
|
|
|
expect(wrapper.element).toMatchSnapshot();
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders design index', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: false }, { data: { design } });
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-05-15 02:08:40 -04:00
|
|
|
expect(wrapper.element).toMatchSnapshot();
|
|
|
|
expect(wrapper.find(GlAlert).exists()).toBe(false);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
2020-06-03 14:08:28 -04:00
|
|
|
it('passes correct props to sidebar component', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: false }, { data: { design } });
|
2020-05-15 02:08:40 -04:00
|
|
|
|
2020-06-03 14:08:28 -04:00
|
|
|
expect(findSidebar().props()).toEqual({
|
|
|
|
design,
|
2020-07-28 08:09:49 -04:00
|
|
|
markdownPreviewPath: '/project-path/preview_markdown?target_type=Issue',
|
2020-06-03 14:08:28 -04:00
|
|
|
resolvedDiscussionsExpanded: false,
|
2020-05-15 02:08:40 -04:00
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('opens a new discussion form', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent(
|
|
|
|
{ loading: false },
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
design,
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
},
|
2020-08-31 23:10:22 -04:00
|
|
|
);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-06-17 11:08:36 -04:00
|
|
|
findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 });
|
2020-05-07 08:09:46 -04:00
|
|
|
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(findDiscussionForm().exists()).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-06-17 11:08:36 -04:00
|
|
|
it('keeps new discussion form focused', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent(
|
|
|
|
{ loading: false },
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
design,
|
|
|
|
annotationCoordinates,
|
2020-06-17 11:08:36 -04:00
|
|
|
},
|
|
|
|
},
|
2020-08-31 23:10:22 -04:00
|
|
|
);
|
2020-06-17 11:08:36 -04:00
|
|
|
|
|
|
|
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
|
|
|
|
|
|
|
|
expect(focusInput).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
it('sends a mutation on submitting form and closes form', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent(
|
|
|
|
{ loading: false },
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
design,
|
|
|
|
annotationCoordinates,
|
|
|
|
comment: newComment,
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
},
|
2020-08-31 23:10:22 -04:00
|
|
|
);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-09-09 17:08:33 -04:00
|
|
|
findDiscussionForm().vm.$emit('submit-form');
|
2020-05-15 02:08:40 -04:00
|
|
|
expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables);
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
return wrapper.vm
|
|
|
|
.$nextTick()
|
|
|
|
.then(() => {
|
2020-05-15 02:08:40 -04:00
|
|
|
return mutate({ variables: createDiscussionMutationVariables });
|
2020-05-07 08:09:46 -04:00
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(findDiscussionForm().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('closes the form and clears the comment on canceling form', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent(
|
|
|
|
{ loading: false },
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
design,
|
|
|
|
annotationCoordinates,
|
|
|
|
comment: newComment,
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
},
|
2020-08-31 23:10:22 -04:00
|
|
|
);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-09-09 17:08:33 -04:00
|
|
|
findDiscussionForm().vm.$emit('cancel-form');
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-05-15 02:08:40 -04:00
|
|
|
expect(wrapper.vm.comment).toBe('');
|
|
|
|
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(findDiscussionForm().exists()).toBe(false);
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('with error', () => {
|
|
|
|
beforeEach(() => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent(
|
|
|
|
{ loading: false },
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
design: mockDesignNoDiscussions,
|
|
|
|
errorMessage: 'woops',
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
},
|
2020-08-31 23:10:22 -04:00
|
|
|
);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('GlAlert is rendered in correct position with correct content', () => {
|
|
|
|
expect(wrapper.element).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('onDesignQueryResult', () => {
|
|
|
|
describe('with no designs', () => {
|
|
|
|
it('redirects to /designs', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: true });
|
2020-06-01 05:08:28 -04:00
|
|
|
router.push = jest.fn();
|
2020-05-07 08:09:46 -04:00
|
|
|
|
|
|
|
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
|
|
|
expect(createFlash).toHaveBeenCalledWith(DESIGN_NOT_FOUND_ERROR);
|
2020-06-01 05:08:28 -04:00
|
|
|
expect(router.push).toHaveBeenCalledTimes(1);
|
|
|
|
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when no design exists for given version', () => {
|
|
|
|
it('redirects to /designs', () => {
|
2020-08-31 23:10:22 -04:00
|
|
|
createComponent({ loading: true });
|
2020-05-07 08:09:46 -04:00
|
|
|
wrapper.setData({
|
|
|
|
allVersions: mockAllVersions,
|
|
|
|
});
|
|
|
|
|
2020-06-01 05:08:28 -04:00
|
|
|
// attempt to query for a version of the design that doesn't exist
|
|
|
|
router.push({ query: { version: '999' } });
|
|
|
|
router.push = jest.fn();
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false });
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
|
|
|
expect(createFlash).toHaveBeenCalledWith(DESIGN_VERSION_NOT_EXIST_ERROR);
|
2020-06-01 05:08:28 -04:00
|
|
|
expect(router.push).toHaveBeenCalledTimes(1);
|
|
|
|
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-08-31 23:10:22 -04:00
|
|
|
|
|
|
|
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' },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|