302 lines
8.3 KiB
JavaScript
302 lines
8.3 KiB
JavaScript
|
import { shallowMount } from '@vue/test-utils';
|
||
|
import { GlAlert } from '@gitlab/ui';
|
||
|
import { ApolloMutation } from 'vue-apollo';
|
||
|
import createFlash from '~/flash';
|
||
|
import DesignIndex from '~/design_management/pages/design/index.vue';
|
||
|
import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue';
|
||
|
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
|
||
|
import Participants from '~/sidebar/components/participants/participants.vue';
|
||
|
import createImageDiffNoteMutation from '~/design_management/graphql/mutations/createImageDiffNote.mutation.graphql';
|
||
|
import updateActiveDiscussionMutation from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
|
||
|
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';
|
||
|
import {
|
||
|
DESIGN_NOT_FOUND_ERROR,
|
||
|
DESIGN_VERSION_NOT_EXIST_ERROR,
|
||
|
} from '~/design_management/utils/error_messages';
|
||
|
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
|
||
|
|
||
|
jest.mock('~/flash');
|
||
|
jest.mock('mousetrap', () => ({
|
||
|
bind: jest.fn(),
|
||
|
unbind: jest.fn(),
|
||
|
}));
|
||
|
|
||
|
describe('Design management design index page', () => {
|
||
|
let wrapper;
|
||
|
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,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
|
||
|
const updateActiveDiscussionMutationVariables = {
|
||
|
mutation: updateActiveDiscussionMutation,
|
||
|
variables: {
|
||
|
id: design.discussions.nodes[0].notes.nodes[0].id,
|
||
|
source: 'discussion',
|
||
|
},
|
||
|
};
|
||
|
|
||
|
const mutate = jest.fn().mockResolvedValue();
|
||
|
const routerPush = jest.fn();
|
||
|
|
||
|
const findDiscussions = () => wrapper.findAll(DesignDiscussion);
|
||
|
const findDiscussionForm = () => wrapper.find(DesignReplyForm);
|
||
|
const findParticipants = () => wrapper.find(Participants);
|
||
|
const findDiscussionsWrapper = () => wrapper.find('.image-notes');
|
||
|
|
||
|
function createComponent(loading = false, data = {}, { routeQuery = {} } = {}) {
|
||
|
const $apollo = {
|
||
|
queries: {
|
||
|
design: {
|
||
|
loading,
|
||
|
},
|
||
|
},
|
||
|
mutate,
|
||
|
};
|
||
|
|
||
|
const $router = {
|
||
|
push: routerPush,
|
||
|
};
|
||
|
|
||
|
const $route = {
|
||
|
query: routeQuery,
|
||
|
};
|
||
|
|
||
|
wrapper = shallowMount(DesignIndex, {
|
||
|
propsData: { id: '1' },
|
||
|
mocks: { $apollo, $router, $route },
|
||
|
stubs: {
|
||
|
ApolloMutation,
|
||
|
DesignDiscussion,
|
||
|
},
|
||
|
data() {
|
||
|
return {
|
||
|
issueIid: '1',
|
||
|
activeDiscussion: {
|
||
|
id: null,
|
||
|
source: null,
|
||
|
},
|
||
|
...data,
|
||
|
};
|
||
|
},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
afterEach(() => {
|
||
|
wrapper.destroy();
|
||
|
});
|
||
|
|
||
|
it('sets loading state', () => {
|
||
|
createComponent(true);
|
||
|
|
||
|
expect(wrapper.element).toMatchSnapshot();
|
||
|
});
|
||
|
|
||
|
it('renders design index', () => {
|
||
|
createComponent(false, { design });
|
||
|
|
||
|
expect(wrapper.element).toMatchSnapshot();
|
||
|
expect(wrapper.find(GlAlert).exists()).toBe(false);
|
||
|
});
|
||
|
|
||
|
it('renders participants', () => {
|
||
|
createComponent(false, { design });
|
||
|
|
||
|
expect(findParticipants().exists()).toBe(true);
|
||
|
});
|
||
|
|
||
|
it('passes the correct amount of participants to the Participants component', () => {
|
||
|
createComponent(false, { design });
|
||
|
|
||
|
expect(findParticipants().props('participants')).toHaveLength(1);
|
||
|
});
|
||
|
|
||
|
describe('when has no discussions', () => {
|
||
|
beforeEach(() => {
|
||
|
createComponent(false, {
|
||
|
design: {
|
||
|
...design,
|
||
|
discussions: {
|
||
|
nodes: [],
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('does not render discussions', () => {
|
||
|
expect(findDiscussions().exists()).toBe(false);
|
||
|
});
|
||
|
|
||
|
it('renders a message about possibility to create a new discussion', () => {
|
||
|
expect(wrapper.find('.new-discussion-disclaimer').exists()).toBe(true);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('when has discussions', () => {
|
||
|
beforeEach(() => {
|
||
|
createComponent(false, { design });
|
||
|
});
|
||
|
|
||
|
it('renders correct amount of discussions', () => {
|
||
|
expect(findDiscussions()).toHaveLength(1);
|
||
|
});
|
||
|
|
||
|
it('sends a mutation to set an active discussion when clicking on a discussion', () => {
|
||
|
findDiscussions()
|
||
|
.at(0)
|
||
|
.trigger('click');
|
||
|
|
||
|
expect(mutate).toHaveBeenCalledWith(updateActiveDiscussionMutationVariables);
|
||
|
});
|
||
|
|
||
|
it('sends a mutation to reset an active discussion when clicking outside of discussion', () => {
|
||
|
findDiscussionsWrapper().trigger('click');
|
||
|
|
||
|
expect(mutate).toHaveBeenCalledWith({
|
||
|
...updateActiveDiscussionMutationVariables,
|
||
|
variables: { id: undefined, source: 'discussion' },
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('opens a new discussion form', () => {
|
||
|
createComponent(false, {
|
||
|
design: {
|
||
|
...design,
|
||
|
discussions: {
|
||
|
nodes: [],
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
wrapper.vm.openCommentForm({ x: 0, y: 0 });
|
||
|
|
||
|
return wrapper.vm.$nextTick().then(() => {
|
||
|
expect(findDiscussionForm().exists()).toBe(true);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('sends a mutation on submitting form and closes form', () => {
|
||
|
createComponent(false, {
|
||
|
design: {
|
||
|
...design,
|
||
|
discussions: {
|
||
|
nodes: [],
|
||
|
},
|
||
|
},
|
||
|
annotationCoordinates,
|
||
|
comment: newComment,
|
||
|
});
|
||
|
|
||
|
findDiscussionForm().vm.$emit('submitForm');
|
||
|
expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables);
|
||
|
|
||
|
return wrapper.vm
|
||
|
.$nextTick()
|
||
|
.then(() => {
|
||
|
return mutate({ variables: createDiscussionMutationVariables });
|
||
|
})
|
||
|
.then(() => {
|
||
|
expect(findDiscussionForm().exists()).toBe(false);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('closes the form and clears the comment on canceling form', () => {
|
||
|
createComponent(false, {
|
||
|
design: {
|
||
|
...design,
|
||
|
discussions: {
|
||
|
nodes: [],
|
||
|
},
|
||
|
},
|
||
|
annotationCoordinates,
|
||
|
comment: newComment,
|
||
|
});
|
||
|
|
||
|
findDiscussionForm().vm.$emit('cancelForm');
|
||
|
|
||
|
expect(wrapper.vm.comment).toBe('');
|
||
|
|
||
|
return wrapper.vm.$nextTick().then(() => {
|
||
|
expect(findDiscussionForm().exists()).toBe(false);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('with error', () => {
|
||
|
beforeEach(() => {
|
||
|
createComponent(false, {
|
||
|
design: {
|
||
|
...design,
|
||
|
discussions: {
|
||
|
nodes: [],
|
||
|
},
|
||
|
},
|
||
|
errorMessage: 'woops',
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('GlAlert is rendered in correct position with correct content', () => {
|
||
|
expect(wrapper.element).toMatchSnapshot();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('onDesignQueryResult', () => {
|
||
|
describe('with no designs', () => {
|
||
|
it('redirects to /designs', () => {
|
||
|
createComponent(true);
|
||
|
|
||
|
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
|
||
|
return wrapper.vm.$nextTick().then(() => {
|
||
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
||
|
expect(createFlash).toHaveBeenCalledWith(DESIGN_NOT_FOUND_ERROR);
|
||
|
expect(routerPush).toHaveBeenCalledTimes(1);
|
||
|
expect(routerPush).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('when no design exists for given version', () => {
|
||
|
it('redirects to /designs', () => {
|
||
|
// attempt to query for a version of the design that doesn't exist
|
||
|
createComponent(true, {}, { routeQuery: { version: '999' } });
|
||
|
wrapper.setData({
|
||
|
allVersions: mockAllVersions,
|
||
|
});
|
||
|
|
||
|
wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false });
|
||
|
return wrapper.vm.$nextTick().then(() => {
|
||
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
||
|
expect(createFlash).toHaveBeenCalledWith(DESIGN_VERSION_NOT_EXIST_ERROR);
|
||
|
expect(routerPush).toHaveBeenCalledTimes(1);
|
||
|
expect(routerPush).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|