summaryrefslogtreecommitdiff
path: root/spec/frontend/design_management
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 01:45:44 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 01:45:44 +0000
commit85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch)
tree9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/frontend/design_management
parent15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff)
downloadgitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/frontend/design_management')
-rw-r--r--spec/frontend/design_management/components/delete_button_spec.js19
-rw-r--r--spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap21
-rw-r--r--spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap14
-rw-r--r--spec/frontend/design_management/components/design_notes/design_discussion_spec.js80
-rw-r--r--spec/frontend/design_management/components/design_notes/design_note_spec.js22
-rw-r--r--spec/frontend/design_management/components/design_notes/design_reply_form_spec.js20
-rw-r--r--spec/frontend/design_management/components/design_overlay_spec.js57
-rw-r--r--spec/frontend/design_management/components/design_presentation_spec.js2
-rw-r--r--spec/frontend/design_management/components/design_sidebar_spec.js41
-rw-r--r--spec/frontend/design_management/components/design_todo_button_spec.js158
-rw-r--r--spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap40
-rw-r--r--spec/frontend/design_management/components/list/item_spec.js3
-rw-r--r--spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap24
-rw-r--r--spec/frontend/design_management/components/upload/design_version_dropdown_spec.js14
-rw-r--r--spec/frontend/design_management/mock_data/apollo_mock.js18
-rw-r--r--spec/frontend/design_management/mock_data/design.js3
-rw-r--r--spec/frontend/design_management/mock_data/discussion.js45
-rw-r--r--spec/frontend/design_management/mock_data/notes.js74
-rw-r--r--spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap2
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap14
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js134
-rw-r--r--spec/frontend/design_management/pages/index_apollo_spec.js162
-rw-r--r--spec/frontend/design_management/pages/index_spec.js140
-rw-r--r--spec/frontend/design_management/router_spec.js5
-rw-r--r--spec/frontend/design_management/utils/cache_update_spec.js13
-rw-r--r--spec/frontend/design_management/utils/design_management_utils_spec.js17
26 files changed, 713 insertions, 429 deletions
diff --git a/spec/frontend/design_management/components/delete_button_spec.js b/spec/frontend/design_management/components/delete_button_spec.js
index cd4ef1f0ccd..961f5bdd2ae 100644
--- a/spec/frontend/design_management/components/delete_button_spec.js
+++ b/spec/frontend/design_management/components/delete_button_spec.js
@@ -8,7 +8,7 @@ describe('Batch delete button component', () => {
const findButton = () => wrapper.find(GlButton);
const findModal = () => wrapper.find(GlModal);
- function createComponent(isDeleting = false) {
+ function createComponent({ isDeleting = false } = {}, { slots = {} } = {}) {
wrapper = shallowMount(BatchDeleteButton, {
propsData: {
isDeleting,
@@ -16,6 +16,7 @@ describe('Batch delete button component', () => {
directives: {
GlModalDirective,
},
+ slots,
});
}
@@ -31,7 +32,7 @@ describe('Batch delete button component', () => {
});
it('renders disabled button when design is deleting', () => {
- createComponent(true);
+ createComponent({ isDeleting: true });
expect(findButton().attributes('disabled')).toBeTruthy();
});
@@ -48,4 +49,18 @@ describe('Batch delete button component', () => {
expect(wrapper.emitted().deleteSelectedDesigns).toBeTruthy();
});
});
+
+ it('renders slot content', () => {
+ const testText = 'Archive selected';
+ createComponent(
+ {},
+ {
+ slots: {
+ default: testText,
+ },
+ },
+ );
+
+ expect(findButton().text()).toBe(testText);
+ });
});
diff --git a/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap b/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap
index b55bacb6fc5..084a7e5d712 100644
--- a/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap
+++ b/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap
@@ -17,15 +17,15 @@ exports[`Design note component should match the snapshot 1`] = `
/>
<div
- class="d-flex justify-content-between"
+ class="gl-display-flex gl-justify-content-space-between"
>
<div>
- <a
+ <gl-link-stub
class="js-user-link"
data-user-id="author-id"
>
<span
- class="note-header-author-name bold"
+ class="note-header-author-name gl-font-weight-bold"
>
</span>
@@ -37,7 +37,7 @@ exports[`Design note component should match the snapshot 1`] = `
>
@
</span>
- </a>
+ </gl-link-stub>
<span
class="note-headline-light note-headline-meta"
@@ -46,12 +46,21 @@ exports[`Design note component should match the snapshot 1`] = `
class="system-note-message"
/>
- <!---->
+ <gl-link-stub
+ class="note-timestamp system-note-separator gl-display-block gl-mb-2"
+ href="#note_123"
+ >
+ <time-ago-tooltip-stub
+ cssclass=""
+ time="2019-07-26T15:02:20Z"
+ tooltipplacement="bottom"
+ />
+ </gl-link-stub>
</span>
</div>
<div
- class="gl-display-flex"
+ class="gl-display-flex gl-align-items-baseline"
>
<!---->
diff --git a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
index e01c79e3520..f8c68ca4c83 100644
--- a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
+++ b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
@@ -1,15 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design reply form component renders button text as "Comment" when creating a comment 1`] = `
-"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled\\">
+"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
<!---->
- Comment
-</button>"
+ <!----> <span class=\\"gl-button-text\\">
+ Comment
+ </span></button>"
`;
exports[`Design reply form component renders button text as "Save comment" when creating a comment 1`] = `
-"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled\\">
+"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
<!---->
- Save comment
-</button>"
+ <!----> <span class=\\"gl-button-text\\">
+ Save comment
+ </span></button>"
`;
diff --git a/spec/frontend/design_management/components/design_notes/design_discussion_spec.js b/spec/frontend/design_management/components/design_notes/design_discussion_spec.js
index 176c10ea584..9fbd9b2c2a3 100644
--- a/spec/frontend/design_management/components/design_notes/design_discussion_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_discussion_spec.js
@@ -8,8 +8,9 @@ import createNoteMutation from '~/design_management/graphql/mutations/create_not
import toggleResolveDiscussionMutation from '~/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
import ToggleRepliesWidget from '~/design_management/components/design_notes/toggle_replies_widget.vue';
+import mockDiscussion from '../../mock_data/discussion';
-const discussion = {
+const defaultMockDiscussion = {
id: '0',
resolved: false,
resolvable: true,
@@ -31,7 +32,6 @@ describe('Design discussions component', () => {
const mutationVariables = {
mutation: createNoteMutation,
- update: expect.anything(),
variables: {
input: {
noteableId: 'noteable-id',
@@ -40,7 +40,7 @@ describe('Design discussions component', () => {
},
},
};
- const mutate = jest.fn(() => Promise.resolve());
+ const mutate = jest.fn().mockResolvedValue({ data: { createNote: { errors: [] } } });
const $apollo = {
mutate,
};
@@ -49,7 +49,7 @@ describe('Design discussions component', () => {
wrapper = mount(DesignDiscussion, {
propsData: {
resolvedDiscussionsExpanded: true,
- discussion,
+ discussion: defaultMockDiscussion,
noteableId: 'noteable-id',
designId: 'design-id',
discussionIndex: 1,
@@ -82,7 +82,7 @@ describe('Design discussions component', () => {
beforeEach(() => {
createComponent({
discussion: {
- ...discussion,
+ ...defaultMockDiscussion,
resolvable: false,
},
});
@@ -93,7 +93,7 @@ describe('Design discussions component', () => {
});
it('does not render a checkbox in reply form', () => {
- findReplyPlaceholder().vm.$emit('onMouseDown');
+ findReplyPlaceholder().vm.$emit('onClick');
return wrapper.vm.$nextTick().then(() => {
expect(findResolveCheckbox().exists()).toBe(false);
@@ -125,7 +125,7 @@ describe('Design discussions component', () => {
it('renders a checkbox with Resolve thread text in reply form', () => {
findReplyPlaceholder().vm.$emit('onClick');
- wrapper.setProps({ discussionWithOpenForm: discussion.id });
+ wrapper.setProps({ discussionWithOpenForm: defaultMockDiscussion.id });
return wrapper.vm.$nextTick().then(() => {
expect(findResolveCheckbox().text()).toBe('Resolve thread');
@@ -141,7 +141,7 @@ describe('Design discussions component', () => {
beforeEach(() => {
createComponent({
discussion: {
- ...discussion,
+ ...defaultMockDiscussion,
resolved: true,
resolvedBy: notes[0].author,
resolvedAt: '2020-05-08T07:10:45Z',
@@ -206,7 +206,7 @@ describe('Design discussions component', () => {
it('renders a checkbox with Unresolve thread text in reply form', () => {
findReplyPlaceholder().vm.$emit('onClick');
- wrapper.setProps({ discussionWithOpenForm: discussion.id });
+ wrapper.setProps({ discussionWithOpenForm: defaultMockDiscussion.id });
return wrapper.vm.$nextTick().then(() => {
expect(findResolveCheckbox().text()).toBe('Unresolve thread');
@@ -218,7 +218,7 @@ describe('Design discussions component', () => {
it('hides reply placeholder and opens form on placeholder click', () => {
createComponent();
findReplyPlaceholder().vm.$emit('onClick');
- wrapper.setProps({ discussionWithOpenForm: discussion.id });
+ wrapper.setProps({ discussionWithOpenForm: defaultMockDiscussion.id });
return wrapper.vm.$nextTick().then(() => {
expect(findReplyPlaceholder().exists()).toBe(false);
@@ -226,34 +226,31 @@ describe('Design discussions component', () => {
});
});
- it('calls mutation on submitting form and closes the form', () => {
+ it('calls mutation on submitting form and closes the form', async () => {
createComponent(
- { discussionWithOpenForm: discussion.id },
+ { discussionWithOpenForm: defaultMockDiscussion.id },
{ discussionComment: 'test', isFormRendered: true },
);
- findReplyForm().vm.$emit('submitForm');
+ findReplyForm().vm.$emit('submit-form');
expect(mutate).toHaveBeenCalledWith(mutationVariables);
- return mutate()
- .then(() => {
- return wrapper.vm.$nextTick();
- })
- .then(() => {
- expect(findReplyForm().exists()).toBe(false);
- });
+ await mutate();
+ await wrapper.vm.$nextTick();
+
+ expect(findReplyForm().exists()).toBe(false);
});
it('clears the discussion comment on closing comment form', () => {
createComponent(
- { discussionWithOpenForm: discussion.id },
+ { discussionWithOpenForm: defaultMockDiscussion.id },
{ discussionComment: 'test', isFormRendered: true },
);
return wrapper.vm
.$nextTick()
.then(() => {
- findReplyForm().vm.$emit('cancelForm');
+ findReplyForm().vm.$emit('cancel-form');
expect(wrapper.vm.discussionComment).toBe('');
return wrapper.vm.$nextTick();
@@ -263,19 +260,26 @@ describe('Design discussions component', () => {
});
});
- it('applies correct class to design notes when discussion is highlighted', () => {
- createComponent(
- {},
- {
- activeDiscussion: {
- id: notes[0].id,
- source: 'pin',
- },
- },
- );
+ describe('when any note from a discussion is active', () => {
+ it.each([notes[0], notes[0].discussion.notes.nodes[1]])(
+ 'applies correct class to all notes in the active discussion',
+ note => {
+ createComponent(
+ { discussion: mockDiscussion },
+ {
+ activeDiscussion: {
+ id: note.id,
+ source: 'pin',
+ },
+ },
+ );
- expect(wrapper.findAll(DesignNote).wrappers.every(note => note.classes('gl-bg-blue-50'))).toBe(
- true,
+ expect(
+ wrapper
+ .findAll(DesignNote)
+ .wrappers.every(designNote => designNote.classes('gl-bg-blue-50')),
+ ).toBe(true);
+ },
);
});
@@ -285,7 +289,7 @@ describe('Design discussions component', () => {
expect(mutate).toHaveBeenCalledWith({
mutation: toggleResolveDiscussionMutation,
variables: {
- id: discussion.id,
+ id: defaultMockDiscussion.id,
resolve: true,
},
});
@@ -296,7 +300,7 @@ describe('Design discussions component', () => {
it('calls toggleResolveDiscussion mutation after adding a note if checkbox was checked', () => {
createComponent(
- { discussionWithOpenForm: discussion.id },
+ { discussionWithOpenForm: defaultMockDiscussion.id },
{ discussionComment: 'test', isFormRendered: true },
);
findResolveButton().trigger('click');
@@ -306,7 +310,7 @@ describe('Design discussions component', () => {
expect(mutate).toHaveBeenCalledWith({
mutation: toggleResolveDiscussionMutation,
variables: {
- id: discussion.id,
+ id: defaultMockDiscussion.id,
resolve: true,
},
});
@@ -317,6 +321,6 @@ describe('Design discussions component', () => {
createComponent();
findReplyPlaceholder().vm.$emit('onClick');
- expect(wrapper.emitted('openForm')).toBeTruthy();
+ expect(wrapper.emitted('open-form')).toBeTruthy();
});
});
diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js
index 8b32d3022ee..043091e3dc2 100644
--- a/spec/frontend/design_management/components/design_notes/design_note_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js
@@ -15,6 +15,7 @@ const note = {
userPermissions: {
adminNote: false,
},
+ createdAt: '2019-07-26T15:02:20Z',
};
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
@@ -79,21 +80,10 @@ describe('Design note component', () => {
it('should render a time ago tooltip if note has createdAt property', () => {
createComponent({
- note: {
- ...note,
- createdAt: '2019-07-26T15:02:20Z',
- },
- });
-
- expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true);
- });
-
- it('should trigger a scrollIntoView method', () => {
- createComponent({
note,
});
- expect(scrollIntoViewMock).toHaveBeenCalled();
+ expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true);
});
it('should not render edit icon when user does not have a permission', () => {
@@ -143,8 +133,8 @@ describe('Design note component', () => {
expect(findReplyForm().exists()).toBe(true);
});
- it('hides the form on hideForm event', () => {
- findReplyForm().vm.$emit('cancelForm');
+ it('hides the form on cancel-form event', () => {
+ findReplyForm().vm.$emit('cancel-form');
return wrapper.vm.$nextTick().then(() => {
expect(findReplyForm().exists()).toBe(false);
@@ -152,8 +142,8 @@ describe('Design note component', () => {
});
});
- it('calls a mutation on submitForm event and hides a form', () => {
- findReplyForm().vm.$emit('submitForm');
+ it('calls a mutation on submit-form event and hides a form', () => {
+ findReplyForm().vm.$emit('submit-form');
expect(mutate).toHaveBeenCalled();
return mutate()
diff --git a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
index 16b34f150b8..1a80fc4e761 100644
--- a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
@@ -70,7 +70,7 @@ describe('Design reply form component', () => {
});
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('submitForm')).toBeFalsy();
+ expect(wrapper.emitted('submit-form')).toBeFalsy();
});
});
@@ -80,20 +80,20 @@ describe('Design reply form component', () => {
});
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('submitForm')).toBeFalsy();
+ expect(wrapper.emitted('submit-form')).toBeFalsy();
});
});
it('emits cancelForm event on pressing escape button on textarea', () => {
findTextarea().trigger('keyup.esc');
- expect(wrapper.emitted('cancelForm')).toBeTruthy();
+ expect(wrapper.emitted('cancel-form')).toBeTruthy();
});
it('emits cancelForm event on clicking Cancel button', () => {
findCancelButton().vm.$emit('click');
- expect(wrapper.emitted('cancelForm')).toHaveLength(1);
+ expect(wrapper.emitted('cancel-form')).toHaveLength(1);
});
});
@@ -112,7 +112,7 @@ describe('Design reply form component', () => {
findSubmitButton().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('submitForm')).toBeTruthy();
+ expect(wrapper.emitted('submit-form')).toBeTruthy();
});
});
@@ -122,7 +122,7 @@ describe('Design reply form component', () => {
});
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('submitForm')).toBeTruthy();
+ expect(wrapper.emitted('submit-form')).toBeTruthy();
});
});
@@ -132,7 +132,7 @@ describe('Design reply form component', () => {
});
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('submitForm')).toBeTruthy();
+ expect(wrapper.emitted('submit-form')).toBeTruthy();
});
});
@@ -147,7 +147,7 @@ describe('Design reply form component', () => {
it('emits cancelForm event on Escape key if text was not changed', () => {
findTextarea().trigger('keyup.esc');
- expect(wrapper.emitted('cancelForm')).toBeTruthy();
+ expect(wrapper.emitted('cancel-form')).toBeTruthy();
});
it('opens confirmation modal on Escape key when text has changed', () => {
@@ -162,7 +162,7 @@ describe('Design reply form component', () => {
it('emits cancelForm event on Cancel button click if text was not changed', () => {
findCancelButton().trigger('click');
- expect(wrapper.emitted('cancelForm')).toBeTruthy();
+ expect(wrapper.emitted('cancel-form')).toBeTruthy();
});
it('opens confirmation modal on Cancel button click when text has changed', () => {
@@ -178,7 +178,7 @@ describe('Design reply form component', () => {
findTextarea().trigger('keyup.esc');
findModal().vm.$emit('ok');
- expect(wrapper.emitted('cancelForm')).toBeTruthy();
+ expect(wrapper.emitted('cancel-form')).toBeTruthy();
});
});
});
diff --git a/spec/frontend/design_management/components/design_overlay_spec.js b/spec/frontend/design_management/components/design_overlay_spec.js
index f243323b162..673a09320e5 100644
--- a/spec/frontend/design_management/components/design_overlay_spec.js
+++ b/spec/frontend/design_management/components/design_overlay_spec.js
@@ -11,11 +11,11 @@ describe('Design overlay component', () => {
const mockDimensions = { width: 100, height: 100 };
- const findOverlay = () => wrapper.find('.image-diff-overlay');
const findAllNotes = () => wrapper.findAll('.js-image-badge');
const findCommentBadge = () => wrapper.find('.comment-indicator');
- const findFirstBadge = () => findAllNotes().at(0);
- const findSecondBadge = () => findAllNotes().at(1);
+ const findBadgeAtIndex = noteIndex => findAllNotes().at(noteIndex);
+ const findFirstBadge = () => findBadgeAtIndex(0);
+ const findSecondBadge = () => findBadgeAtIndex(1);
const clickAndDragBadge = (elem, fromPoint, toPoint) => {
elem.trigger('mousedown', { clientX: fromPoint.x, clientY: fromPoint.y });
@@ -56,9 +56,7 @@ describe('Design overlay component', () => {
it('should have correct inline style', () => {
createComponent();
- expect(wrapper.find('.image-diff-overlay').attributes().style).toBe(
- 'width: 100px; height: 100px; top: 0px; left: 0px;',
- );
+ expect(wrapper.attributes().style).toBe('width: 100px; height: 100px; top: 0px; left: 0px;');
});
it('should emit `openCommentForm` when clicking on overlay', () => {
@@ -69,7 +67,7 @@ describe('Design overlay component', () => {
};
wrapper
- .find('.image-diff-overlay-add-comment')
+ .find('[data-qa-selector="design_image_button"]')
.trigger('mouseup', { offsetX: newCoordinates.x, offsetY: newCoordinates.y });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted('openCommentForm')).toEqual([
@@ -107,16 +105,43 @@ describe('Design overlay component', () => {
expect(findSecondBadge().classes()).toContain('resolved');
});
- it('when there is an active discussion, should apply inactive class to all pins besides the active one', () => {
- wrapper.setData({
- activeDiscussion: {
- id: notes[0].id,
- source: 'discussion',
- },
+ describe('when no discussion is active', () => {
+ it('should not apply inactive class to any pins', () => {
+ expect(
+ findAllNotes(0).wrappers.every(designNote => designNote.classes('gl-bg-blue-50')),
+ ).toBe(false);
});
+ });
+
+ describe('when a discussion is active', () => {
+ it.each([notes[0].discussion.notes.nodes[1], notes[0].discussion.notes.nodes[0]])(
+ 'should not apply inactive class to the pin for the active discussion',
+ note => {
+ wrapper.setData({
+ activeDiscussion: {
+ id: note.id,
+ source: 'discussion',
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findBadgeAtIndex(0).classes()).not.toContain('inactive');
+ });
+ },
+ );
+
+ it('should apply inactive class to all pins besides the active one', () => {
+ wrapper.setData({
+ activeDiscussion: {
+ id: notes[0].id,
+ source: 'discussion',
+ },
+ });
- return wrapper.vm.$nextTick().then(() => {
- expect(findSecondBadge().classes()).toContain('inactive');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findSecondBadge().classes()).toContain('inactive');
+ expect(findFirstBadge().classes()).not.toContain('inactive');
+ });
});
});
});
@@ -309,7 +334,7 @@ describe('Design overlay component', () => {
it.each`
element | getElementFunc | event
- ${'overlay'} | ${findOverlay} | ${'mouseleave'}
+ ${'overlay'} | ${() => wrapper} | ${'mouseleave'}
${'comment badge'} | ${findCommentBadge} | ${'mouseup'}
`(
'should emit `openCommentForm` event when $event fired on $element element',
diff --git a/spec/frontend/design_management/components/design_presentation_spec.js b/spec/frontend/design_management/components/design_presentation_spec.js
index 7e513182589..d633d00f2ed 100644
--- a/spec/frontend/design_management/components/design_presentation_spec.js
+++ b/spec/frontend/design_management/components/design_presentation_spec.js
@@ -42,7 +42,7 @@ describe('Design management design presentation component', () => {
wrapper.element.scrollTo = jest.fn();
}
- const findOverlayCommentButton = () => wrapper.find('.image-diff-overlay-add-comment');
+ const findOverlayCommentButton = () => wrapper.find('[data-qa-selector="design_image_button"]');
/**
* Spy on $refs and mock given values
diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js
index e098e7de867..700faa8a70f 100644
--- a/spec/frontend/design_management/components/design_sidebar_spec.js
+++ b/spec/frontend/design_management/components/design_sidebar_spec.js
@@ -6,6 +6,10 @@ import Participants from '~/sidebar/components/participants/participants.vue';
import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue';
import design from '../mock_data/design';
import updateActiveDiscussionMutation from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
+import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
+
+const scrollIntoViewMock = jest.fn();
+HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
const updateActiveDiscussionMutationVariables = {
mutation: updateActiveDiscussionMutation,
@@ -39,7 +43,7 @@ describe('Design management design sidebar component', () => {
const findNewDiscussionDisclaimer = () =>
wrapper.find('[data-testid="new-discussion-disclaimer"]');
- function createComponent(props = {}) {
+ function createComponent(props = {}, { enableTodoButton } = {}) {
wrapper = shallowMount(DesignSidebar, {
propsData: {
design,
@@ -53,6 +57,10 @@ describe('Design management design sidebar component', () => {
mutate,
},
},
+ stubs: { GlPopover },
+ provide: {
+ glFeatures: { designManagementTodoButton: enableTodoButton },
+ },
});
}
@@ -146,22 +154,22 @@ describe('Design management design sidebar component', () => {
});
it('emits correct event on discussion create note error', () => {
- findFirstDiscussion().vm.$emit('createNoteError', 'payload');
+ findFirstDiscussion().vm.$emit('create-note-error', 'payload');
expect(wrapper.emitted('onDesignDiscussionError')).toEqual([['payload']]);
});
it('emits correct event on discussion update note error', () => {
- findFirstDiscussion().vm.$emit('updateNoteError', 'payload');
+ findFirstDiscussion().vm.$emit('update-note-error', 'payload');
expect(wrapper.emitted('updateNoteError')).toEqual([['payload']]);
});
it('emits correct event on discussion resolve error', () => {
- findFirstDiscussion().vm.$emit('resolveDiscussionError', 'payload');
+ findFirstDiscussion().vm.$emit('resolve-discussion-error', 'payload');
expect(wrapper.emitted('resolveDiscussionError')).toEqual([['payload']]);
});
it('changes prop correctly on opening discussion form', () => {
- findFirstDiscussion().vm.$emit('openForm', 'some-id');
+ findFirstDiscussion().vm.$emit('open-form', 'some-id');
return wrapper.vm.$nextTick().then(() => {
expect(findFirstDiscussion().props('discussionWithOpenForm')).toBe('some-id');
@@ -220,6 +228,10 @@ describe('Design management design sidebar component', () => {
expect(findPopover().exists()).toBe(true);
});
+ it('scrolls to resolved threads link', () => {
+ expect(scrollIntoViewMock).toHaveBeenCalled();
+ });
+
it('dismisses a popover on the outside click', () => {
wrapper.trigger('click');
return wrapper.vm.$nextTick(() => {
@@ -233,4 +245,23 @@ describe('Design management design sidebar component', () => {
expect(Cookies.set).toHaveBeenCalledWith(cookieKey, 'true', { expires: 365 * 10 });
});
});
+
+ it('does not render To-Do button by default', () => {
+ createComponent();
+ expect(wrapper.find(DesignTodoButton).exists()).toBe(false);
+ });
+
+ describe('when `design_management_todo_button` feature flag is enabled', () => {
+ beforeEach(() => {
+ createComponent({}, { enableTodoButton: true });
+ });
+
+ it('renders sidebar root element with no top padding', () => {
+ expect(wrapper.classes()).toContain('gl-pt-0');
+ });
+
+ it('renders To-Do button', () => {
+ expect(wrapper.find(DesignTodoButton).exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/design_management/components/design_todo_button_spec.js b/spec/frontend/design_management/components/design_todo_button_spec.js
new file mode 100644
index 00000000000..451c23f0fea
--- /dev/null
+++ b/spec/frontend/design_management/components/design_todo_button_spec.js
@@ -0,0 +1,158 @@
+import { shallowMount, mount } from '@vue/test-utils';
+import TodoButton from '~/vue_shared/components/todo_button.vue';
+import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
+import createDesignTodoMutation from '~/design_management/graphql/mutations/create_design_todo.mutation.graphql';
+import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
+import mockDesign from '../mock_data/design';
+
+const mockDesignWithPendingTodos = {
+ ...mockDesign,
+ currentUserTodos: {
+ nodes: [
+ {
+ id: 'todo-id',
+ },
+ ],
+ },
+};
+
+const mutate = jest.fn().mockResolvedValue();
+
+describe('Design management design todo button', () => {
+ let wrapper;
+
+ function createComponent(props = {}, { mountFn = shallowMount } = {}) {
+ wrapper = mountFn(DesignTodoButton, {
+ propsData: {
+ design: mockDesign,
+ ...props,
+ },
+ provide: {
+ projectPath: 'project-path',
+ issueIid: '10',
+ },
+ mocks: {
+ $route: {
+ params: {
+ id: 'my-design.jpg',
+ },
+ query: {},
+ },
+ $apollo: {
+ mutate,
+ },
+ },
+ });
+ }
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ jest.clearAllMocks();
+ });
+
+ it('renders TodoButton component', () => {
+ expect(wrapper.find(TodoButton).exists()).toBe(true);
+ });
+
+ describe('when design has a pending todo', () => {
+ beforeEach(() => {
+ createComponent({ design: mockDesignWithPendingTodos }, { mountFn: mount });
+ });
+
+ it('renders correct button text', () => {
+ expect(wrapper.text()).toBe('Mark as done');
+ });
+
+ describe('when clicked', () => {
+ let dispatchEventSpy;
+
+ beforeEach(() => {
+ dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
+ jest.spyOn(document, 'querySelector').mockReturnValue({
+ innerText: 2,
+ });
+
+ createComponent({ design: mockDesignWithPendingTodos }, { mountFn: mount });
+ wrapper.trigger('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls `$apollo.mutate` with the `todoMarkDone` mutation and variables containing `id`', async () => {
+ const todoMarkDoneMutationVariables = {
+ mutation: todoMarkDoneMutation,
+ update: expect.anything(),
+ variables: {
+ id: 'todo-id',
+ },
+ };
+
+ expect(mutate).toHaveBeenCalledTimes(1);
+ expect(mutate).toHaveBeenCalledWith(todoMarkDoneMutationVariables);
+ });
+
+ it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => {
+ const dispatchedEvent = dispatchEventSpy.mock.calls[0][0];
+
+ expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchedEvent.detail).toEqual({ count: 1 });
+ expect(dispatchedEvent.type).toBe('todo:toggle');
+ });
+ });
+ });
+
+ describe('when design has no pending todos', () => {
+ beforeEach(() => {
+ createComponent({}, { mountFn: mount });
+ });
+
+ it('renders correct button text', () => {
+ expect(wrapper.text()).toBe('Add a To-Do');
+ });
+
+ describe('when clicked', () => {
+ let dispatchEventSpy;
+
+ beforeEach(() => {
+ dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
+ jest.spyOn(document, 'querySelector').mockReturnValue({
+ innerText: 2,
+ });
+
+ createComponent({}, { mountFn: mount });
+ wrapper.trigger('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls `$apollo.mutate` with the `createDesignTodoMutation` mutation and variables containing `issuable_id`, `issue_id`, & `projectPath`', async () => {
+ const createDesignTodoMutationVariables = {
+ mutation: createDesignTodoMutation,
+ update: expect.anything(),
+ variables: {
+ atVersion: null,
+ filenames: ['my-design.jpg'],
+ designId: '1',
+ issueId: '1',
+ issueIid: '10',
+ projectPath: 'project-path',
+ },
+ };
+
+ expect(mutate).toHaveBeenCalledTimes(1);
+ expect(mutate).toHaveBeenCalledWith(createDesignTodoMutationVariables);
+ });
+
+ it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => {
+ const dispatchedEvent = dispatchEventSpy.mock.calls[0][0];
+
+ expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchedEvent.detail).toEqual({ count: 3 });
+ expect(dispatchedEvent.type).toBe('todo:toggle');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
index d76b6e712fe..822df1f6472 100644
--- a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
+++ b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
@@ -10,11 +10,11 @@ exports[`Design management list item component when item appears in view after i
exports[`Design management list item component with notes renders item with multiple comments 1`] = `
<router-link-stub
- class="card cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
+ class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
to="[object Object]"
>
<div
- class="card-body p-0 d-flex-center overflow-hidden position-relative"
+ class="card-body gl-p-0 gl-display-flex gl-align-items-center gl-justify-content-center gl-overflow-hidden gl-relative"
>
<!---->
@@ -23,7 +23,7 @@ exports[`Design management list item component with notes renders item with mult
<img
alt="test"
- class="block mx-auto mw-100 mh-100 design-img"
+ class="gl-display-block gl-mx-auto gl-max-w-full mh-100 design-img"
data-qa-selector="design_image"
src=""
/>
@@ -31,13 +31,13 @@ exports[`Design management list item component with notes renders item with mult
</div>
<div
- class="card-footer d-flex w-100"
+ class="card-footer gl-display-flex gl-w-full"
>
<div
- class="d-flex flex-column str-truncated-100"
+ class="gl-display-flex gl-flex-direction-column str-truncated-100"
>
<span
- class="bold str-truncated-100"
+ class="gl-font-weight-bold str-truncated-100"
data-qa-selector="design_file_name"
>
test
@@ -57,17 +57,17 @@ exports[`Design management list item component with notes renders item with mult
</div>
<div
- class="ml-auto d-flex align-items-center text-secondary"
+ class="gl-ml-auto gl-display-flex gl-align-items-center gl-text-gray-500"
>
- <icon-stub
- class="ml-1"
+ <gl-icon-stub
+ class="gl-ml-2"
name="comments"
size="16"
/>
<span
aria-label="2 comments"
- class="ml-1"
+ class="gl-ml-2"
>
2
@@ -80,11 +80,11 @@ exports[`Design management list item component with notes renders item with mult
exports[`Design management list item component with notes renders item with single comment 1`] = `
<router-link-stub
- class="card cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
+ class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
to="[object Object]"
>
<div
- class="card-body p-0 d-flex-center overflow-hidden position-relative"
+ class="card-body gl-p-0 gl-display-flex gl-align-items-center gl-justify-content-center gl-overflow-hidden gl-relative"
>
<!---->
@@ -93,7 +93,7 @@ exports[`Design management list item component with notes renders item with sing
<img
alt="test"
- class="block mx-auto mw-100 mh-100 design-img"
+ class="gl-display-block gl-mx-auto gl-max-w-full mh-100 design-img"
data-qa-selector="design_image"
src=""
/>
@@ -101,13 +101,13 @@ exports[`Design management list item component with notes renders item with sing
</div>
<div
- class="card-footer d-flex w-100"
+ class="card-footer gl-display-flex gl-w-full"
>
<div
- class="d-flex flex-column str-truncated-100"
+ class="gl-display-flex gl-flex-direction-column str-truncated-100"
>
<span
- class="bold str-truncated-100"
+ class="gl-font-weight-bold str-truncated-100"
data-qa-selector="design_file_name"
>
test
@@ -127,17 +127,17 @@ exports[`Design management list item component with notes renders item with sing
</div>
<div
- class="ml-auto d-flex align-items-center text-secondary"
+ class="gl-ml-auto gl-display-flex gl-align-items-center gl-text-gray-500"
>
- <icon-stub
- class="ml-1"
+ <gl-icon-stub
+ class="gl-ml-2"
name="comments"
size="16"
/>
<span
aria-label="1 comment"
- class="ml-1"
+ class="gl-ml-2"
>
1
diff --git a/spec/frontend/design_management/components/list/item_spec.js b/spec/frontend/design_management/components/list/item_spec.js
index d1c90bd57b0..55c6ecbc26b 100644
--- a/spec/frontend/design_management/components/list/item_spec.js
+++ b/spec/frontend/design_management/components/list/item_spec.js
@@ -1,7 +1,6 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlIcon, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
import VueRouter from 'vue-router';
-import Icon from '~/vue_shared/components/icon.vue';
import Item from '~/design_management/components/list/item.vue';
const localVue = createLocalVue();
@@ -20,7 +19,7 @@ describe('Design management list item component', () => {
let wrapper;
const findDesignEvent = () => wrapper.find('[data-testid="designEvent"]');
- const findEventIcon = () => findDesignEvent().find(Icon);
+ const findEventIcon = () => findDesignEvent().find(GlIcon);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
function createComponent({
diff --git a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
index d6fd09eb698..1e94e90c3b0 100644
--- a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
+++ b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design management design version dropdown component renders design version dropdown button 1`] = `
-<gl-new-dropdown-stub
+<gl-dropdown-stub
category="tertiary"
headertext=""
issueiid=""
@@ -10,7 +10,7 @@ exports[`Design management design version dropdown component renders design vers
text="Showing latest version"
variant="default"
>
- <gl-new-dropdown-item-stub
+ <gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
@@ -22,8 +22,8 @@ exports[`Design management design version dropdown component renders design vers
Version
2
(latest)
- </gl-new-dropdown-item-stub>
- <gl-new-dropdown-item-stub
+ </gl-dropdown-item-stub>
+ <gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
@@ -34,12 +34,12 @@ exports[`Design management design version dropdown component renders design vers
Version
1
- </gl-new-dropdown-item-stub>
-</gl-new-dropdown-stub>
+ </gl-dropdown-item-stub>
+</gl-dropdown-stub>
`;
exports[`Design management design version dropdown component renders design version list 1`] = `
-<gl-new-dropdown-stub
+<gl-dropdown-stub
category="tertiary"
headertext=""
issueiid=""
@@ -48,7 +48,7 @@ exports[`Design management design version dropdown component renders design vers
text="Showing latest version"
variant="default"
>
- <gl-new-dropdown-item-stub
+ <gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
@@ -60,8 +60,8 @@ exports[`Design management design version dropdown component renders design vers
Version
2
(latest)
- </gl-new-dropdown-item-stub>
- <gl-new-dropdown-item-stub
+ </gl-dropdown-item-stub>
+ <gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
@@ -72,6 +72,6 @@ exports[`Design management design version dropdown component renders design vers
Version
1
- </gl-new-dropdown-item-stub>
-</gl-new-dropdown-stub>
+ </gl-dropdown-item-stub>
+</gl-dropdown-stub>
`;
diff --git a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
index f4206cdaeb3..4ef787ac754 100644
--- a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
+++ b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlNewDropdown, GlNewDropdownItem, GlSprintf } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
import DesignVersionDropdown from '~/design_management/components/upload/design_version_dropdown.vue';
import mockAllVersions from './mock_data/all_versions';
@@ -42,7 +42,7 @@ describe('Design management design version dropdown component', () => {
wrapper.destroy();
});
- const findVersionLink = index => wrapper.findAll(GlNewDropdownItem).at(index);
+ const findVersionLink = index => wrapper.findAll(GlDropdownItem).at(index);
it('renders design version dropdown button', () => {
createComponent();
@@ -75,7 +75,7 @@ describe('Design management design version dropdown component', () => {
createComponent();
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(GlNewDropdown).attributes('text')).toBe('Showing latest version');
+ expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version');
});
});
@@ -83,7 +83,7 @@ describe('Design management design version dropdown component', () => {
createComponent({ maxVersions: 1 });
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(GlNewDropdown).attributes('text')).toBe('Showing latest version');
+ expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version');
});
});
@@ -91,7 +91,7 @@ describe('Design management design version dropdown component', () => {
createComponent({ $route: designRouteFactory(PREVIOUS_VERSION_ID) });
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(GlNewDropdown).attributes('text')).toBe(`Showing version #1`);
+ expect(wrapper.find(GlDropdown).attributes('text')).toBe(`Showing version #1`);
});
});
@@ -99,7 +99,7 @@ describe('Design management design version dropdown component', () => {
createComponent({ $route: designRouteFactory(LATEST_VERSION_ID) });
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(GlNewDropdown).attributes('text')).toBe('Showing latest version');
+ expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version');
});
});
@@ -107,7 +107,7 @@ describe('Design management design version dropdown component', () => {
createComponent();
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.findAll(GlNewDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
+ expect(wrapper.findAll(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
});
});
});
diff --git a/spec/frontend/design_management/mock_data/apollo_mock.js b/spec/frontend/design_management/mock_data/apollo_mock.js
index 5e2df3877a5..1c7806c292f 100644
--- a/spec/frontend/design_management/mock_data/apollo_mock.js
+++ b/spec/frontend/design_management/mock_data/apollo_mock.js
@@ -13,6 +13,9 @@ export const designListQueryResponse = {
notesCount: 3,
image: 'image-1',
imageV432x230: 'image-1',
+ currentUserTodos: {
+ nodes: [],
+ },
},
{
id: '2',
@@ -21,6 +24,9 @@ export const designListQueryResponse = {
notesCount: 2,
image: 'image-2',
imageV432x230: 'image-2',
+ currentUserTodos: {
+ nodes: [],
+ },
},
{
id: '3',
@@ -29,6 +35,9 @@ export const designListQueryResponse = {
notesCount: 1,
image: 'image-3',
imageV432x230: 'image-3',
+ currentUserTodos: {
+ nodes: [],
+ },
},
],
},
@@ -60,6 +69,9 @@ export const reorderedDesigns = [
notesCount: 2,
image: 'image-2',
imageV432x230: 'image-2',
+ currentUserTodos: {
+ nodes: [],
+ },
},
{
id: '1',
@@ -68,6 +80,9 @@ export const reorderedDesigns = [
notesCount: 3,
image: 'image-1',
imageV432x230: 'image-1',
+ currentUserTodos: {
+ nodes: [],
+ },
},
{
id: '3',
@@ -76,6 +91,9 @@ export const reorderedDesigns = [
notesCount: 1,
image: 'image-3',
imageV432x230: 'image-3',
+ currentUserTodos: {
+ nodes: [],
+ },
},
];
diff --git a/spec/frontend/design_management/mock_data/design.js b/spec/frontend/design_management/mock_data/design.js
index 72be33fef1d..f2a3a800969 100644
--- a/spec/frontend/design_management/mock_data/design.js
+++ b/spec/frontend/design_management/mock_data/design.js
@@ -1,5 +1,5 @@
export default {
- id: 'design-id',
+ id: 'gid::/gitlab/Design/1',
filename: 'test.jpg',
fullPath: 'full-design-path',
image: 'test.jpg',
@@ -8,6 +8,7 @@ export default {
name: 'test',
},
issue: {
+ id: 'gid::/gitlab/Issue/1',
title: 'My precious issue',
webPath: 'full-issue-path',
webUrl: 'full-issue-url',
diff --git a/spec/frontend/design_management/mock_data/discussion.js b/spec/frontend/design_management/mock_data/discussion.js
new file mode 100644
index 00000000000..fbf9a2fdcc1
--- /dev/null
+++ b/spec/frontend/design_management/mock_data/discussion.js
@@ -0,0 +1,45 @@
+export default {
+ id: 'discussion-id-1',
+ resolved: false,
+ resolvable: true,
+ notes: [
+ {
+ id: 'note-id-1',
+ index: 1,
+ position: {
+ height: 100,
+ width: 100,
+ x: 10,
+ y: 15,
+ },
+ author: {
+ name: 'John',
+ webUrl: 'link-to-john-profile',
+ },
+ createdAt: '2020-05-08T07:10:45Z',
+ userPermissions: {
+ adminNote: true,
+ },
+ resolved: false,
+ },
+ {
+ id: 'note-id-3',
+ index: 3,
+ position: {
+ height: 50,
+ width: 50,
+ x: 25,
+ y: 25,
+ },
+ author: {
+ name: 'Mary',
+ webUrl: 'link-to-mary-profile',
+ },
+ createdAt: '2020-05-08T07:10:45Z',
+ userPermissions: {
+ adminNote: true,
+ },
+ resolved: false,
+ },
+ ],
+};
diff --git a/spec/frontend/design_management/mock_data/notes.js b/spec/frontend/design_management/mock_data/notes.js
index 80cb3944786..41cefaca05b 100644
--- a/spec/frontend/design_management/mock_data/notes.js
+++ b/spec/frontend/design_management/mock_data/notes.js
@@ -1,46 +1,44 @@
+import DISCUSSION_1 from './discussion';
+
+const DISCUSSION_2 = {
+ id: 'discussion-id-2',
+ notes: {
+ nodes: [
+ {
+ id: 'note-id-2',
+ index: 2,
+ position: {
+ height: 50,
+ width: 50,
+ x: 25,
+ y: 25,
+ },
+ author: {
+ name: 'Mary',
+ webUrl: 'link-to-mary-profile',
+ },
+ createdAt: '2020-05-08T07:10:45Z',
+ userPermissions: {
+ adminNote: true,
+ },
+ resolved: true,
+ },
+ ],
+ },
+};
+
export default [
{
- id: 'note-id-1',
- index: 1,
- position: {
- height: 100,
- width: 100,
- x: 10,
- y: 15,
- },
- author: {
- name: 'John',
- webUrl: 'link-to-john-profile',
- },
- createdAt: '2020-05-08T07:10:45Z',
- userPermissions: {
- adminNote: true,
- },
+ ...DISCUSSION_1.notes[0],
discussion: {
- id: 'discussion-id-1',
+ id: DISCUSSION_1.id,
+ notes: {
+ nodes: DISCUSSION_1.notes,
+ },
},
- resolved: false,
},
{
- id: 'note-id-2',
- index: 2,
- position: {
- height: 50,
- width: 50,
- x: 25,
- y: 25,
- },
- author: {
- name: 'Mary',
- webUrl: 'link-to-mary-profile',
- },
- createdAt: '2020-05-08T07:10:45Z',
- userPermissions: {
- adminNote: true,
- },
- discussion: {
- id: 'discussion-id-2',
- },
- resolved: true,
+ ...DISCUSSION_2.notes.nodes[0],
+ discussion: DISCUSSION_2,
},
];
diff --git a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
index 3881b2d7679..b80b7fdb43e 100644
--- a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
@@ -111,7 +111,7 @@ exports[`Design management index page designs renders designs list and header wi
>
<gl-button-stub
category="primary"
- class="gl-mr-3 js-select-all"
+ class="gl-mr-4 js-select-all"
icon=""
size="small"
variant="link"
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index 823294efc38..c849e4d4ed6 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -32,6 +32,8 @@ exports[`Design management design index page renders design index 1`] = `
<div
class="image-notes"
>
+ <!---->
+
<h2
class="gl-font-weight-bold gl-mt-0"
>
@@ -57,11 +59,11 @@ exports[`Design management design index page renders design index 1`] = `
<design-discussion-stub
data-testid="unresolved-discussion"
- designid="test"
+ designid="gid::/gitlab/Design/1"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
- noteableid="design-id"
+ noteableid="gid::/gitlab/Design/1"
/>
<gl-button-stub
@@ -92,7 +94,7 @@ exports[`Design management design index page renders design index 1`] = `
</p>
<a
- href="#"
+ href="https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads"
rel="noopener noreferrer"
target="_blank"
>
@@ -105,11 +107,11 @@ exports[`Design management design index page renders design index 1`] = `
>
<design-discussion-stub
data-testid="resolved-discussion"
- designid="test"
+ designid="gid::/gitlab/Design/1"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
- noteableid="design-id"
+ noteableid="gid::/gitlab/Design/1"
/>
</gl-collapse-stub>
@@ -179,6 +181,8 @@ exports[`Design management design index page with error GlAlert is rendered in c
<div
class="image-notes"
>
+ <!---->
+
<h2
class="gl-font-weight-bold gl-mt-0"
>
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index 369c8667f4d..d9f7146d258 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -7,24 +7,21 @@ import DesignIndex from '~/design_management/pages/design/index.vue';
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
import DesignPresentation from '~/design_management/components/design_presentation.vue';
import createImageDiffNoteMutation from '~/design_management/graphql/mutations/create_image_diff_note.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 updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
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';
+import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
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';
+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';
jest.mock('~/flash');
-jest.mock('mousetrap', () => ({
- bind: jest.fn(),
- unbind: jest.fn(),
-}));
const focusInput = jest.fn();
@@ -34,6 +31,12 @@ const DesignReplyForm = {
focusInput,
},
};
+const mockDesignNoDiscussions = {
+ ...design,
+ discussions: {
+ nodes: [],
+ },
+};
const localVue = createLocalVue();
localVue.use(VueRouter);
@@ -75,7 +78,7 @@ describe('Design management design index page', () => {
const findSidebar = () => wrapper.find(DesignSidebar);
const findDesignPresentation = () => wrapper.find(DesignPresentation);
- function createComponent(loading = false, data = {}) {
+ function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) {
const $apollo = {
queries: {
design: {
@@ -87,6 +90,8 @@ describe('Design management design index page', () => {
router = createRouter();
+ router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions });
+
wrapper = shallowMount(DesignIndex, {
propsData: { id: '1' },
mocks: { $apollo },
@@ -126,29 +131,28 @@ describe('Design management design index page', () => {
},
};
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl);
- createComponent(true);
+ createComponent({ loading: true });
- wrapper.vm.$router.push('/designs/test');
expect(mockEl.classList.add).toHaveBeenCalledTimes(1);
expect(mockEl.classList.add).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
});
});
it('sets loading state', () => {
- createComponent(true);
+ createComponent({ loading: true });
expect(wrapper.element).toMatchSnapshot();
});
it('renders design index', () => {
- createComponent(false, { design });
+ createComponent({ loading: false }, { data: { design } });
expect(wrapper.element).toMatchSnapshot();
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
it('passes correct props to sidebar component', () => {
- createComponent(false, { design });
+ createComponent({ loading: false }, { data: { design } });
expect(findSidebar().props()).toEqual({
design,
@@ -158,14 +162,14 @@ describe('Design management design index page', () => {
});
it('opens a new discussion form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
},
},
- });
+ );
findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 });
@@ -175,15 +179,15 @@ describe('Design management design index page', () => {
});
it('keeps new discussion form focused', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
},
},
- annotationCoordinates,
- });
+ );
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
@@ -191,18 +195,18 @@ describe('Design management design index page', () => {
});
it('sends a mutation on submitting form and closes form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
+ comment: newComment,
},
},
- annotationCoordinates,
- comment: newComment,
- });
+ );
- findDiscussionForm().vm.$emit('submitForm');
+ findDiscussionForm().vm.$emit('submit-form');
expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables);
return wrapper.vm
@@ -216,18 +220,18 @@ describe('Design management design index page', () => {
});
it('closes the form and clears the comment on canceling form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
+ comment: newComment,
},
},
- annotationCoordinates,
- comment: newComment,
- });
+ );
- findDiscussionForm().vm.$emit('cancelForm');
+ findDiscussionForm().vm.$emit('cancel-form');
expect(wrapper.vm.comment).toBe('');
@@ -238,15 +242,15 @@ describe('Design management design index page', () => {
describe('with error', () => {
beforeEach(() => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design: mockDesignNoDiscussions,
+ errorMessage: 'woops',
},
},
- errorMessage: 'woops',
- });
+ );
});
it('GlAlert is rendered in correct position with correct content', () => {
@@ -257,7 +261,7 @@ describe('Design management design index page', () => {
describe('onDesignQueryResult', () => {
describe('with no designs', () => {
it('redirects to /designs', () => {
- createComponent(true);
+ createComponent({ loading: true });
router.push = jest.fn();
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
@@ -272,7 +276,7 @@ describe('Design management design index page', () => {
describe('when no design exists for given version', () => {
it('redirects to /designs', () => {
- createComponent(true);
+ createComponent({ loading: true });
wrapper.setData({
allVersions: mockAllVersions,
});
@@ -291,4 +295,24 @@ describe('Design management design index page', () => {
});
});
});
+
+ 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' },
+ });
+ });
+ });
});
diff --git a/spec/frontend/design_management/pages/index_apollo_spec.js b/spec/frontend/design_management/pages/index_apollo_spec.js
deleted file mode 100644
index 3ea711c2cfa..00000000000
--- a/spec/frontend/design_management/pages/index_apollo_spec.js
+++ /dev/null
@@ -1,162 +0,0 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { createMockClient } from 'mock-apollo-client';
-import VueApollo from 'vue-apollo';
-import VueRouter from 'vue-router';
-import VueDraggable from 'vuedraggable';
-import { InMemoryCache } from 'apollo-cache-inmemory';
-import Design from '~/design_management/components/list/item.vue';
-import createRouter from '~/design_management/router';
-import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql';
-import permissionsQuery from '~/design_management/graphql/queries/design_permissions.query.graphql';
-import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql';
-import { deprecatedCreateFlash as createFlash } from '~/flash';
-import Index from '~/design_management/pages/index.vue';
-import {
- designListQueryResponse,
- permissionsQueryResponse,
- moveDesignMutationResponse,
- reorderedDesigns,
- moveDesignMutationResponseWithErrors,
-} from '../mock_data/apollo_mock';
-
-jest.mock('~/flash');
-
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-const router = createRouter();
-localVue.use(VueRouter);
-
-const designToMove = {
- __typename: 'Design',
- id: '2',
- event: 'NONE',
- filename: 'fox_2.jpg',
- notesCount: 2,
- image: 'image-2',
- imageV432x230: 'image-2',
-};
-
-describe('Design management index page with Apollo mock', () => {
- let wrapper;
- let mockClient;
- let apolloProvider;
- let moveDesignHandler;
-
- async function moveDesigns(localWrapper) {
- await jest.runOnlyPendingTimers();
- await localWrapper.vm.$nextTick();
-
- localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns);
- localWrapper.find(VueDraggable).vm.$emit('change', {
- moved: {
- newIndex: 0,
- element: designToMove,
- },
- });
- }
-
- const fragmentMatcher = { match: () => true };
-
- const cache = new InMemoryCache({
- fragmentMatcher,
- addTypename: false,
- });
-
- const findDesigns = () => wrapper.findAll(Design);
-
- function createComponent({
- moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse),
- }) {
- mockClient = createMockClient({ cache });
-
- mockClient.setRequestHandler(
- getDesignListQuery,
- jest.fn().mockResolvedValue(designListQueryResponse),
- );
-
- mockClient.setRequestHandler(
- permissionsQuery,
- jest.fn().mockResolvedValue(permissionsQueryResponse),
- );
-
- moveDesignHandler = moveHandler;
-
- mockClient.setRequestHandler(moveDesignMutation, moveDesignHandler);
-
- apolloProvider = new VueApollo({
- defaultClient: mockClient,
- });
-
- wrapper = shallowMount(Index, {
- localVue,
- apolloProvider,
- router,
- stubs: { VueDraggable },
- });
- }
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- mockClient = null;
- apolloProvider = null;
- });
-
- it('has a design with id 1 as a first one', async () => {
- createComponent({});
-
- await jest.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
-
- expect(findDesigns()).toHaveLength(3);
- expect(
- findDesigns()
- .at(0)
- .props('id'),
- ).toBe('1');
- });
-
- it('calls a mutation with correct parameters and reorders designs', async () => {
- createComponent({});
-
- await moveDesigns(wrapper);
-
- expect(moveDesignHandler).toHaveBeenCalled();
-
- await wrapper.vm.$nextTick();
-
- expect(
- findDesigns()
- .at(0)
- .props('id'),
- ).toBe('2');
- });
-
- it('displays flash if mutation had a recoverable error', async () => {
- createComponent({
- moveHandler: jest.fn().mockResolvedValue(moveDesignMutationResponseWithErrors),
- });
-
- await moveDesigns(wrapper);
-
- await wrapper.vm.$nextTick();
-
- expect(createFlash).toHaveBeenCalledWith('Houston, we have a problem');
- });
-
- it('displays flash if mutation had a non-recoverable error', async () => {
- createComponent({
- moveHandler: jest.fn().mockRejectedValue('Error'),
- });
-
- await moveDesigns(wrapper);
-
- await jest.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
-
- expect(createFlash).toHaveBeenCalledWith(
- 'Something went wrong when reordering designs. Please try again',
- );
- });
-});
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 093fa155d2e..661717d29a3 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -1,13 +1,15 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
-import { ApolloMutation } from 'vue-apollo';
+import VueApollo, { ApolloMutation } from 'vue-apollo';
import VueDraggable from 'vuedraggable';
import VueRouter from 'vue-router';
import { GlEmptyState } from '@gitlab/ui';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
import Index from '~/design_management/pages/index.vue';
import uploadDesignQuery from '~/design_management/graphql/mutations/upload_design.mutation.graphql';
import DesignDestroyer from '~/design_management/components/design_destroyer.vue';
import DesignDropzone from '~/design_management/components/upload/design_dropzone.vue';
import DeleteButton from '~/design_management/components/delete_button.vue';
+import Design from '~/design_management/components/list/item.vue';
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
import {
EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE,
@@ -17,6 +19,16 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
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';
+import {
+ designListQueryResponse,
+ permissionsQueryResponse,
+ moveDesignMutationResponse,
+ reorderedDesigns,
+ moveDesignMutationResponseWithErrors,
+} from '../mock_data/apollo_mock';
+import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql';
+import permissionsQuery from '~/design_management/graphql/queries/design_permissions.query.graphql';
+import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql';
jest.mock('~/flash.js');
const mockPageEl = {
@@ -61,9 +73,21 @@ const mockVersion = {
id: 'gid://gitlab/DesignManagement::Version/1',
};
+const designToMove = {
+ __typename: 'Design',
+ id: '2',
+ event: 'NONE',
+ filename: 'fox_2.jpg',
+ notesCount: 2,
+ image: 'image-2',
+ imageV432x230: 'image-2',
+};
+
describe('Design management index page', () => {
let mutate;
let wrapper;
+ let fakeApollo;
+ let moveDesignHandler;
const findDesignCheckboxes = () => wrapper.findAll('.design-checkbox');
const findSelectAllButton = () => wrapper.find('.js-select-all');
@@ -74,6 +98,20 @@ describe('Design management index page', () => {
const findDropzoneWrapper = () => wrapper.find('[data-testid="design-dropzone-wrapper"]');
const findFirstDropzoneWithDesign = () => wrapper.findAll(DesignDropzone).at(1);
const findDesignsWrapper = () => wrapper.find('[data-testid="designs-root"]');
+ const findDesigns = () => wrapper.findAll(Design);
+
+ async function moveDesigns(localWrapper) {
+ await jest.runOnlyPendingTimers();
+ await localWrapper.vm.$nextTick();
+
+ localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns);
+ localWrapper.find(VueDraggable).vm.$emit('change', {
+ moved: {
+ newIndex: 0,
+ element: designToMove,
+ },
+ });
+ }
function createComponent({
loading = false,
@@ -118,8 +156,30 @@ describe('Design management index page', () => {
});
}
+ function createComponentWithApollo({
+ moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse),
+ }) {
+ localVue.use(VueApollo);
+ moveDesignHandler = moveHandler;
+
+ const requestHandlers = [
+ [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
+ [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
+ [moveDesignMutation, moveDesignHandler],
+ ];
+
+ fakeApollo = createMockApollo(requestHandlers);
+ wrapper = shallowMount(Index, {
+ localVue,
+ apolloProvider: fakeApollo,
+ router,
+ stubs: { VueDraggable },
+ });
+ }
+
afterEach(() => {
wrapper.destroy();
+ wrapper = null;
});
describe('designs', () => {
@@ -478,16 +538,15 @@ describe('Design management index page', () => {
describe('on non-latest version', () => {
beforeEach(() => {
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
+ });
- router.replace({
+ it('does not render design checkboxes', async () => {
+ await router.replace({
name: DESIGNS_ROUTE_NAME,
query: {
version: '2',
},
});
- });
-
- it('does not render design checkboxes', () => {
expect(findDesignCheckboxes()).toHaveLength(0);
});
@@ -514,13 +573,6 @@ describe('Design management index page', () => {
files: [{ name: 'image.png', type: 'image/png' }],
getData: () => 'test.png',
};
-
- router.replace({
- name: DESIGNS_ROUTE_NAME,
- query: {
- version: '2',
- },
- });
});
it('does not call paste event if designs wrapper is not hovered', () => {
@@ -587,7 +639,69 @@ describe('Design management index page', () => {
});
createComponent(true);
- expect(scrollIntoViewMock).toHaveBeenCalled();
+ return wrapper.vm.$nextTick().then(() => {
+ expect(scrollIntoViewMock).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('with mocked Apollo client', () => {
+ it('has a design with id 1 as a first one', async () => {
+ createComponentWithApollo({});
+
+ await jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+
+ expect(findDesigns()).toHaveLength(3);
+ expect(
+ findDesigns()
+ .at(0)
+ .props('id'),
+ ).toBe('1');
+ });
+
+ it('calls a mutation with correct parameters and reorders designs', async () => {
+ createComponentWithApollo({});
+
+ await moveDesigns(wrapper);
+
+ expect(moveDesignHandler).toHaveBeenCalled();
+
+ await wrapper.vm.$nextTick();
+
+ expect(
+ findDesigns()
+ .at(0)
+ .props('id'),
+ ).toBe('2');
+ });
+
+ it('displays flash if mutation had a recoverable error', async () => {
+ createComponentWithApollo({
+ moveHandler: jest.fn().mockResolvedValue(moveDesignMutationResponseWithErrors),
+ });
+
+ await moveDesigns(wrapper);
+
+ await wrapper.vm.$nextTick();
+
+ expect(createFlash).toHaveBeenCalledWith('Houston, we have a problem');
+ });
+
+ it('displays flash if mutation had a non-recoverable error', async () => {
+ createComponentWithApollo({
+ moveHandler: jest.fn().mockRejectedValue('Error'),
+ });
+
+ await moveDesigns(wrapper);
+
+ await wrapper.vm.$nextTick(); // kick off the DOM update
+ await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
+ await wrapper.vm.$nextTick(); // kick off the DOM update for flash
+
+ expect(createFlash).toHaveBeenCalledWith(
+ 'Something went wrong when reordering designs. Please try again',
+ );
});
});
});
diff --git a/spec/frontend/design_management/router_spec.js b/spec/frontend/design_management/router_spec.js
index 2b8c7ee959b..d4cb9f75a77 100644
--- a/spec/frontend/design_management/router_spec.js
+++ b/spec/frontend/design_management/router_spec.js
@@ -35,11 +35,6 @@ function factory(routeArg) {
});
}
-jest.mock('mousetrap', () => ({
- bind: jest.fn(),
- unbind: jest.fn(),
-}));
-
describe('Design management router', () => {
afterEach(() => {
window.location.hash = '';
diff --git a/spec/frontend/design_management/utils/cache_update_spec.js b/spec/frontend/design_management/utils/cache_update_spec.js
index e8a5cf3949d..6c859e8c3e8 100644
--- a/spec/frontend/design_management/utils/cache_update_spec.js
+++ b/spec/frontend/design_management/utils/cache_update_spec.js
@@ -1,14 +1,12 @@
import { InMemoryCache } from 'apollo-cache-inmemory';
import {
updateStoreAfterDesignsDelete,
- updateStoreAfterAddDiscussionComment,
updateStoreAfterAddImageDiffNote,
updateStoreAfterUploadDesign,
updateStoreAfterUpdateImageDiffNote,
} from '~/design_management/utils/cache_update';
import {
designDeletionError,
- ADD_DISCUSSION_COMMENT_ERROR,
ADD_IMAGE_DIFF_NOTE_ERROR,
UPDATE_IMAGE_DIFF_NOTE_ERROR,
} from '~/design_management/utils/error_messages';
@@ -28,12 +26,11 @@ describe('Design Management cache update', () => {
describe('error handling', () => {
it.each`
- fnName | subject | errorMessage | extraArgs
- ${'updateStoreAfterDesignsDelete'} | ${updateStoreAfterDesignsDelete} | ${designDeletionError({ singular: true })} | ${[[design]]}
- ${'updateStoreAfterAddDiscussionComment'} | ${updateStoreAfterAddDiscussionComment} | ${ADD_DISCUSSION_COMMENT_ERROR} | ${[]}
- ${'updateStoreAfterAddImageDiffNote'} | ${updateStoreAfterAddImageDiffNote} | ${ADD_IMAGE_DIFF_NOTE_ERROR} | ${[]}
- ${'updateStoreAfterUploadDesign'} | ${updateStoreAfterUploadDesign} | ${mockErrors[0]} | ${[]}
- ${'updateStoreAfterUpdateImageDiffNote'} | ${updateStoreAfterUpdateImageDiffNote} | ${UPDATE_IMAGE_DIFF_NOTE_ERROR} | ${[]}
+ fnName | subject | errorMessage | extraArgs
+ ${'updateStoreAfterDesignsDelete'} | ${updateStoreAfterDesignsDelete} | ${designDeletionError({ singular: true })} | ${[[design]]}
+ ${'updateStoreAfterAddImageDiffNote'} | ${updateStoreAfterAddImageDiffNote} | ${ADD_IMAGE_DIFF_NOTE_ERROR} | ${[]}
+ ${'updateStoreAfterUploadDesign'} | ${updateStoreAfterUploadDesign} | ${mockErrors[0]} | ${[]}
+ ${'updateStoreAfterUpdateImageDiffNote'} | ${updateStoreAfterUpdateImageDiffNote} | ${UPDATE_IMAGE_DIFF_NOTE_ERROR} | ${[]}
`('$fnName handles errors in response', ({ subject, extraArgs, errorMessage }) => {
expect(createFlash).not.toHaveBeenCalled();
expect(() => subject(mockStore, { errors: mockErrors }, {}, ...extraArgs)).toThrow();
diff --git a/spec/frontend/design_management/utils/design_management_utils_spec.js b/spec/frontend/design_management/utils/design_management_utils_spec.js
index e6d836b9157..7e857d08d25 100644
--- a/spec/frontend/design_management/utils/design_management_utils_spec.js
+++ b/spec/frontend/design_management/utils/design_management_utils_spec.js
@@ -6,6 +6,7 @@ import {
updateImageDiffNoteOptimisticResponse,
isValidDesignFile,
extractDesign,
+ extractDesignNoteId,
} from '~/design_management/utils/design_management_utils';
import mockResponseNoDesigns from '../mock_data/no_designs';
import mockResponseWithDesigns from '../mock_data/designs';
@@ -171,3 +172,19 @@ describe('extractDesign', () => {
});
});
});
+
+describe('extractDesignNoteId', () => {
+ it.each`
+ hash | expectedNoteId
+ ${'#note_0'} | ${'0'}
+ ${'#note_1'} | ${'1'}
+ ${'#note_23'} | ${'23'}
+ ${'#note_456'} | ${'456'}
+ ${'note_1'} | ${null}
+ ${'#note_'} | ${null}
+ ${'#note_asd'} | ${null}
+ ${'#note_1asd'} | ${null}
+ `('returns $expectedNoteId when hash is $hash', ({ hash, expectedNoteId }) => {
+ expect(extractDesignNoteId(hash)).toBe(expectedNoteId);
+ });
+});