summaryrefslogtreecommitdiff
path: root/spec/frontend/design_management/components
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 16:05:49 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 16:05:49 +0000
commit43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch)
treedceebdc68925362117480a5d672bcff122fb625b /spec/frontend/design_management/components
parent20c84b99005abd1c82101dfeff264ac50d2df211 (diff)
downloadgitlab-ce-0f94cf6ca9d272d8e0fda4a7a597866cf3dc1fc0.tar.gz
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc4216-0-stable
Diffstat (limited to 'spec/frontend/design_management/components')
-rw-r--r--spec/frontend/design_management/components/delete_button_spec.js6
-rw-r--r--spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap3
-rw-r--r--spec/frontend/design_management/components/design_notes/design_discussion_spec.js170
-rw-r--r--spec/frontend/design_management/components/design_notes/design_note_signed_out_spec.js4
-rw-r--r--spec/frontend/design_management/components/design_notes/design_note_spec.js56
-rw-r--r--spec/frontend/design_management/components/design_notes/design_reply_form_spec.js258
-rw-r--r--spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js4
-rw-r--r--spec/frontend/design_management/components/design_overlay_spec.js48
-rw-r--r--spec/frontend/design_management/components/design_presentation_spec.js6
-rw-r--r--spec/frontend/design_management/components/design_scaler_spec.js7
-rw-r--r--spec/frontend/design_management/components/design_sidebar_spec.js10
-rw-r--r--spec/frontend/design_management/components/design_todo_button_spec.js6
-rw-r--r--spec/frontend/design_management/components/image_spec.js4
-rw-r--r--spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap10
-rw-r--r--spec/frontend/design_management/components/list/item_spec.js4
-rw-r--r--spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap39
-rw-r--r--spec/frontend/design_management/components/toolbar/design_navigation_spec.js71
-rw-r--r--spec/frontend/design_management/components/toolbar/index_spec.js46
-rw-r--r--spec/frontend/design_management/components/upload/button_spec.js4
-rw-r--r--spec/frontend/design_management/components/upload/design_version_dropdown_spec.js49
-rw-r--r--spec/frontend/design_management/components/upload/mock_data/all_versions.js20
21 files changed, 447 insertions, 378 deletions
diff --git a/spec/frontend/design_management/components/delete_button_spec.js b/spec/frontend/design_management/components/delete_button_spec.js
index 426a61f5a47..cacda9a475e 100644
--- a/spec/frontend/design_management/components/delete_button_spec.js
+++ b/spec/frontend/design_management/components/delete_button_spec.js
@@ -21,10 +21,6 @@ describe('Batch delete button component', () => {
});
}
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders non-disabled button by default', () => {
createComponent();
@@ -34,7 +30,7 @@ describe('Batch delete button component', () => {
it('renders disabled button when design is deleting', () => {
createComponent({ isDeleting: true });
- expect(findButton().attributes('disabled')).toBe('true');
+ expect(findButton().attributes('disabled')).toBeDefined();
});
it('emits `delete-selected-designs` event on modal ok click', async () => {
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 402e55347af..3b407d11041 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
@@ -58,6 +58,7 @@ exports[`Design note component should match the snapshot 1`] = `
>
<time-ago-tooltip-stub
cssclass=""
+ datetimeformat="DATE_WITH_TIME_FORMAT"
time="2019-07-26T15:02:20Z"
tooltipplacement="bottom"
/>
@@ -70,6 +71,8 @@ exports[`Design note component should match the snapshot 1`] = `
>
<!---->
+
+ <!---->
</div>
</div>
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 2091e1e08dd..a6ab147884f 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
@@ -1,18 +1,22 @@
import { GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import { ApolloMutation } from 'vue-apollo';
import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue';
import DesignNote from '~/design_management/components/design_notes/design_note.vue';
import DesignNoteSignedOut from '~/design_management/components/design_notes/design_note_signed_out.vue';
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
import ToggleRepliesWidget from '~/design_management/components/design_notes/toggle_replies_widget.vue';
-import createNoteMutation from '~/design_management/graphql/mutations/create_note.mutation.graphql';
import toggleResolveDiscussionMutation from '~/design_management/graphql/mutations/toggle_resolve_discussion.mutation.graphql';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import destroyNoteMutation from '~/design_management/graphql/mutations/destroy_note.mutation.graphql';
+import { DELETE_NOTE_ERROR_MSG } from '~/design_management/constants';
import mockDiscussion from '../../mock_data/discussion';
import notes from '../../mock_data/notes';
+jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
+
const defaultMockDiscussion = {
id: '0',
resolved: false,
@@ -23,7 +27,6 @@ const defaultMockDiscussion = {
const DEFAULT_TODO_COUNT = 2;
describe('Design discussions component', () => {
- const originalGon = window.gon;
let wrapper;
const findDesignNotes = () => wrapper.findAllComponents(DesignNote);
@@ -34,18 +37,7 @@ describe('Design discussions component', () => {
const findResolvedMessage = () => wrapper.find('[data-testid="resolved-message"]');
const findResolveLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findResolveCheckbox = () => wrapper.find('[data-testid="resolve-checkbox"]');
- const findApolloMutation = () => wrapper.findComponent(ApolloMutation);
- const mutationVariables = {
- mutation: createNoteMutation,
- variables: {
- input: {
- noteableId: 'noteable-id',
- body: 'test',
- discussionId: '0',
- },
- },
- };
const registerPath = '/users/sign_up?redirect_to_referer=yes';
const signInPath = '/users/sign_in?redirect_to_referer=yes';
const mutate = jest.fn().mockResolvedValue({ data: { createNote: { errors: [] } } });
@@ -59,7 +51,7 @@ describe('Design discussions component', () => {
provider: { clients: { defaultClient: { readQuery } } },
};
- function createComponent(props = {}, data = {}) {
+ function createComponent({ props = {}, data = {}, apolloConfig = {} } = {}) {
wrapper = mount(DesignDiscussion, {
propsData: {
resolvedDiscussionsExpanded: true,
@@ -82,7 +74,10 @@ describe('Design discussions component', () => {
issueIid: '1',
},
mocks: {
- $apollo,
+ $apollo: {
+ ...$apollo,
+ ...apolloConfig,
+ },
$route: {
hash: '#note_1',
params: {
@@ -101,16 +96,17 @@ describe('Design discussions component', () => {
});
afterEach(() => {
- wrapper.destroy();
- window.gon = originalGon;
+ confirmAction.mockReset();
});
describe('when discussion is not resolvable', () => {
beforeEach(() => {
createComponent({
- discussion: {
- ...defaultMockDiscussion,
- resolvable: false,
+ props: {
+ discussion: {
+ ...defaultMockDiscussion,
+ resolvable: false,
+ },
},
});
});
@@ -171,11 +167,13 @@ describe('Design discussions component', () => {
innerText: DEFAULT_TODO_COUNT,
});
createComponent({
- discussion: {
- ...defaultMockDiscussion,
- resolved: true,
- resolvedBy: notes[0].author,
- resolvedAt: '2020-05-08T07:10:45Z',
+ props: {
+ discussion: {
+ ...defaultMockDiscussion,
+ resolved: true,
+ resolvedBy: notes[0].author,
+ resolvedAt: '2020-05-08T07:10:45Z',
+ },
},
});
});
@@ -206,10 +204,10 @@ describe('Design discussions component', () => {
});
it('emit todo:toggle when discussion is resolved', async () => {
- createComponent(
- { discussionWithOpenForm: defaultMockDiscussion.id },
- { discussionComment: 'test', isFormRendered: true },
- );
+ createComponent({
+ props: { discussionWithOpenForm: defaultMockDiscussion.id },
+ data: { isFormRendered: true },
+ });
findResolveButton().trigger('click');
findReplyForm().vm.$emit('submitForm');
@@ -261,32 +259,28 @@ describe('Design discussions component', () => {
expect(findReplyForm().exists()).toBe(true);
});
- it('calls mutation on submitting form and closes the form', async () => {
- createComponent(
- { discussionWithOpenForm: defaultMockDiscussion.id },
- { discussionComment: 'test', isFormRendered: true },
- );
+ it('closes the form when note submit mutation is completed', async () => {
+ createComponent({
+ props: { discussionWithOpenForm: defaultMockDiscussion.id },
+ data: { isFormRendered: true },
+ });
- findReplyForm().vm.$emit('submit-form');
- expect(mutate).toHaveBeenCalledWith(mutationVariables);
+ findReplyForm().vm.$emit('note-submit-complete', { data: { createNote: {} } });
- await mutate();
await nextTick();
expect(findReplyForm().exists()).toBe(false);
});
it('clears the discussion comment on closing comment form', async () => {
- createComponent(
- { discussionWithOpenForm: defaultMockDiscussion.id },
- { discussionComment: 'test', isFormRendered: true },
- );
+ createComponent({
+ props: { discussionWithOpenForm: defaultMockDiscussion.id },
+ data: { isFormRendered: true },
+ });
await nextTick();
findReplyForm().vm.$emit('cancel-form');
- expect(wrapper.vm.discussionComment).toBe('');
-
await nextTick();
expect(findReplyForm().exists()).toBe(false);
});
@@ -295,15 +289,15 @@ describe('Design discussions component', () => {
it.each([notes[0], notes[0].discussion.notes.nodes[1]])(
'applies correct class to all notes in the active discussion',
(note) => {
- createComponent(
- { discussion: mockDiscussion },
- {
+ createComponent({
+ props: { discussion: mockDiscussion },
+ data: {
activeDiscussion: {
id: note.id,
source: 'pin',
},
},
- );
+ });
expect(
wrapper
@@ -329,10 +323,10 @@ describe('Design discussions component', () => {
});
it('calls toggleResolveDiscussion mutation after adding a note if checkbox was checked', () => {
- createComponent(
- { discussionWithOpenForm: defaultMockDiscussion.id },
- { discussionComment: 'test', isFormRendered: true },
- );
+ createComponent({
+ props: { discussionWithOpenForm: defaultMockDiscussion.id },
+ data: { isFormRendered: true },
+ });
findResolveButton().trigger('click');
findReplyForm().vm.$emit('submitForm');
@@ -359,15 +353,15 @@ describe('Design discussions component', () => {
beforeEach(() => {
window.gon = { current_user_id: null };
- createComponent(
- {
+ createComponent({
+ props: {
discussion: {
...defaultMockDiscussion,
},
discussionWithOpenForm: defaultMockDiscussion.id,
},
- { discussionComment: 'test', isFormRendered: true },
- );
+ data: { isFormRendered: true },
+ });
});
it('does not render resolve discussion button', () => {
@@ -378,10 +372,6 @@ describe('Design discussions component', () => {
expect(findReplyPlaceholder().exists()).toBe(false);
});
- it('does not render apollo-mutation component', () => {
- expect(findApolloMutation().exists()).toBe(false);
- });
-
it('renders design-note-signed-out component', () => {
expect(findDesignNoteSignedOut().exists()).toBe(true);
expect(findDesignNoteSignedOut().props()).toMatchObject({
@@ -390,4 +380,64 @@ describe('Design discussions component', () => {
});
});
});
+
+ it('should open confirmation modal when the note emits `delete-note` event', () => {
+ createComponent();
+
+ findDesignNotes().at(0).vm.$emit('delete-note', { id: '1' });
+ expect(confirmAction).toHaveBeenCalled();
+ });
+
+ describe('when confirmation modal is opened', () => {
+ const noteId = 'note-test-id';
+
+ it('sends the mutation with correct variables', async () => {
+ confirmAction.mockResolvedValueOnce(true);
+ const destroyNoteMutationSuccess = jest.fn().mockResolvedValue({
+ data: { destroyNote: { note: null, __typename: 'DestroyNote', errors: [] } },
+ });
+ createComponent({ apolloConfig: { mutate: destroyNoteMutationSuccess } });
+
+ findDesignNotes().at(0).vm.$emit('delete-note', { id: noteId });
+
+ expect(confirmAction).toHaveBeenCalled();
+
+ await waitForPromises();
+
+ expect(destroyNoteMutationSuccess).toHaveBeenCalledWith({
+ update: expect.any(Function),
+ mutation: destroyNoteMutation,
+ variables: {
+ input: {
+ id: noteId,
+ },
+ },
+ optimisticResponse: {
+ destroyNote: {
+ note: null,
+ errors: [],
+ __typename: 'DestroyNotePayload',
+ },
+ },
+ });
+ });
+
+ it('emits `delete-note-error` event if GraphQL mutation fails', async () => {
+ confirmAction.mockResolvedValueOnce(true);
+ const destroyNoteMutationError = jest.fn().mockRejectedValue(new Error('GraphQL error'));
+ createComponent({ apolloConfig: { mutate: destroyNoteMutationError } });
+
+ findDesignNotes().at(0).vm.$emit('delete-note', { id: noteId });
+
+ await waitForPromises();
+
+ expect(destroyNoteMutationError).toHaveBeenCalled();
+
+ await waitForPromises();
+
+ expect(wrapper.emitted()).toEqual({
+ 'delete-note-error': [[DELETE_NOTE_ERROR_MSG]],
+ });
+ });
+ });
});
diff --git a/spec/frontend/design_management/components/design_notes/design_note_signed_out_spec.js b/spec/frontend/design_management/components/design_notes/design_note_signed_out_spec.js
index e71bb5ab520..95b08b89809 100644
--- a/spec/frontend/design_management/components/design_notes/design_note_signed_out_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_note_signed_out_spec.js
@@ -18,10 +18,6 @@ function createComponent(isAddDiscussion = false) {
describe('DesignNoteSignedOut', () => {
let wrapper;
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders message containing register and sign-in links while user wants to reply to a discussion', () => {
wrapper = createComponent();
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 df511586c10..6f5b282fa3b 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
@@ -1,6 +1,6 @@
import { ApolloMutation } from 'vue-apollo';
import { nextTick } from 'vue';
-import { GlAvatar, GlAvatarLink } from '@gitlab/ui';
+import { GlAvatar, GlAvatarLink, GlDropdown } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import DesignNote from '~/design_management/components/design_notes/design_note.vue';
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
@@ -38,6 +38,8 @@ describe('Design note component', () => {
const findReplyForm = () => wrapper.findComponent(DesignReplyForm);
const findEditButton = () => wrapper.findByTestId('note-edit');
const findNoteContent = () => wrapper.findByTestId('note-text');
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDeleteNoteButton = () => wrapper.find('[data-testid="delete-note-button"]');
function createComponent(props = {}, data = { isEditing: false }) {
wrapper = shallowMountExtended(DesignNote, {
@@ -63,10 +65,6 @@ describe('Design note component', () => {
});
}
- afterEach(() => {
- wrapper.destroy();
- });
-
it('should match the snapshot', () => {
createComponent({
note,
@@ -112,6 +110,14 @@ describe('Design note component', () => {
expect(findEditButton().exists()).toBe(false);
});
+ it('should not display a dropdown if user does not have a permission to delete note', () => {
+ createComponent({
+ note,
+ });
+
+ expect(findDropdown().exists()).toBe(false);
+ });
+
describe('when user has a permission to edit note', () => {
it('should open an edit form on edit button click', async () => {
createComponent({
@@ -158,15 +164,47 @@ describe('Design note component', () => {
expect(findNoteContent().exists()).toBe(true);
});
- it('calls a mutation on submit-form event and hides a form', async () => {
- findReplyForm().vm.$emit('submit-form');
- expect(mutate).toHaveBeenCalled();
+ it('hides a form after update mutation is completed', async () => {
+ findReplyForm().vm.$emit('note-submit-complete', { data: { updateNote: { errors: [] } } });
- await mutate();
await nextTick();
expect(findReplyForm().exists()).toBe(false);
expect(findNoteContent().exists()).toBe(true);
});
});
});
+
+ describe('when user has a permission to delete note', () => {
+ it('should display a dropdown', () => {
+ createComponent({
+ note: {
+ ...note,
+ userPermissions: {
+ adminNote: true,
+ },
+ },
+ });
+
+ expect(findDropdown().exists()).toBe(true);
+ });
+ });
+
+ it('should emit `delete-note` event with proper payload when delete note button is clicked', () => {
+ const payload = {
+ ...note,
+ userPermissions: {
+ adminNote: true,
+ },
+ };
+
+ createComponent({
+ note: {
+ ...payload,
+ },
+ });
+
+ findDeleteNoteButton().vm.$emit('click');
+
+ expect(wrapper.emitted()).toEqual({ 'delete-note': [[{ ...payload }]] });
+ });
});
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 f4d4f9cf896..f08efc0c685 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
@@ -1,46 +1,95 @@
+import { GlAlert } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import Autosave from '~/autosave';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
+import createNoteMutation from '~/design_management/graphql/mutations/create_note.mutation.graphql';
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
+import {
+ ADD_DISCUSSION_COMMENT_ERROR,
+ ADD_IMAGE_DIFF_NOTE_ERROR,
+ UPDATE_IMAGE_DIFF_NOTE_ERROR,
+ UPDATE_NOTE_ERROR,
+} from '~/design_management/utils/error_messages';
+import {
+ mockNoteSubmitSuccessMutationResponse,
+ mockNoteSubmitFailureMutationResponse,
+} from '../../mock_data/apollo_mock';
+
+Vue.use(VueApollo);
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
jest.mock('~/autosave');
describe('Design reply form component', () => {
let wrapper;
- let originalGon;
+ let mockApollo;
const findTextarea = () => wrapper.find('textarea');
const findSubmitButton = () => wrapper.findComponent({ ref: 'submitButton' });
const findCancelButton = () => wrapper.findComponent({ ref: 'cancelButton' });
-
- function createComponent(props = {}, mountOptions = {}) {
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ const mockNoteableId = 'gid://gitlab/DesignManagement::Design/6';
+ const mockComment = 'New comment';
+ const mockDiscussionId = 'gid://gitlab/Discussion/6466a72f35b163f3c3e52d7976a09387f2c573e8';
+ const createNoteMutationData = {
+ input: {
+ noteableId: mockNoteableId,
+ discussionId: mockDiscussionId,
+ body: mockComment,
+ },
+ };
+
+ const ctrlKey = {
+ ctrlKey: true,
+ };
+ const metaKey = {
+ metaKey: true,
+ };
+ const mockMutationHandler = jest.fn().mockResolvedValue(mockNoteSubmitSuccessMutationResponse);
+
+ function createComponent({
+ props = {},
+ mountOptions = {},
+ data = {},
+ mutationHandler = mockMutationHandler,
+ } = {}) {
+ mockApollo = createMockApollo([[createNoteMutation, mutationHandler]]);
wrapper = mount(DesignReplyForm, {
propsData: {
+ designNoteMutation: createNoteMutation,
+ noteableId: mockNoteableId,
+ markdownDocsPath: 'path/to/markdown/docs',
+ markdownPreviewPath: 'path/to/markdown/preview',
value: '',
- isSaving: false,
- noteableId: 'gid://gitlab/DesignManagement::Design/6',
...props,
},
...mountOptions,
+ apolloProvider: mockApollo,
+ data() {
+ return {
+ ...data,
+ };
+ },
});
}
beforeEach(() => {
- originalGon = window.gon;
window.gon.current_user_id = 1;
});
afterEach(() => {
- wrapper.destroy();
- window.gon = originalGon;
+ mockApollo = null;
confirmAction.mockReset();
});
it('textarea has focus after component mount', () => {
// We need to attach to document, so that `document.activeElement` is properly set in jsdom
- createComponent({}, { attachTo: document.body });
+ createComponent({ mountOptions: { attachTo: document.body } });
expect(findTextarea().element).toEqual(document.activeElement);
});
@@ -64,7 +113,7 @@ describe('Design reply form component', () => {
});
it('renders button text as "Save comment" when creating a comment', () => {
- createComponent({ isNewComment: false });
+ createComponent({ props: { isNewComment: false } });
expect(findSubmitButton().html()).toMatchSnapshot();
});
@@ -75,9 +124,8 @@ describe('Design reply form component', () => {
${'gid://gitlab/DiffDiscussion/123'} | ${123}
`(
'initializes autosave support on discussion with proper key',
- async ({ discussionId, shortDiscussionId }) => {
- createComponent({ discussionId });
- await nextTick();
+ ({ discussionId, shortDiscussionId }) => {
+ createComponent({ props: { discussionId } });
expect(Autosave).toHaveBeenCalledWith(expect.any(Element), [
'Discussion',
@@ -89,31 +137,21 @@ describe('Design reply form component', () => {
describe('when form has no text', () => {
beforeEach(() => {
- createComponent({
- value: '',
- });
+ createComponent();
});
it('submit button is disabled', () => {
expect(findSubmitButton().attributes().disabled).toBe('disabled');
});
- it('does not emit submitForm event on textarea ctrl+enter keydown', async () => {
- findTextarea().trigger('keydown.enter', {
- ctrlKey: true,
- });
-
- await nextTick();
- expect(wrapper.emitted('submit-form')).toBeUndefined();
- });
-
- it('does not emit submitForm event on textarea meta+enter keydown', async () => {
- findTextarea().trigger('keydown.enter', {
- metaKey: true,
- });
+ it.each`
+ key | keyData
+ ${'ctrl'} | ${ctrlKey}
+ ${'meta'} | ${metaKey}
+ `('does not perform mutation on textarea $key+enter keydown', ({ keyData }) => {
+ findTextarea().trigger('keydown.enter', keyData);
- await nextTick();
- expect(wrapper.emitted('submit-form')).toBeUndefined();
+ expect(mockMutationHandler).not.toHaveBeenCalled();
});
it('emits cancelForm event on pressing escape button on textarea', () => {
@@ -129,118 +167,150 @@ describe('Design reply form component', () => {
});
});
- describe('when form has text', () => {
- beforeEach(() => {
- createComponent({
- value: 'test',
- });
- });
-
+ describe('when the form has text', () => {
it('submit button is enabled', () => {
+ createComponent({ props: { value: mockComment } });
expect(findSubmitButton().attributes().disabled).toBeUndefined();
});
- it('emits submitForm event on Comment button click', async () => {
- const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
+ it('calls a mutation on submit button click event', async () => {
+ const mockMutationVariables = {
+ noteableId: mockNoteableId,
+ discussionId: mockDiscussionId,
+ };
- findSubmitButton().vm.$emit('click');
+ createComponent({
+ props: {
+ mutationVariables: mockMutationVariables,
+ value: mockComment,
+ },
+ });
- await nextTick();
- expect(wrapper.emitted('submit-form')).toHaveLength(1);
- expect(autosaveResetSpy).toHaveBeenCalled();
- });
+ findSubmitButton().vm.$emit('click');
- it('emits submitForm event on textarea ctrl+enter keydown', async () => {
- const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
+ expect(mockMutationHandler).toHaveBeenCalledWith(createNoteMutationData);
- findTextarea().trigger('keydown.enter', {
- ctrlKey: true,
- });
+ await waitForPromises();
- await nextTick();
- expect(wrapper.emitted('submit-form')).toHaveLength(1);
- expect(autosaveResetSpy).toHaveBeenCalled();
+ expect(wrapper.emitted('note-submit-complete')).toEqual([
+ [mockNoteSubmitSuccessMutationResponse],
+ ]);
});
- it('emits submitForm event on textarea meta+enter keydown', async () => {
- const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
+ it.each`
+ key | keyData
+ ${'ctrl'} | ${ctrlKey}
+ ${'meta'} | ${metaKey}
+ `('does perform mutation on textarea $key+enter keydown', async ({ keyData }) => {
+ const mockMutationVariables = {
+ noteableId: mockNoteableId,
+ discussionId: mockDiscussionId,
+ };
- findTextarea().trigger('keydown.enter', {
- metaKey: true,
+ createComponent({
+ props: {
+ mutationVariables: mockMutationVariables,
+ value: mockComment,
+ },
});
- await nextTick();
- expect(wrapper.emitted('submit-form')).toHaveLength(1);
- expect(autosaveResetSpy).toHaveBeenCalled();
- });
+ findTextarea().trigger('keydown.enter', keyData);
- it('emits input event on changing textarea content', async () => {
- findTextarea().setValue('test2');
+ expect(mockMutationHandler).toHaveBeenCalledWith(createNoteMutationData);
- await nextTick();
- expect(wrapper.emitted('input')).toEqual([['test2']]);
+ await waitForPromises();
+ expect(wrapper.emitted('note-submit-complete')).toEqual([
+ [mockNoteSubmitSuccessMutationResponse],
+ ]);
});
- it('emits cancelForm event on Escape key if text was not changed', () => {
- findTextarea().trigger('keyup.esc');
+ it('shows error message when mutation fails', async () => {
+ const failedMutation = jest.fn().mockRejectedValue(mockNoteSubmitFailureMutationResponse);
+ createComponent({
+ props: {
+ designNoteMutation: createNoteMutation,
+ value: mockComment,
+ },
+ mutationHandler: failedMutation,
+ data: {
+ errorMessage: 'error',
+ },
+ });
- expect(wrapper.emitted('cancel-form')).toHaveLength(1);
+ findSubmitButton().vm.$emit('click');
+
+ await waitForPromises();
+ expect(findAlert().exists()).toBe(true);
});
- it('opens confirmation modal on Escape key when text has changed', async () => {
- wrapper.setProps({ value: 'test2' });
+ it.each`
+ isDiscussion | isNewComment | errorMessage
+ ${true} | ${true} | ${ADD_IMAGE_DIFF_NOTE_ERROR}
+ ${true} | ${false} | ${UPDATE_IMAGE_DIFF_NOTE_ERROR}
+ ${false} | ${true} | ${ADD_DISCUSSION_COMMENT_ERROR}
+ ${false} | ${false} | ${UPDATE_NOTE_ERROR}
+ `(
+ 'return proper error message on error in case of isDiscussion is $isDiscussion and isNewComment is $isNewComment',
+ ({ isDiscussion, isNewComment, errorMessage }) => {
+ createComponent({ props: { isDiscussion, isNewComment } });
+
+ expect(wrapper.vm.getErrorMessage()).toBe(errorMessage);
+ },
+ );
- await nextTick();
- findTextarea().trigger('keyup.esc');
- expect(confirmAction).toHaveBeenCalled();
- });
+ it('emits cancelForm event on Escape key if text was not changed', () => {
+ createComponent();
- it('emits cancelForm event on Cancel button click if text was not changed', () => {
- findCancelButton().trigger('click');
+ findTextarea().trigger('keyup.esc');
expect(wrapper.emitted('cancel-form')).toHaveLength(1);
});
- it('opens confirmation modal on Cancel button click when text has changed', async () => {
- wrapper.setProps({ value: 'test2' });
+ it('opens confirmation modal on Escape key when text has changed', () => {
+ createComponent();
+
+ findTextarea().setValue(mockComment);
+
+ findTextarea().trigger('keyup.esc');
- await nextTick();
- findCancelButton().trigger('click');
expect(confirmAction).toHaveBeenCalled();
});
it('emits cancelForm event when confirmed', async () => {
confirmAction.mockResolvedValueOnce(true);
- const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
- wrapper.setProps({ value: 'test3' });
- await nextTick();
+ createComponent({ props: { value: mockComment } });
+ findTextarea().setValue('Comment changed');
findTextarea().trigger('keyup.esc');
- await nextTick();
expect(confirmAction).toHaveBeenCalled();
- await nextTick();
+ await waitForPromises();
expect(wrapper.emitted('cancel-form')).toHaveLength(1);
- expect(autosaveResetSpy).toHaveBeenCalled();
});
- it("doesn't emit cancelForm event when not confirmed", async () => {
+ it('does not emit cancelForm event when not confirmed', async () => {
confirmAction.mockResolvedValueOnce(false);
- const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
- wrapper.setProps({ value: 'test3' });
- await nextTick();
+ createComponent({ props: { value: mockComment } });
+ findTextarea().setValue('Comment changed');
findTextarea().trigger('keyup.esc');
- await nextTick();
expect(confirmAction).toHaveBeenCalled();
- await nextTick();
+ await waitForPromises();
expect(wrapper.emitted('cancel-form')).toBeUndefined();
- expect(autosaveResetSpy).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when component is destroyed', () => {
+ it('calls autosave.reset', async () => {
+ const autosaveResetSpy = jest.spyOn(Autosave.prototype, 'reset');
+ createComponent();
+ await wrapper.destroy();
+ expect(autosaveResetSpy).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js b/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js
index 41129e2b58d..eaa5a620fa6 100644
--- a/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js
+++ b/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js
@@ -23,10 +23,6 @@ describe('Toggle replies widget component', () => {
});
}
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('when replies are collapsed', () => {
beforeEach(() => {
createComponent();
diff --git a/spec/frontend/design_management/components/design_overlay_spec.js b/spec/frontend/design_management/components/design_overlay_spec.js
index 2807fe7727f..3eb47fdb97e 100644
--- a/spec/frontend/design_management/components/design_overlay_spec.js
+++ b/spec/frontend/design_management/components/design_overlay_spec.js
@@ -1,6 +1,6 @@
-import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import DesignOverlay from '~/design_management/components/design_overlay.vue';
@@ -16,22 +16,20 @@ describe('Design overlay component', () => {
const mockDimensions = { width: 100, height: 100 };
- const findOverlay = () => wrapper.find('[data-testid="design-overlay"]');
- const findAllNotes = () => wrapper.findAll('[data-testid="note-pin"]');
- const findCommentBadge = () => wrapper.find('[data-testid="comment-badge"]');
+ const findOverlay = () => wrapper.findByTestId('design-overlay');
+ const findAllNotes = () => wrapper.findAllByTestId('note-pin');
+ const findCommentBadge = () => wrapper.findByTestId('comment-badge');
const findBadgeAtIndex = (noteIndex) => findAllNotes().at(noteIndex);
const findFirstBadge = () => findBadgeAtIndex(0);
const findSecondBadge = () => findBadgeAtIndex(1);
- const clickAndDragBadge = async (elem, fromPoint, toPoint) => {
+ const clickAndDragBadge = (elem, fromPoint, toPoint) => {
elem.vm.$emit(
'mousedown',
new MouseEvent('click', { clientX: fromPoint.x, clientY: fromPoint.y }),
);
findOverlay().trigger('mousemove', { clientX: toPoint.x, clientY: toPoint.y });
- await nextTick();
elem.vm.$emit('mouseup', new MouseEvent('click', { clientX: toPoint.x, clientY: toPoint.y }));
- await nextTick();
};
function createComponent(props = {}, data = {}) {
@@ -47,7 +45,7 @@ describe('Design overlay component', () => {
},
});
- wrapper = shallowMount(DesignOverlay, {
+ wrapper = shallowMountExtended(DesignOverlay, {
apolloProvider,
propsData: {
dimensions: mockDimensions,
@@ -80,7 +78,7 @@ describe('Design overlay component', () => {
expect(wrapper.attributes().style).toBe('width: 100px; height: 100px; top: 0px; left: 0px;');
});
- it('should emit `openCommentForm` when clicking on overlay', async () => {
+ it('should emit `openCommentForm` when clicking on overlay', () => {
createComponent();
const newCoordinates = {
x: 10,
@@ -90,7 +88,7 @@ describe('Design overlay component', () => {
wrapper
.find('[data-qa-selector="design_image_button"]')
.trigger('mouseup', { offsetX: newCoordinates.x, offsetY: newCoordinates.y });
- await nextTick();
+
expect(wrapper.emitted('openCommentForm')).toEqual([
[{ x: newCoordinates.x, y: newCoordinates.y }],
]);
@@ -175,25 +173,15 @@ describe('Design overlay component', () => {
});
});
- it('should recalculate badges positions on window resize', async () => {
+ it('should calculate badges positions based on dimensions', () => {
createComponent({
notes,
dimensions: {
- width: 400,
- height: 400,
- },
- });
-
- expect(findFirstBadge().props('position')).toEqual({ left: '40px', top: '60px' });
-
- wrapper.setProps({
- dimensions: {
width: 200,
height: 200,
},
});
- await nextTick();
expect(findFirstBadge().props('position')).toEqual({ left: '20px', top: '30px' });
});
@@ -216,7 +204,6 @@ describe('Design overlay component', () => {
new MouseEvent('click', { clientX: position.x, clientY: position.y }),
);
- await nextTick();
findFirstBadge().vm.$emit(
'mouseup',
new MouseEvent('click', { clientX: position.x, clientY: position.y }),
@@ -290,7 +277,7 @@ describe('Design overlay component', () => {
});
describe('when moving the comment badge', () => {
- it('should update badge style when note-moving action ends', async () => {
+ it('should update badge style when note-moving action ends', () => {
const { position } = notes[0];
createComponent({
currentCommentForm: {
@@ -298,19 +285,15 @@ describe('Design overlay component', () => {
},
});
- const commentBadge = findCommentBadge();
+ expect(findCommentBadge().props('position')).toEqual({ left: '10px', top: '15px' });
+
const toPoint = { x: 20, y: 20 };
- await clickAndDragBadge(commentBadge, { x: position.x, y: position.y }, toPoint);
- commentBadge.vm.$emit('mouseup', new MouseEvent('click'));
- // simulates the currentCommentForm being updated in index.vue component, and
- // propagated back down to this prop
- wrapper.setProps({
+ createComponent({
currentCommentForm: { height: position.height, width: position.width, ...toPoint },
});
- await nextTick();
- expect(commentBadge.props('position')).toEqual({ left: '20px', top: '20px' });
+ expect(findCommentBadge().props('position')).toEqual({ left: '20px', top: '20px' });
});
it('should emit `openCommentForm` event when mouseleave fired on overlay element', async () => {
@@ -330,8 +313,7 @@ describe('Design overlay component', () => {
newCoordinates,
);
- wrapper.trigger('mouseleave');
- await nextTick();
+ findOverlay().vm.$emit('mouseleave');
expect(wrapper.emitted('openCommentForm')).toEqual([[newCoordinates]]);
});
diff --git a/spec/frontend/design_management/components/design_presentation_spec.js b/spec/frontend/design_management/components/design_presentation_spec.js
index 4a339899473..fdcea6d88c0 100644
--- a/spec/frontend/design_management/components/design_presentation_spec.js
+++ b/spec/frontend/design_management/components/design_presentation_spec.js
@@ -15,7 +15,6 @@ const mockOverlayData = {
};
describe('Design management design presentation component', () => {
- const originalGon = window.gon;
let wrapper;
function createComponent(
@@ -114,11 +113,6 @@ describe('Design management design presentation component', () => {
window.gon = { current_user_id: 1 };
});
- afterEach(() => {
- wrapper.destroy();
- window.gon = originalGon;
- });
-
it('renders image and overlay when image provided', async () => {
createComponent(
{
diff --git a/spec/frontend/design_management/components/design_scaler_spec.js b/spec/frontend/design_management/components/design_scaler_spec.js
index e1a66cea329..b29448b4471 100644
--- a/spec/frontend/design_management/components/design_scaler_spec.js
+++ b/spec/frontend/design_management/components/design_scaler_spec.js
@@ -25,11 +25,6 @@ describe('Design management design scaler component', () => {
createComponent();
});
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
describe('when `scale` value is greater than 1', () => {
beforeEach(async () => {
setScale(1.6);
@@ -41,7 +36,7 @@ describe('Design management design scaler component', () => {
expect(wrapper.emitted('scale')[1]).toEqual([1]);
});
- it('emits @scale event when "decrement" button clicked', async () => {
+ it('emits @scale event when "decrement" button clicked', () => {
getDecreaseScaleButton().vm.$emit('click');
expect(wrapper.emitted('scale')[1]).toEqual([1.4]);
});
diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js
index af995f75ddc..90424175417 100644
--- a/spec/frontend/design_management/components/design_sidebar_spec.js
+++ b/spec/frontend/design_management/components/design_sidebar_spec.js
@@ -29,7 +29,6 @@ const $route = {
const mutate = jest.fn().mockResolvedValue();
describe('Design management design sidebar component', () => {
- const originalGon = window.gon;
let wrapper;
const findDiscussions = () => wrapper.findAllComponents(DesignDiscussion);
@@ -67,11 +66,6 @@ describe('Design management design sidebar component', () => {
window.gon = { current_user_id: 1 };
});
- afterEach(() => {
- wrapper.destroy();
- window.gon = originalGon;
- });
-
it('renders participants', () => {
createComponent();
@@ -143,8 +137,8 @@ describe('Design management design sidebar component', () => {
expect(findResolvedCommentsToggle().props('visible')).toBe(true);
});
- it('sends a mutation to set an active discussion when clicking on a discussion', () => {
- findFirstDiscussion().trigger('click');
+ it('emits correct event to send a mutation to set an active discussion when clicking on a discussion', () => {
+ findFirstDiscussion().vm.$emit('update-active-discussion');
expect(mutate).toHaveBeenCalledWith(updateActiveDiscussionMutationVariables);
});
diff --git a/spec/frontend/design_management/components/design_todo_button_spec.js b/spec/frontend/design_management/components/design_todo_button_spec.js
index ac26873b692..698535d8937 100644
--- a/spec/frontend/design_management/components/design_todo_button_spec.js
+++ b/spec/frontend/design_management/components/design_todo_button_spec.js
@@ -51,8 +51,6 @@ describe('Design management design todo button', () => {
});
afterEach(() => {
- wrapper.destroy();
- wrapper = null;
jest.clearAllMocks();
});
@@ -83,7 +81,7 @@ describe('Design management design todo button', () => {
await nextTick();
});
- it('calls `$apollo.mutate` with the `todoMarkDone` mutation and variables containing `id`', async () => {
+ it('calls `$apollo.mutate` with the `todoMarkDone` mutation and variables containing `id`', () => {
const todoMarkDoneMutationVariables = {
mutation: todoMarkDoneMutation,
update: expect.anything(),
@@ -129,7 +127,7 @@ describe('Design management design todo button', () => {
await nextTick();
});
- it('calls `$apollo.mutate` with the `createDesignTodoMutation` mutation and variables containing `issuable_id`, `issue_id`, & `projectPath`', async () => {
+ it('calls `$apollo.mutate` with the `createDesignTodoMutation` mutation and variables containing `issuable_id`, `issue_id`, & `projectPath`', () => {
const createDesignTodoMutationVariables = {
mutation: createDesignTodoMutation,
update: expect.anything(),
diff --git a/spec/frontend/design_management/components/image_spec.js b/spec/frontend/design_management/components/image_spec.js
index 95d2ad504de..53abcc559d8 100644
--- a/spec/frontend/design_management/components/image_spec.js
+++ b/spec/frontend/design_management/components/image_spec.js
@@ -20,10 +20,6 @@ describe('Design management large image component', () => {
stubPerformanceWebAPI();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders loading state', () => {
createComponent({
isLoading: true,
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 3517c0f7a44..9451f35ac5b 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
@@ -38,13 +38,13 @@ exports[`Design management list item component with notes renders item with mult
</div>
<div
- class="card-footer gl-display-flex gl-w-full"
+ class="card-footer gl-display-flex gl-w-full gl-bg-white gl-py-3 gl-px-4"
>
<div
class="gl-display-flex gl-flex-direction-column str-truncated-100"
>
<span
- class="gl-font-weight-bold str-truncated-100"
+ class="gl-font-weight-semibold str-truncated-100"
data-qa-selector="design_file_name"
data-testid="design-img-filename-1"
title="test"
@@ -59,6 +59,7 @@ exports[`Design management list item component with notes renders item with mult
Updated
<timeago-stub
cssclass=""
+ datetimeformat="DATE_WITH_TIME_FORMAT"
time="01-01-2019"
tooltipplacement="bottom"
/>
@@ -117,13 +118,13 @@ exports[`Design management list item component with notes renders item with sing
</div>
<div
- class="card-footer gl-display-flex gl-w-full"
+ class="card-footer gl-display-flex gl-w-full gl-bg-white gl-py-3 gl-px-4"
>
<div
class="gl-display-flex gl-flex-direction-column str-truncated-100"
>
<span
- class="gl-font-weight-bold str-truncated-100"
+ class="gl-font-weight-semibold str-truncated-100"
data-qa-selector="design_file_name"
data-testid="design-img-filename-1"
title="test"
@@ -138,6 +139,7 @@ exports[`Design management list item component with notes renders item with sing
Updated
<timeago-stub
cssclass=""
+ datetimeformat="DATE_WITH_TIME_FORMAT"
time="01-01-2019"
tooltipplacement="bottom"
/>
diff --git a/spec/frontend/design_management/components/list/item_spec.js b/spec/frontend/design_management/components/list/item_spec.js
index e907e2e4ac5..4a0ad5a045b 100644
--- a/spec/frontend/design_management/components/list/item_spec.js
+++ b/spec/frontend/design_management/components/list/item_spec.js
@@ -54,10 +54,6 @@ describe('Design management list item component', () => {
);
}
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('when item is not in view', () => {
it('image is not rendered', () => {
createComponent();
diff --git a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap b/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap
deleted file mode 100644
index b5a69b28a88..00000000000
--- a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Design management pagination component hides components when designs are empty 1`] = `<!---->`;
-
-exports[`Design management pagination component renders navigation buttons 1`] = `
-<div
- class="gl-display-flex gl-align-items-center"
->
-
- 0 of 2
-
- <gl-button-group-stub
- class="gl-mx-5"
- >
- <gl-button-stub
- aria-label="Go to previous design"
- buttontextclasses=""
- category="primary"
- class="js-previous-design"
- disabled="true"
- icon="chevron-lg-left"
- size="medium"
- title="Go to previous design"
- variant="default"
- />
-
- <gl-button-stub
- aria-label="Go to next design"
- buttontextclasses=""
- category="primary"
- class="js-next-design"
- icon="chevron-lg-right"
- size="medium"
- title="Go to next design"
- variant="default"
- />
- </gl-button-group-stub>
-</div>
-`;
diff --git a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
index 38a7fadee79..cee05bafeb6 100644
--- a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
+++ b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
@@ -1,9 +1,17 @@
-/* global Mousetrap */
-import 'mousetrap';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlButtonGroup } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
import DesignNavigation from '~/design_management/components/toolbar/design_navigation.vue';
import { DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
+import { Mousetrap } from '~/lib/mousetrap';
+import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import {
+ getDesignListQueryResponse,
+ designListQueryResponseNodes,
+} from '../../mock_data/apollo_mock';
const push = jest.fn();
const $router = {
@@ -18,11 +26,23 @@ const $route = {
describe('Design management pagination component', () => {
let wrapper;
- function createComponent() {
+ const buildMockHandler = (nodes = designListQueryResponseNodes) => {
+ return jest.fn().mockResolvedValue(getDesignListQueryResponse({ designs: nodes }));
+ };
+
+ const createMockApolloProvider = (handler) => {
+ Vue.use(VueApollo);
+
+ return createMockApollo([[getDesignListQuery, handler]]);
+ };
+
+ function createComponent({ propsData = {}, handler = buildMockHandler() } = {}) {
wrapper = shallowMount(DesignNavigation, {
propsData: {
id: '2',
+ ...propsData,
},
+ apolloProvider: createMockApolloProvider(handler),
mocks: {
$router,
$route,
@@ -30,52 +50,43 @@ describe('Design management pagination component', () => {
});
}
- beforeEach(() => {
- createComponent();
- });
+ const findGlButtonGroup = () => wrapper.findComponent(GlButtonGroup);
- afterEach(() => {
- wrapper.destroy();
- });
+ it('hides components when designs are empty', async () => {
+ createComponent({ handler: buildMockHandler([]) });
+ await waitForPromises();
- it('hides components when designs are empty', () => {
- expect(wrapper.element).toMatchSnapshot();
+ expect(findGlButtonGroup().exists()).toBe(false);
});
it('renders navigation buttons', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- designCollection: { designs: [{ id: '1' }, { id: '2' }] },
- });
+ createComponent({ handler: buildMockHandler() });
+ await waitForPromises();
- await nextTick();
- expect(wrapper.element).toMatchSnapshot();
+ expect(findGlButtonGroup().exists()).toBe(true);
});
describe('keyboard buttons navigation', () => {
- beforeEach(() => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- designCollection: { designs: [{ filename: '1' }, { filename: '2' }, { filename: '3' }] },
- });
- });
+ it('routes to previous design on Left button', async () => {
+ createComponent({ propsData: { id: designListQueryResponseNodes[1].filename } });
+ await waitForPromises();
- it('routes to previous design on Left button', () => {
Mousetrap.trigger('left');
expect(push).toHaveBeenCalledWith({
name: DESIGN_ROUTE_NAME,
- params: { id: '1' },
+ params: { id: designListQueryResponseNodes[0].filename },
query: {},
});
});
- it('routes to next design on Right button', () => {
+ it('routes to next design on Right button', async () => {
+ createComponent({ propsData: { id: designListQueryResponseNodes[1].filename } });
+ await waitForPromises();
+
Mousetrap.trigger('right');
expect(push).toHaveBeenCalledWith({
name: DESIGN_ROUTE_NAME,
- params: { id: '3' },
+ params: { id: designListQueryResponseNodes[2].filename },
query: {},
});
});
diff --git a/spec/frontend/design_management/components/toolbar/index_spec.js b/spec/frontend/design_management/components/toolbar/index_spec.js
index 1776405ece9..764ad73805f 100644
--- a/spec/frontend/design_management/components/toolbar/index_spec.js
+++ b/spec/frontend/design_management/components/toolbar/index_spec.js
@@ -1,12 +1,18 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import DeleteButton from '~/design_management/components/delete_button.vue';
import Toolbar from '~/design_management/components/toolbar/index.vue';
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
+import { getPermissionsQueryResponse } from '../../mock_data/apollo_mock';
Vue.use(VueRouter);
+Vue.use(VueApollo);
const router = new VueRouter();
const RouterLinkStub = {
@@ -27,7 +33,12 @@ describe('Design management toolbar component', () => {
const updatedAt = new Date();
updatedAt.setHours(updatedAt.getHours() - 1);
+ const mockApollo = createMockApollo([
+ [permissionsQuery, jest.fn().mockResolvedValue(getPermissionsQueryResponse(createDesign))],
+ ]);
+
wrapper = shallowMount(Toolbar, {
+ apolloProvider: mockApollo,
router,
propsData: {
id: '1',
@@ -46,31 +57,20 @@ describe('Design management toolbar component', () => {
'router-link': RouterLinkStub,
},
});
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- permissions: {
- createDesign,
- },
- });
}
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders design and updated data', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
+
expect(wrapper.element).toMatchSnapshot();
});
it('links back to designs list', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
const link = wrapper.find('a');
expect(link.props('to')).toEqual({
@@ -84,35 +84,41 @@ describe('Design management toolbar component', () => {
it('renders delete button on latest designs version with logged in user', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
+
expect(wrapper.findComponent(DeleteButton).exists()).toBe(true);
});
it('does not render delete button on non-latest version', async () => {
createComponent(false, true, { isLatestVersion: false });
- await nextTick();
+ await waitForPromises();
+
expect(wrapper.findComponent(DeleteButton).exists()).toBe(false);
});
it('does not render delete button when user is not logged in', async () => {
createComponent(false, false);
- await nextTick();
+ await waitForPromises();
+
expect(wrapper.findComponent(DeleteButton).exists()).toBe(false);
});
it('emits `delete` event on deleteButton `delete-selected-designs` event', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
+
wrapper.findComponent(DeleteButton).vm.$emit('delete-selected-designs');
expect(wrapper.emitted().delete).toHaveLength(1);
});
- it('renders download button with correct link', () => {
+ it('renders download button with correct link', async () => {
createComponent();
+ await waitForPromises();
+
expect(wrapper.findComponent(GlButton).attributes('href')).toBe(
'/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d',
);
diff --git a/spec/frontend/design_management/components/upload/button_spec.js b/spec/frontend/design_management/components/upload/button_spec.js
index 59821218ab8..ceae7920e0d 100644
--- a/spec/frontend/design_management/components/upload/button_spec.js
+++ b/spec/frontend/design_management/components/upload/button_spec.js
@@ -14,10 +14,6 @@ describe('Design management upload button component', () => {
});
}
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders upload design button', () => {
createComponent();
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 6ad10e707ab..3ee68f80538 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,9 +1,14 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { GlAvatar, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import DesignVersionDropdown from '~/design_management/components/upload/design_version_dropdown.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import mockAllVersions from './mock_data/all_versions';
+import mockAllVersions from '../../mock_data/all_versions';
+import { getDesignListQueryResponse } from '../../mock_data/apollo_mock';
const LATEST_VERSION_ID = 1;
const PREVIOUS_VERSION_ID = 2;
@@ -20,11 +25,20 @@ const MOCK_ROUTE = {
query: {},
};
+Vue.use(VueApollo);
+
describe('Design management design version dropdown component', () => {
let wrapper;
function createComponent({ maxVersions = -1, $route = MOCK_ROUTE } = {}) {
+ const designVersions =
+ maxVersions > -1 ? mockAllVersions.slice(0, maxVersions) : mockAllVersions;
+ const designListHandler = jest
+ .fn()
+ .mockResolvedValue(getDesignListQueryResponse({ versions: designVersions }));
+
wrapper = shallowMount(DesignVersionDropdown, {
+ apolloProvider: createMockApollo([[getDesignListQuery, designListHandler]]),
propsData: {
projectPath: '',
issueIid: '',
@@ -34,18 +48,8 @@ describe('Design management design version dropdown component', () => {
},
stubs: { GlAvatar: true, GlCollapsibleListbox },
});
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- allVersions: maxVersions > -1 ? mockAllVersions.slice(0, maxVersions) : mockAllVersions,
- });
}
- afterEach(() => {
- wrapper.destroy();
- });
-
const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
const findAllListboxItems = () => wrapper.findAllComponents(GlListboxItem);
const findVersionLink = (index) => wrapper.findAllComponents(GlListboxItem).at(index);
@@ -56,7 +60,7 @@ describe('Design management design version dropdown component', () => {
beforeEach(async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
listItem = findAllListboxItems().at(0);
});
@@ -78,7 +82,8 @@ describe('Design management design version dropdown component', () => {
it('has "latest" on most recent version item', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
+
expect(findVersionLink(0).text()).toContain('latest');
});
});
@@ -87,7 +92,7 @@ describe('Design management design version dropdown component', () => {
it('displays latest version text by default', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
@@ -95,35 +100,39 @@ describe('Design management design version dropdown component', () => {
it('displays latest version text when only 1 version is present', async () => {
createComponent({ maxVersions: 1 });
- await nextTick();
+ await waitForPromises();
+
expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
it('displays version text when the current version is not the latest', async () => {
createComponent({ $route: designRouteFactory(PREVIOUS_VERSION_ID) });
- await nextTick();
+ await waitForPromises();
+
expect(findListbox().props('toggleText')).toBe(`Showing version #1`);
});
it('displays latest version text when the current version is the latest', async () => {
createComponent({ $route: designRouteFactory(LATEST_VERSION_ID) });
- await nextTick();
+ await waitForPromises();
+
expect(findListbox().props('toggleText')).toBe('Showing latest version');
});
it('should have the same length as apollo query', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
+
expect(findAllListboxItems()).toHaveLength(wrapper.vm.allVersions.length);
});
it('should render TimeAgo', async () => {
createComponent();
- await nextTick();
+ await waitForPromises();
expect(wrapper.findAllComponents(TimeAgo)).toHaveLength(wrapper.vm.allVersions.length);
});
diff --git a/spec/frontend/design_management/components/upload/mock_data/all_versions.js b/spec/frontend/design_management/components/upload/mock_data/all_versions.js
deleted file mode 100644
index 24c59ce1a75..00000000000
--- a/spec/frontend/design_management/components/upload/mock_data/all_versions.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export default [
- {
- id: 'gid://gitlab/DesignManagement::Version/1',
- sha: 'b389071a06c153509e11da1f582005b316667001',
- createdAt: '2021-08-09T06:05:00Z',
- author: {
- id: 'gid://gitlab/User/1',
- name: 'Adminstrator',
- },
- },
- {
- id: 'gid://gitlab/DesignManagement::Version/2',
- sha: 'b389071a06c153509e11da1f582005b316667021',
- createdAt: '2021-08-09T06:05:00Z',
- author: {
- id: 'gid://gitlab/User/1',
- name: 'Adminstrator',
- },
- },
-];