diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/frontend/design_management_legacy | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/frontend/design_management_legacy')
46 files changed, 0 insertions, 6243 deletions
diff --git a/spec/frontend/design_management_legacy/components/__snapshots__/design_note_pin_spec.js.snap b/spec/frontend/design_management_legacy/components/__snapshots__/design_note_pin_spec.js.snap deleted file mode 100644 index 62a0f675cff..00000000000 --- a/spec/frontend/design_management_legacy/components/__snapshots__/design_note_pin_spec.js.snap +++ /dev/null @@ -1,42 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design note pin component should match the snapshot of note when repositioning 1`] = ` -<button - aria-label="Comment form position" - class="design-pin gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-0 btn-transparent comment-indicator" - style="left: 10px; top: 10px; cursor: move;" - type="button" -> - <gl-icon-stub - name="image-comment-dark" - size="24" - /> -</button> -`; - -exports[`Design note pin component should match the snapshot of note with index 1`] = ` -<button - aria-label="Comment '1' position" - class="design-pin gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-0 js-image-badge badge badge-pill" - style="left: 10px; top: 10px;" - type="button" -> - - 1 - -</button> -`; - -exports[`Design note pin component should match the snapshot of note without index 1`] = ` -<button - aria-label="Comment form position" - class="design-pin gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-0 btn-transparent comment-indicator" - style="left: 10px; top: 10px;" - type="button" -> - <gl-icon-stub - name="image-comment-dark" - size="24" - /> -</button> -`; diff --git a/spec/frontend/design_management_legacy/components/__snapshots__/design_presentation_spec.js.snap b/spec/frontend/design_management_legacy/components/__snapshots__/design_presentation_spec.js.snap deleted file mode 100644 index 189962c5b2e..00000000000 --- a/spec/frontend/design_management_legacy/components/__snapshots__/design_presentation_spec.js.snap +++ /dev/null @@ -1,104 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management design presentation component currentCommentForm is equal to current annotation position when isAnnotating is true 1`] = ` -<div - class="h-100 w-100 p-3 overflow-auto position-relative" -> - <div - class="h-100 w-100 d-flex align-items-center position-relative" - > - <design-image-stub - image="test.jpg" - name="test" - scale="1" - /> - - <design-overlay-stub - currentcommentform="[object Object]" - dimensions="[object Object]" - notes="" - position="[object Object]" - /> - </div> -</div> -`; - -exports[`Design management design presentation component currentCommentForm is null when isAnnotating is false 1`] = ` -<div - class="h-100 w-100 p-3 overflow-auto position-relative" -> - <div - class="h-100 w-100 d-flex align-items-center position-relative" - > - <design-image-stub - image="test.jpg" - name="test" - scale="1" - /> - - <design-overlay-stub - dimensions="[object Object]" - notes="" - position="[object Object]" - /> - </div> -</div> -`; - -exports[`Design management design presentation component currentCommentForm is null when isAnnotating is true but annotation position is falsey 1`] = ` -<div - class="h-100 w-100 p-3 overflow-auto position-relative" -> - <div - class="h-100 w-100 d-flex align-items-center position-relative" - > - <design-image-stub - image="test.jpg" - name="test" - scale="1" - /> - - <design-overlay-stub - dimensions="[object Object]" - notes="" - position="[object Object]" - /> - </div> -</div> -`; - -exports[`Design management design presentation component renders empty state when no image provided 1`] = ` -<div - class="h-100 w-100 p-3 overflow-auto position-relative" -> - <div - class="h-100 w-100 d-flex align-items-center position-relative" - > - <!----> - - <!----> - </div> -</div> -`; - -exports[`Design management design presentation component renders image and overlay when image provided 1`] = ` -<div - class="h-100 w-100 p-3 overflow-auto position-relative" -> - <div - class="h-100 w-100 d-flex align-items-center position-relative" - > - <design-image-stub - image="test.jpg" - name="test" - scale="1" - /> - - <design-overlay-stub - dimensions="[object Object]" - notes="" - position="[object Object]" - /> - </div> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/__snapshots__/design_scaler_spec.js.snap b/spec/frontend/design_management_legacy/components/__snapshots__/design_scaler_spec.js.snap deleted file mode 100644 index cb4575cbd11..00000000000 --- a/spec/frontend/design_management_legacy/components/__snapshots__/design_scaler_spec.js.snap +++ /dev/null @@ -1,115 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management design scaler component minus and reset buttons are disabled when scale === 1 1`] = ` -<div - class="design-scaler btn-group" - role="group" -> - <button - class="btn" - disabled="disabled" - > - <span - class="d-flex-center gl-icon s16" - > - - – - - </span> - </button> - - <button - class="btn" - disabled="disabled" - > - <gl-icon-stub - name="redo" - size="16" - /> - </button> - - <button - class="btn" - > - <gl-icon-stub - name="plus" - size="16" - /> - </button> -</div> -`; - -exports[`Design management design scaler component minus and reset buttons are enabled when scale > 1 1`] = ` -<div - class="design-scaler btn-group" - role="group" -> - <button - class="btn" - > - <span - class="d-flex-center gl-icon s16" - > - - – - - </span> - </button> - - <button - class="btn" - > - <gl-icon-stub - name="redo" - size="16" - /> - </button> - - <button - class="btn" - > - <gl-icon-stub - name="plus" - size="16" - /> - </button> -</div> -`; - -exports[`Design management design scaler component plus button is disabled when scale === 2 1`] = ` -<div - class="design-scaler btn-group" - role="group" -> - <button - class="btn" - > - <span - class="d-flex-center gl-icon s16" - > - - – - - </span> - </button> - - <button - class="btn" - > - <gl-icon-stub - name="redo" - size="16" - /> - </button> - - <button - class="btn" - disabled="disabled" - > - <gl-icon-stub - name="plus" - size="16" - /> - </button> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/__snapshots__/image_spec.js.snap b/spec/frontend/design_management_legacy/components/__snapshots__/image_spec.js.snap deleted file mode 100644 index acaa62b11eb..00000000000 --- a/spec/frontend/design_management_legacy/components/__snapshots__/image_spec.js.snap +++ /dev/null @@ -1,68 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management large image component renders image 1`] = ` -<div - class="m-auto js-design-image" -> - <!----> - - <img - alt="test" - class="mh-100 img-fluid" - src="test.jpg" - /> -</div> -`; - -exports[`Design management large image component renders loading state 1`] = ` -<div - class="m-auto js-design-image" - isloading="true" -> - <!----> - - <img - alt="" - class="mh-100 img-fluid" - src="" - /> -</div> -`; - -exports[`Design management large image component renders media broken icon on error 1`] = ` -<gl-icon-stub - class="text-secondary-100" - name="media-broken" - size="48" -/> -`; - -exports[`Design management large image component sets correct classes and styles if imageStyle is set 1`] = ` -<div - class="m-auto js-design-image" -> - <!----> - - <img - alt="test" - class="mh-100" - src="test.jpg" - style="width: 100px; height: 100px;" - /> -</div> -`; - -exports[`Design management large image component zoom sets image style when zoomed 1`] = ` -<div - class="m-auto js-design-image" -> - <!----> - - <img - alt="test" - class="mh-100" - src="test.jpg" - style="width: 200px; height: 200px;" - /> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/delete_button_spec.js b/spec/frontend/design_management_legacy/components/delete_button_spec.js deleted file mode 100644 index 73b4908d06a..00000000000 --- a/spec/frontend/design_management_legacy/components/delete_button_spec.js +++ /dev/null @@ -1,51 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlDeprecatedButton, GlModal, GlModalDirective } from '@gitlab/ui'; -import BatchDeleteButton from '~/design_management_legacy/components/delete_button.vue'; - -describe('Batch delete button component', () => { - let wrapper; - - const findButton = () => wrapper.find(GlDeprecatedButton); - const findModal = () => wrapper.find(GlModal); - - function createComponent(isDeleting = false) { - wrapper = shallowMount(BatchDeleteButton, { - propsData: { - isDeleting, - }, - directives: { - GlModalDirective, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders non-disabled button by default', () => { - createComponent(); - - expect(findButton().exists()).toBe(true); - expect(findButton().attributes('disabled')).toBeFalsy(); - }); - - it('renders disabled button when design is deleting', () => { - createComponent(true); - expect(findButton().attributes('disabled')).toBeTruthy(); - }); - - it('emits `deleteSelectedDesigns` event on modal ok click', () => { - createComponent(); - findButton().vm.$emit('click'); - return wrapper.vm - .$nextTick() - .then(() => { - findModal().vm.$emit('ok'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.emitted().deleteSelectedDesigns).toBeTruthy(); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_note_pin_spec.js b/spec/frontend/design_management_legacy/components/design_note_pin_spec.js deleted file mode 100644 index 3077928cf86..00000000000 --- a/spec/frontend/design_management_legacy/components/design_note_pin_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DesignNotePin from '~/design_management_legacy/components/design_note_pin.vue'; - -describe('Design note pin component', () => { - let wrapper; - - function createComponent(propsData = {}) { - wrapper = shallowMount(DesignNotePin, { - propsData: { - position: { - left: '10px', - top: '10px', - }, - ...propsData, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('should match the snapshot of note without index', () => { - createComponent(); - expect(wrapper.element).toMatchSnapshot(); - }); - - it('should match the snapshot of note with index', () => { - createComponent({ label: 1 }); - expect(wrapper.element).toMatchSnapshot(); - }); - - it('should match the snapshot of note when repositioning', () => { - createComponent({ repositioning: true }); - expect(wrapper.element).toMatchSnapshot(); - }); - - describe('pinStyle', () => { - it('sets cursor to `move` when repositioning = true', () => { - createComponent({ repositioning: true }); - expect(wrapper.vm.pinStyle.cursor).toBe('move'); - }); - - it('does not set cursor when repositioning = false', () => { - createComponent(); - expect(wrapper.vm.pinStyle.cursor).toBe(undefined); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_note_spec.js.snap b/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_note_spec.js.snap deleted file mode 100644 index b55bacb6fc5..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_note_spec.js.snap +++ /dev/null @@ -1,67 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design note component should match the snapshot 1`] = ` -<timeline-entry-item-stub - class="design-note note-form" - id="note_123" -> - <user-avatar-link-stub - imgalt="" - imgcssclasses="" - imgsize="40" - imgsrc="" - linkhref="" - tooltipplacement="top" - tooltiptext="" - username="" - /> - - <div - class="d-flex justify-content-between" - > - <div> - <a - class="js-user-link" - data-user-id="author-id" - > - <span - class="note-header-author-name bold" - > - - </span> - - <!----> - - <span - class="note-headline-light" - > - @ - </span> - </a> - - <span - class="note-headline-light note-headline-meta" - > - <span - class="system-note-message" - /> - - <!----> - </span> - </div> - - <div - class="gl-display-flex" - > - - <!----> - </div> - </div> - - <div - class="note-text js-note-text md" - data-qa-selector="note_content" - /> - -</timeline-entry-item-stub> -`; diff --git a/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_reply_form_spec.js.snap b/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_reply_form_spec.js.snap deleted file mode 100644 index e01c79e3520..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/__snapshots__/design_reply_form_spec.js.snap +++ /dev/null @@ -1,15 +0,0 @@ -// 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\\"> - <!----> - Comment -</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\\"> - <!----> - Save comment -</button>" -`; diff --git a/spec/frontend/design_management_legacy/components/design_notes/design_discussion_spec.js b/spec/frontend/design_management_legacy/components/design_notes/design_discussion_spec.js deleted file mode 100644 index d20be97f470..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/design_discussion_spec.js +++ /dev/null @@ -1,318 +0,0 @@ -import { mount } from '@vue/test-utils'; -import { GlLoadingIcon } from '@gitlab/ui'; -import notes from '../../mock_data/notes'; -import DesignDiscussion from '~/design_management_legacy/components/design_notes/design_discussion.vue'; -import DesignNote from '~/design_management_legacy/components/design_notes/design_note.vue'; -import DesignReplyForm from '~/design_management_legacy/components/design_notes/design_reply_form.vue'; -import createNoteMutation from '~/design_management_legacy/graphql/mutations/create_note.mutation.graphql'; -import toggleResolveDiscussionMutation from '~/design_management_legacy/graphql/mutations/toggle_resolve_discussion.mutation.graphql'; -import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; -import ToggleRepliesWidget from '~/design_management_legacy/components/design_notes/toggle_replies_widget.vue'; - -const discussion = { - id: '0', - resolved: false, - resolvable: true, - notes, -}; - -describe('Design discussions component', () => { - let wrapper; - - const findDesignNotes = () => wrapper.findAll(DesignNote); - const findReplyPlaceholder = () => wrapper.find(ReplyPlaceholder); - const findReplyForm = () => wrapper.find(DesignReplyForm); - const findRepliesWidget = () => wrapper.find(ToggleRepliesWidget); - const findResolveButton = () => wrapper.find('[data-testid="resolve-button"]'); - const findResolveIcon = () => wrapper.find('[data-testid="resolve-icon"]'); - const findResolvedMessage = () => wrapper.find('[data-testid="resolved-message"]'); - const findResolveLoadingIcon = () => wrapper.find(GlLoadingIcon); - const findResolveCheckbox = () => wrapper.find('[data-testid="resolve-checkbox"]'); - - const mutationVariables = { - mutation: createNoteMutation, - update: expect.anything(), - variables: { - input: { - noteableId: 'noteable-id', - body: 'test', - discussionId: '0', - }, - }, - }; - const mutate = jest.fn(() => Promise.resolve()); - const $apollo = { - mutate, - }; - - function createComponent(props = {}, data = {}) { - wrapper = mount(DesignDiscussion, { - propsData: { - resolvedDiscussionsExpanded: true, - discussion, - noteableId: 'noteable-id', - designId: 'design-id', - discussionIndex: 1, - discussionWithOpenForm: '', - ...props, - }, - data() { - return { - ...data, - }; - }, - mocks: { - $apollo, - $route: { - hash: '#note_1', - }, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when discussion is not resolvable', () => { - beforeEach(() => { - createComponent({ - discussion: { - ...discussion, - resolvable: false, - }, - }); - }); - - it('does not render an icon to resolve a thread', () => { - expect(findResolveIcon().exists()).toBe(false); - }); - - it('does not render a checkbox in reply form', () => { - findReplyPlaceholder().vm.$emit('onMouseDown'); - - return wrapper.vm.$nextTick().then(() => { - expect(findResolveCheckbox().exists()).toBe(false); - }); - }); - }); - - describe('when discussion is unresolved', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders correct amount of discussion notes', () => { - expect(findDesignNotes()).toHaveLength(2); - expect(findDesignNotes().wrappers.every(w => w.isVisible())).toBe(true); - }); - - it('renders reply placeholder', () => { - expect(findReplyPlaceholder().isVisible()).toBe(true); - }); - - it('does not render toggle replies widget', () => { - expect(findRepliesWidget().exists()).toBe(false); - }); - - it('renders a correct icon to resolve a thread', () => { - expect(findResolveIcon().props('name')).toBe('check-circle'); - }); - - it('renders a checkbox with Resolve thread text in reply form', () => { - findReplyPlaceholder().vm.$emit('onClick'); - wrapper.setProps({ discussionWithOpenForm: discussion.id }); - - return wrapper.vm.$nextTick().then(() => { - expect(findResolveCheckbox().text()).toBe('Resolve thread'); - }); - }); - - it('does not render resolved message', () => { - expect(findResolvedMessage().exists()).toBe(false); - }); - }); - - describe('when discussion is resolved', () => { - beforeEach(() => { - createComponent({ - discussion: { - ...discussion, - resolved: true, - resolvedBy: notes[0].author, - resolvedAt: '2020-05-08T07:10:45Z', - }, - }); - }); - - it('shows only the first note', () => { - expect( - findDesignNotes() - .at(0) - .isVisible(), - ).toBe(true); - expect( - findDesignNotes() - .at(1) - .isVisible(), - ).toBe(false); - }); - - it('renders resolved message', () => { - expect(findResolvedMessage().exists()).toBe(true); - }); - - it('does not show renders reply placeholder', () => { - expect(findReplyPlaceholder().isVisible()).toBe(false); - }); - - it('renders toggle replies widget with correct props', () => { - expect(findRepliesWidget().exists()).toBe(true); - expect(findRepliesWidget().props()).toEqual({ - collapsed: true, - replies: notes.slice(1), - }); - }); - - it('renders a correct icon to resolve a thread', () => { - expect(findResolveIcon().props('name')).toBe('check-circle-filled'); - }); - - describe('when replies are expanded', () => { - beforeEach(() => { - findRepliesWidget().vm.$emit('toggle'); - return wrapper.vm.$nextTick(); - }); - - it('renders replies widget with collapsed prop equal to false', () => { - expect(findRepliesWidget().props('collapsed')).toBe(false); - }); - - it('renders the second note', () => { - expect( - findDesignNotes() - .at(1) - .isVisible(), - ).toBe(true); - }); - - it('renders a reply placeholder', () => { - expect(findReplyPlaceholder().isVisible()).toBe(true); - }); - - it('renders a checkbox with Unresolve thread text in reply form', () => { - findReplyPlaceholder().vm.$emit('onClick'); - wrapper.setProps({ discussionWithOpenForm: discussion.id }); - - return wrapper.vm.$nextTick().then(() => { - expect(findResolveCheckbox().text()).toBe('Unresolve thread'); - }); - }); - }); - }); - - it('hides reply placeholder and opens form on placeholder click', () => { - createComponent(); - findReplyPlaceholder().vm.$emit('onClick'); - wrapper.setProps({ discussionWithOpenForm: discussion.id }); - - return wrapper.vm.$nextTick().then(() => { - expect(findReplyPlaceholder().exists()).toBe(false); - expect(findReplyForm().exists()).toBe(true); - }); - }); - - it('calls mutation on submitting form and closes the form', () => { - createComponent( - { discussionWithOpenForm: discussion.id }, - { discussionComment: 'test', isFormRendered: true }, - ); - - findReplyForm().vm.$emit('submitForm'); - expect(mutate).toHaveBeenCalledWith(mutationVariables); - - return mutate() - .then(() => { - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(findReplyForm().exists()).toBe(false); - }); - }); - - it('clears the discussion comment on closing comment form', () => { - createComponent( - { discussionWithOpenForm: discussion.id }, - { discussionComment: 'test', isFormRendered: true }, - ); - - return wrapper.vm - .$nextTick() - .then(() => { - findReplyForm().vm.$emit('cancelForm'); - - expect(wrapper.vm.discussionComment).toBe(''); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(findReplyForm().exists()).toBe(false); - }); - }); - - it('applies correct class to design notes when discussion is highlighted', () => { - createComponent( - {}, - { - activeDiscussion: { - id: notes[0].id, - source: 'pin', - }, - }, - ); - - expect(wrapper.findAll(DesignNote).wrappers.every(note => note.classes('gl-bg-blue-50'))).toBe( - true, - ); - }); - - it('calls toggleResolveDiscussion mutation on resolve thread button click', () => { - createComponent(); - findResolveButton().trigger('click'); - expect(mutate).toHaveBeenCalledWith({ - mutation: toggleResolveDiscussionMutation, - variables: { - id: discussion.id, - resolve: true, - }, - }); - return wrapper.vm.$nextTick(() => { - expect(findResolveLoadingIcon().exists()).toBe(true); - }); - }); - - it('calls toggleResolveDiscussion mutation after adding a note if checkbox was checked', () => { - createComponent( - { discussionWithOpenForm: discussion.id }, - { discussionComment: 'test', isFormRendered: true }, - ); - findResolveButton().trigger('click'); - findReplyForm().vm.$emit('submitForm'); - - return mutate().then(() => { - expect(mutate).toHaveBeenCalledWith({ - mutation: toggleResolveDiscussionMutation, - variables: { - id: discussion.id, - resolve: true, - }, - }); - }); - }); - - it('emits openForm event on opening the form', () => { - createComponent(); - findReplyPlaceholder().vm.$emit('onClick'); - - expect(wrapper.emitted('openForm')).toBeTruthy(); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_notes/design_note_spec.js b/spec/frontend/design_management_legacy/components/design_notes/design_note_spec.js deleted file mode 100644 index aa187cd1388..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/design_note_spec.js +++ /dev/null @@ -1,170 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { ApolloMutation } from 'vue-apollo'; -import DesignNote from '~/design_management_legacy/components/design_notes/design_note.vue'; -import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; -import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import DesignReplyForm from '~/design_management_legacy/components/design_notes/design_reply_form.vue'; - -const scrollIntoViewMock = jest.fn(); -const note = { - id: 'gid://gitlab/DiffNote/123', - author: { - id: 'author-id', - }, - body: 'test', - userPermissions: { - adminNote: false, - }, -}; -HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; - -const $route = { - hash: '#note_123', -}; - -const mutate = jest.fn().mockResolvedValue({ data: { updateNote: {} } }); - -describe('Design note component', () => { - let wrapper; - - const findUserAvatar = () => wrapper.find(UserAvatarLink); - const findUserLink = () => wrapper.find('.js-user-link'); - const findReplyForm = () => wrapper.find(DesignReplyForm); - const findEditButton = () => wrapper.find('.js-note-edit'); - const findNoteContent = () => wrapper.find('.js-note-text'); - - function createComponent(props = {}, data = { isEditing: false }) { - wrapper = shallowMount(DesignNote, { - propsData: { - note: {}, - ...props, - }, - data() { - return { - ...data, - }; - }, - mocks: { - $route, - $apollo: { - mutate, - }, - }, - stubs: { - ApolloMutation, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('should match the snapshot', () => { - createComponent({ - note, - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('should render an author', () => { - createComponent({ - note, - }); - - expect(findUserAvatar().exists()).toBe(true); - expect(findUserLink().exists()).toBe(true); - }); - - 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(); - }); - - it('should not render edit icon when user does not have a permission', () => { - createComponent({ - note, - }); - - expect(findEditButton().exists()).toBe(false); - }); - - describe('when user has a permission to edit note', () => { - it('should open an edit form on edit button click', () => { - createComponent({ - note: { - ...note, - userPermissions: { - adminNote: true, - }, - }, - }); - - findEditButton().trigger('click'); - - return wrapper.vm.$nextTick().then(() => { - expect(findReplyForm().exists()).toBe(true); - expect(findNoteContent().exists()).toBe(false); - }); - }); - - describe('when edit form is rendered', () => { - beforeEach(() => { - createComponent( - { - note: { - ...note, - userPermissions: { - adminNote: true, - }, - }, - }, - { isEditing: true }, - ); - }); - - it('should not render note content and should render reply form', () => { - expect(findNoteContent().exists()).toBe(false); - expect(findReplyForm().exists()).toBe(true); - }); - - it('hides the form on hideForm event', () => { - findReplyForm().vm.$emit('cancelForm'); - - return wrapper.vm.$nextTick().then(() => { - expect(findReplyForm().exists()).toBe(false); - expect(findNoteContent().exists()).toBe(true); - }); - }); - - it('calls a mutation on submitForm event and hides a form', () => { - findReplyForm().vm.$emit('submitForm'); - expect(mutate).toHaveBeenCalled(); - - return mutate() - .then(() => { - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(findReplyForm().exists()).toBe(false); - expect(findNoteContent().exists()).toBe(true); - }); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_notes/design_reply_form_spec.js b/spec/frontend/design_management_legacy/components/design_notes/design_reply_form_spec.js deleted file mode 100644 index 088a71b64af..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/design_reply_form_spec.js +++ /dev/null @@ -1,184 +0,0 @@ -import { mount } from '@vue/test-utils'; -import DesignReplyForm from '~/design_management_legacy/components/design_notes/design_reply_form.vue'; - -const showModal = jest.fn(); - -const GlModal = { - template: '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>', - methods: { - show: showModal, - }, -}; - -describe('Design reply form component', () => { - let wrapper; - - const findTextarea = () => wrapper.find('textarea'); - const findSubmitButton = () => wrapper.find({ ref: 'submitButton' }); - const findCancelButton = () => wrapper.find({ ref: 'cancelButton' }); - const findModal = () => wrapper.find({ ref: 'cancelCommentModal' }); - - function createComponent(props = {}, mountOptions = {}) { - wrapper = mount(DesignReplyForm, { - propsData: { - value: '', - isSaving: false, - ...props, - }, - stubs: { GlModal }, - ...mountOptions, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('textarea has focus after component mount', () => { - // We need to attach to document, so that `document.activeElement` is properly set in jsdom - createComponent({}, { attachToDocument: true }); - - expect(findTextarea().element).toEqual(document.activeElement); - }); - - it('renders button text as "Comment" when creating a comment', () => { - createComponent(); - - expect(findSubmitButton().html()).toMatchSnapshot(); - }); - - it('renders button text as "Save comment" when creating a comment', () => { - createComponent({ isNewComment: false }); - - expect(findSubmitButton().html()).toMatchSnapshot(); - }); - - describe('when form has no text', () => { - beforeEach(() => { - createComponent({ - value: '', - }); - }); - - it('submit button is disabled', () => { - expect(findSubmitButton().attributes().disabled).toBeTruthy(); - }); - - it('does not emit submitForm event on textarea ctrl+enter keydown', () => { - findTextarea().trigger('keydown.enter', { - ctrlKey: true, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('submitForm')).toBeFalsy(); - }); - }); - - it('does not emit submitForm event on textarea meta+enter keydown', () => { - findTextarea().trigger('keydown.enter', { - metaKey: true, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('submitForm')).toBeFalsy(); - }); - }); - - it('emits cancelForm event on pressing escape button on textarea', () => { - findTextarea().trigger('keyup.esc'); - - expect(wrapper.emitted('cancelForm')).toBeTruthy(); - }); - - it('emits cancelForm event on clicking Cancel button', () => { - findCancelButton().vm.$emit('click'); - - expect(wrapper.emitted('cancelForm')).toHaveLength(1); - }); - }); - - describe('when form has text', () => { - beforeEach(() => { - createComponent({ - value: 'test', - }); - }); - - it('submit button is enabled', () => { - expect(findSubmitButton().attributes().disabled).toBeFalsy(); - }); - - it('emits submitForm event on Comment button click', () => { - findSubmitButton().vm.$emit('click'); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('submitForm')).toBeTruthy(); - }); - }); - - it('emits submitForm event on textarea ctrl+enter keydown', () => { - findTextarea().trigger('keydown.enter', { - ctrlKey: true, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('submitForm')).toBeTruthy(); - }); - }); - - it('emits submitForm event on textarea meta+enter keydown', () => { - findTextarea().trigger('keydown.enter', { - metaKey: true, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('submitForm')).toBeTruthy(); - }); - }); - - it('emits input event on changing textarea content', () => { - findTextarea().setValue('test2'); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('input')).toBeTruthy(); - }); - }); - - it('emits cancelForm event on Escape key if text was not changed', () => { - findTextarea().trigger('keyup.esc'); - - expect(wrapper.emitted('cancelForm')).toBeTruthy(); - }); - - it('opens confirmation modal on Escape key when text has changed', () => { - wrapper.setProps({ value: 'test2' }); - - return wrapper.vm.$nextTick().then(() => { - findTextarea().trigger('keyup.esc'); - expect(showModal).toHaveBeenCalled(); - }); - }); - - it('emits cancelForm event on Cancel button click if text was not changed', () => { - findCancelButton().trigger('click'); - - expect(wrapper.emitted('cancelForm')).toBeTruthy(); - }); - - it('opens confirmation modal on Cancel button click when text has changed', () => { - wrapper.setProps({ value: 'test2' }); - - return wrapper.vm.$nextTick().then(() => { - findCancelButton().trigger('click'); - expect(showModal).toHaveBeenCalled(); - }); - }); - - it('emits cancelForm event on modal Ok button click', () => { - findTextarea().trigger('keyup.esc'); - findModal().vm.$emit('ok'); - - expect(wrapper.emitted('cancelForm')).toBeTruthy(); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_notes/toggle_replies_widget_spec.js b/spec/frontend/design_management_legacy/components/design_notes/toggle_replies_widget_spec.js deleted file mode 100644 index acc7cbbca52..00000000000 --- a/spec/frontend/design_management_legacy/components/design_notes/toggle_replies_widget_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlIcon, GlButton, GlLink } from '@gitlab/ui'; -import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import ToggleRepliesWidget from '~/design_management_legacy/components/design_notes/toggle_replies_widget.vue'; -import notes from '../../mock_data/notes'; - -describe('Toggle replies widget component', () => { - let wrapper; - - const findToggleWrapper = () => wrapper.find('[data-testid="toggle-comments-wrapper"]'); - const findIcon = () => wrapper.find(GlIcon); - const findButton = () => wrapper.find(GlButton); - const findAuthorLink = () => wrapper.find(GlLink); - const findTimeAgo = () => wrapper.find(TimeAgoTooltip); - - function createComponent(props = {}) { - wrapper = shallowMount(ToggleRepliesWidget, { - propsData: { - collapsed: true, - replies: notes, - ...props, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when replies are collapsed', () => { - beforeEach(() => { - createComponent(); - }); - - it('should not have expanded class', () => { - expect(findToggleWrapper().classes()).not.toContain('expanded'); - }); - - it('should render chevron-right icon', () => { - expect(findIcon().props('name')).toBe('chevron-right'); - }); - - it('should have replies length on button', () => { - expect(findButton().text()).toBe('2 replies'); - }); - - it('should render a link to the last reply author', () => { - expect(findAuthorLink().exists()).toBe(true); - expect(findAuthorLink().text()).toBe(notes[1].author.name); - expect(findAuthorLink().attributes('href')).toBe(notes[1].author.webUrl); - }); - - it('should render correct time ago tooltip', () => { - expect(findTimeAgo().exists()).toBe(true); - expect(findTimeAgo().props('time')).toBe(notes[1].createdAt); - }); - }); - - describe('when replies are expanded', () => { - beforeEach(() => { - createComponent({ collapsed: false }); - }); - - it('should have expanded class', () => { - expect(findToggleWrapper().classes()).toContain('expanded'); - }); - - it('should render chevron-down icon', () => { - expect(findIcon().props('name')).toBe('chevron-down'); - }); - - it('should have Collapse replies text on button', () => { - expect(findButton().text()).toBe('Collapse replies'); - }); - - it('should not have a link to the last reply author', () => { - expect(findAuthorLink().exists()).toBe(false); - }); - - it('should not render time ago tooltip', () => { - expect(findTimeAgo().exists()).toBe(false); - }); - }); - - it('should emit toggle event on icon click', () => { - createComponent(); - findIcon().vm.$emit('click', new MouseEvent('click')); - - expect(wrapper.emitted('toggle')).toHaveLength(1); - }); - - it('should emit toggle event on button click', () => { - createComponent(); - findButton().vm.$emit('click', new MouseEvent('click')); - - expect(wrapper.emitted('toggle')).toHaveLength(1); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_overlay_spec.js b/spec/frontend/design_management_legacy/components/design_overlay_spec.js deleted file mode 100644 index c014f3479f4..00000000000 --- a/spec/frontend/design_management_legacy/components/design_overlay_spec.js +++ /dev/null @@ -1,410 +0,0 @@ -import { mount } from '@vue/test-utils'; -import DesignOverlay from '~/design_management_legacy/components/design_overlay.vue'; -import updateActiveDiscussion from '~/design_management_legacy/graphql/mutations/update_active_discussion.mutation.graphql'; -import notes from '../mock_data/notes'; -import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '~/design_management_legacy/constants'; - -const mutate = jest.fn(() => Promise.resolve()); - -describe('Design overlay component', () => { - let wrapper; - - 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 clickAndDragBadge = (elem, fromPoint, toPoint) => { - elem.trigger('mousedown', { clientX: fromPoint.x, clientY: fromPoint.y }); - return wrapper.vm.$nextTick().then(() => { - elem.trigger('mousemove', { clientX: toPoint.x, clientY: toPoint.y }); - return wrapper.vm.$nextTick(); - }); - }; - - function createComponent(props = {}, data = {}) { - wrapper = mount(DesignOverlay, { - propsData: { - dimensions: mockDimensions, - position: { - top: '0', - left: '0', - }, - resolvedDiscussionsExpanded: false, - ...props, - }, - data() { - return { - activeDiscussion: { - id: null, - source: null, - }, - ...data, - }; - }, - mocks: { - $apollo: { - mutate, - }, - }, - }); - } - - it('should have correct inline style', () => { - createComponent(); - - expect(wrapper.find('.image-diff-overlay').attributes().style).toBe( - 'width: 100px; height: 100px; top: 0px; left: 0px;', - ); - }); - - it('should emit `openCommentForm` when clicking on overlay', () => { - createComponent(); - const newCoordinates = { - x: 10, - y: 10, - }; - - wrapper - .find('.image-diff-overlay-add-comment') - .trigger('mouseup', { offsetX: newCoordinates.x, offsetY: newCoordinates.y }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('openCommentForm')).toEqual([ - [{ x: newCoordinates.x, y: newCoordinates.y }], - ]); - }); - }); - - describe('with notes', () => { - it('should render only the first note', () => { - createComponent({ - notes, - }); - expect(findAllNotes()).toHaveLength(1); - }); - - describe('with resolved discussions toggle expanded', () => { - beforeEach(() => { - createComponent({ - notes, - resolvedDiscussionsExpanded: true, - }); - }); - - it('should render all notes', () => { - expect(findAllNotes()).toHaveLength(notes.length); - }); - - it('should have set the correct position for each note badge', () => { - expect(findFirstBadge().attributes().style).toBe('left: 10px; top: 15px;'); - expect(findSecondBadge().attributes().style).toBe('left: 50px; top: 50px;'); - }); - - it('should apply resolved class to the resolved note pin', () => { - 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', - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(findSecondBadge().classes()).toContain('inactive'); - }); - }); - }); - - it('should recalculate badges positions on window resize', () => { - createComponent({ - notes, - dimensions: { - width: 400, - height: 400, - }, - }); - - expect(findFirstBadge().attributes().style).toBe('left: 40px; top: 60px;'); - - wrapper.setProps({ - dimensions: { - width: 200, - height: 200, - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(findFirstBadge().attributes().style).toBe('left: 20px; top: 30px;'); - }); - }); - - it('should call an update active discussion mutation when clicking a note without moving it', () => { - const note = notes[0]; - const { position } = note; - const mutationVariables = { - mutation: updateActiveDiscussion, - variables: { - id: note.id, - source: ACTIVE_DISCUSSION_SOURCE_TYPES.pin, - }, - }; - - findFirstBadge().trigger('mousedown', { clientX: position.x, clientY: position.y }); - - return wrapper.vm.$nextTick().then(() => { - findFirstBadge().trigger('mouseup', { clientX: position.x, clientY: position.y }); - expect(mutate).toHaveBeenCalledWith(mutationVariables); - }); - }); - }); - - describe('when moving notes', () => { - it('should update badge style when note is being moved', () => { - createComponent({ - notes, - }); - - const { position } = notes[0]; - - return clickAndDragBadge( - findFirstBadge(), - { x: position.x, y: position.y }, - { x: 20, y: 20 }, - ).then(() => { - expect(findFirstBadge().attributes().style).toBe('left: 20px; top: 20px; cursor: move;'); - }); - }); - - it('should emit `moveNote` event when note-moving action ends', () => { - createComponent({ notes }); - const note = notes[0]; - const { position } = note; - const newCoordinates = { x: 20, y: 20 }; - - wrapper.setData({ - movingNoteNewPosition: { - ...position, - ...newCoordinates, - }, - movingNoteStartPosition: { - noteId: notes[0].id, - discussionId: notes[0].discussion.id, - ...position, - }, - }); - - const badge = findFirstBadge(); - return clickAndDragBadge(badge, { x: position.x, y: position.y }, newCoordinates) - .then(() => { - badge.trigger('mouseup'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.emitted('moveNote')).toEqual([ - [ - { - noteId: notes[0].id, - discussionId: notes[0].discussion.id, - coordinates: newCoordinates, - }, - ], - ]); - }); - }); - - describe('without [adminNote] permission', () => { - const mockNoteNotAuthorised = { - ...notes[0], - userPermissions: { - adminNote: false, - }, - }; - - const mockNoteCoordinates = { - x: mockNoteNotAuthorised.position.x, - y: mockNoteNotAuthorised.position.y, - }; - - it('should be unable to move a note', () => { - createComponent({ - dimensions: mockDimensions, - notes: [mockNoteNotAuthorised], - }); - - const badge = findAllNotes().at(0); - return clickAndDragBadge(badge, { ...mockNoteCoordinates }, { x: 20, y: 20 }).then(() => { - // note position should not change after a click-and-drag attempt - expect(findFirstBadge().attributes().style).toContain( - `left: ${mockNoteCoordinates.x}px; top: ${mockNoteCoordinates.y}px;`, - ); - }); - }); - }); - }); - - describe('with a new form', () => { - it('should render a new comment badge', () => { - createComponent({ - currentCommentForm: { - ...notes[0].position, - }, - }); - - expect(findCommentBadge().exists()).toBe(true); - expect(findCommentBadge().attributes().style).toBe('left: 10px; top: 15px;'); - }); - - describe('when moving the comment badge', () => { - it('should update badge style to reflect new position', () => { - const { position } = notes[0]; - - createComponent({ - currentCommentForm: { - ...position, - }, - }); - - return clickAndDragBadge( - findCommentBadge(), - { x: position.x, y: position.y }, - { x: 20, y: 20 }, - ).then(() => { - expect(findCommentBadge().attributes().style).toBe( - 'left: 20px; top: 20px; cursor: move;', - ); - }); - }); - - it('should update badge style when note-moving action ends', () => { - const { position } = notes[0]; - createComponent({ - currentCommentForm: { - ...position, - }, - }); - - const commentBadge = findCommentBadge(); - const toPoint = { x: 20, y: 20 }; - - return clickAndDragBadge(commentBadge, { x: position.x, y: position.y }, toPoint) - .then(() => { - commentBadge.trigger('mouseup'); - // simulates the currentCommentForm being updated in index.vue component, and - // propagated back down to this prop - wrapper.setProps({ - currentCommentForm: { height: position.height, width: position.width, ...toPoint }, - }); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(commentBadge.attributes().style).toBe('left: 20px; top: 20px;'); - }); - }); - - it.each` - element | getElementFunc | event - ${'overlay'} | ${findOverlay} | ${'mouseleave'} - ${'comment badge'} | ${findCommentBadge} | ${'mouseup'} - `( - 'should emit `openCommentForm` event when $event fired on $element element', - ({ getElementFunc, event }) => { - createComponent({ - notes, - currentCommentForm: { - ...notes[0].position, - }, - }); - - const newCoordinates = { x: 20, y: 20 }; - wrapper.setData({ - movingNoteStartPosition: { - ...notes[0].position, - }, - movingNoteNewPosition: { - ...notes[0].position, - ...newCoordinates, - }, - }); - - getElementFunc().trigger(event); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('openCommentForm')).toEqual([[newCoordinates]]); - }); - }, - ); - }); - }); - - describe('getMovingNotePositionDelta', () => { - it('should calculate delta correctly from state', () => { - createComponent(); - - wrapper.setData({ - movingNoteStartPosition: { - clientX: 10, - clientY: 20, - }, - }); - - const mockMouseEvent = { - clientX: 30, - clientY: 10, - }; - - expect(wrapper.vm.getMovingNotePositionDelta(mockMouseEvent)).toEqual({ - deltaX: 20, - deltaY: -10, - }); - }); - }); - - describe('isPositionInOverlay', () => { - createComponent({ dimensions: mockDimensions }); - - it.each` - test | coordinates | expectedResult - ${'within overlay bounds'} | ${{ x: 50, y: 50 }} | ${true} - ${'outside overlay bounds'} | ${{ x: 101, y: 101 }} | ${false} - `('returns [$expectedResult] when position is $test', ({ coordinates, expectedResult }) => { - const position = { ...mockDimensions, ...coordinates }; - - expect(wrapper.vm.isPositionInOverlay(position)).toBe(expectedResult); - }); - }); - - describe('getNoteRelativePosition', () => { - it('calculates position correctly', () => { - createComponent({ dimensions: mockDimensions }); - const position = { x: 50, y: 50, width: 200, height: 200 }; - - expect(wrapper.vm.getNoteRelativePosition(position)).toEqual({ left: 25, top: 25 }); - }); - }); - - describe('canMoveNote', () => { - it.each` - adminNotePermission | canMoveNoteResult - ${true} | ${true} - ${false} | ${false} - ${undefined} | ${false} - `( - 'returns [$canMoveNoteResult] when [adminNote permission] is [$adminNotePermission]', - ({ adminNotePermission, canMoveNoteResult }) => { - createComponent(); - - const note = { - userPermissions: { - adminNote: adminNotePermission, - }, - }; - expect(wrapper.vm.canMoveNote(note)).toBe(canMoveNoteResult); - }, - ); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_presentation_spec.js b/spec/frontend/design_management_legacy/components/design_presentation_spec.js deleted file mode 100644 index ceff86b0549..00000000000 --- a/spec/frontend/design_management_legacy/components/design_presentation_spec.js +++ /dev/null @@ -1,553 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DesignPresentation from '~/design_management_legacy/components/design_presentation.vue'; -import DesignOverlay from '~/design_management_legacy/components/design_overlay.vue'; - -const mockOverlayData = { - overlayDimensions: { - width: 100, - height: 100, - }, - overlayPosition: { - top: '0', - left: '0', - }, -}; - -describe('Design management design presentation component', () => { - let wrapper; - - function createComponent( - { - image, - imageName, - discussions = [], - isAnnotating = false, - resolvedDiscussionsExpanded = false, - } = {}, - data = {}, - stubs = {}, - ) { - wrapper = shallowMount(DesignPresentation, { - propsData: { - image, - imageName, - discussions, - isAnnotating, - resolvedDiscussionsExpanded, - }, - stubs, - }); - - wrapper.setData(data); - wrapper.element.scrollTo = jest.fn(); - } - - const findOverlayCommentButton = () => wrapper.find('.image-diff-overlay-add-comment'); - - /** - * Spy on $refs and mock given values - * @param {Object} viewportDimensions {width, height} - * @param {Object} childDimensions {width, height} - * @param {Float} scrollTopPerc 0 < x < 1 - * @param {Float} scrollLeftPerc 0 < x < 1 - */ - function mockRefDimensions( - ref, - viewportDimensions, - childDimensions, - scrollTopPerc, - scrollLeftPerc, - ) { - jest.spyOn(ref, 'scrollWidth', 'get').mockReturnValue(childDimensions.width); - jest.spyOn(ref, 'scrollHeight', 'get').mockReturnValue(childDimensions.height); - jest.spyOn(ref, 'offsetWidth', 'get').mockReturnValue(viewportDimensions.width); - jest.spyOn(ref, 'offsetHeight', 'get').mockReturnValue(viewportDimensions.height); - jest - .spyOn(ref, 'scrollLeft', 'get') - .mockReturnValue((childDimensions.width - viewportDimensions.width) * scrollLeftPerc); - jest - .spyOn(ref, 'scrollTop', 'get') - .mockReturnValue((childDimensions.height - viewportDimensions.height) * scrollTopPerc); - } - - function clickDragExplore(startCoords, endCoords, { useTouchEvents, mouseup } = {}) { - const event = useTouchEvents - ? { - mousedown: 'touchstart', - mousemove: 'touchmove', - mouseup: 'touchend', - } - : { - mousedown: 'mousedown', - mousemove: 'mousemove', - mouseup: 'mouseup', - }; - - const addCommentOverlay = findOverlayCommentButton(); - - // triggering mouse events on this element best simulates - // reality, as it is the lowest-level node that needs to - // respond to mouse events - addCommentOverlay.trigger(event.mousedown, { - clientX: startCoords.clientX, - clientY: startCoords.clientY, - }); - return wrapper.vm - .$nextTick() - .then(() => { - addCommentOverlay.trigger(event.mousemove, { - clientX: endCoords.clientX, - clientY: endCoords.clientY, - }); - - return wrapper.vm.$nextTick(); - }) - .then(() => { - if (mouseup) { - addCommentOverlay.trigger(event.mouseup); - return wrapper.vm.$nextTick(); - } - - return undefined; - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders image and overlay when image provided', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders empty state when no image provided', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('openCommentForm event emits correct data', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - - wrapper.vm.openCommentForm({ x: 1, y: 1 }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('openCommentForm')).toEqual([ - [{ ...mockOverlayData.overlayDimensions, x: 1, y: 1 }], - ]); - }); - }); - - describe('currentCommentForm', () => { - it('is null when isAnnotating is false', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.currentCommentForm).toBeNull(); - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('is null when isAnnotating is true but annotation position is falsey', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - isAnnotating: true, - }, - mockOverlayData, - ); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.currentCommentForm).toBeNull(); - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('is equal to current annotation position when isAnnotating is true', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - isAnnotating: true, - }, - { - ...mockOverlayData, - currentAnnotationPosition: { - x: 1, - y: 1, - width: 100, - height: 100, - }, - }, - ); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.currentCommentForm).toEqual({ - x: 1, - y: 1, - width: 100, - height: 100, - }); - expect(wrapper.element).toMatchSnapshot(); - }); - }); - }); - - describe('setOverlayPosition', () => { - beforeEach(() => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('sets overlay position correctly when overlay is smaller than viewport', () => { - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetWidth', 'get').mockReturnValue(200); - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetHeight', 'get').mockReturnValue(200); - - wrapper.vm.setOverlayPosition(); - expect(wrapper.vm.overlayPosition).toEqual({ - left: `calc(50% - ${mockOverlayData.overlayDimensions.width / 2}px)`, - top: `calc(50% - ${mockOverlayData.overlayDimensions.height / 2}px)`, - }); - }); - - it('sets overlay position correctly when overlay width is larger than viewports', () => { - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetWidth', 'get').mockReturnValue(50); - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetHeight', 'get').mockReturnValue(200); - - wrapper.vm.setOverlayPosition(); - expect(wrapper.vm.overlayPosition).toEqual({ - left: '0', - top: `calc(50% - ${mockOverlayData.overlayDimensions.height / 2}px)`, - }); - }); - - it('sets overlay position correctly when overlay height is larger than viewports', () => { - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetWidth', 'get').mockReturnValue(200); - jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetHeight', 'get').mockReturnValue(50); - - wrapper.vm.setOverlayPosition(); - expect(wrapper.vm.overlayPosition).toEqual({ - left: `calc(50% - ${mockOverlayData.overlayDimensions.width / 2}px)`, - top: '0', - }); - }); - }); - - describe('getViewportCenter', () => { - beforeEach(() => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - }); - - it('calculate center correctly with no scroll', () => { - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 10, height: 10 }, - { width: 20, height: 20 }, - 0, - 0, - ); - - expect(wrapper.vm.getViewportCenter()).toEqual({ - x: 5, - y: 5, - }); - }); - - it('calculate center correctly with some scroll', () => { - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 10, height: 10 }, - { width: 20, height: 20 }, - 0.5, - 0.5, - ); - - expect(wrapper.vm.getViewportCenter()).toEqual({ - x: 10, - y: 10, - }); - }); - - it('Returns default case if no overflow (scrollWidth==offsetWidth, etc.)', () => { - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 20, height: 20 }, - { width: 20, height: 20 }, - 0.5, - 0.5, - ); - - expect(wrapper.vm.getViewportCenter()).toEqual({ - x: 10, - y: 10, - }); - }); - }); - - describe('scaleZoomFocalPoint', () => { - it('scales focal point correctly when zooming in', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - { - ...mockOverlayData, - zoomFocalPoint: { - x: 5, - y: 5, - width: 50, - height: 50, - }, - }, - ); - - wrapper.vm.scaleZoomFocalPoint(); - expect(wrapper.vm.zoomFocalPoint).toEqual({ - x: 10, - y: 10, - width: 100, - height: 100, - }); - }); - - it('scales focal point correctly when zooming out', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - { - ...mockOverlayData, - zoomFocalPoint: { - x: 10, - y: 10, - width: 200, - height: 200, - }, - }, - ); - - wrapper.vm.scaleZoomFocalPoint(); - expect(wrapper.vm.zoomFocalPoint).toEqual({ - x: 5, - y: 5, - width: 100, - height: 100, - }); - }); - }); - - describe('onImageResize', () => { - it('sets zoom focal point on initial load', () => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - ); - - wrapper.setMethods({ - shiftZoomFocalPoint: jest.fn(), - scaleZoomFocalPoint: jest.fn(), - scrollToFocalPoint: jest.fn(), - }); - - wrapper.vm.onImageResize({ width: 10, height: 10 }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.shiftZoomFocalPoint).toHaveBeenCalled(); - expect(wrapper.vm.initialLoad).toBe(false); - }); - }); - - it('calls scaleZoomFocalPoint and scrollToFocalPoint after initial load', () => { - wrapper.vm.onImageResize({ width: 10, height: 10 }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.scaleZoomFocalPoint).toHaveBeenCalled(); - expect(wrapper.vm.scrollToFocalPoint).toHaveBeenCalled(); - }); - }); - }); - - describe('onPresentationMousedown', () => { - it.each` - scenario | width | height - ${'width overflows'} | ${101} | ${100} - ${'height overflows'} | ${100} | ${101} - ${'width and height overflows'} | ${200} | ${200} - `('sets lastDragPosition when design $scenario', ({ width, height }) => { - createComponent(); - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 100, height: 100 }, - { width, height }, - ); - - const newLastDragPosition = { x: 2, y: 2 }; - wrapper.vm.onPresentationMousedown({ - clientX: newLastDragPosition.x, - clientY: newLastDragPosition.y, - }); - - expect(wrapper.vm.lastDragPosition).toStrictEqual(newLastDragPosition); - }); - - it('does not set lastDragPosition if design does not overflow', () => { - const lastDragPosition = { x: 1, y: 1 }; - - createComponent({}, { lastDragPosition }); - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 100, height: 100 }, - { width: 50, height: 50 }, - ); - - wrapper.vm.onPresentationMousedown({ clientX: 2, clientY: 2 }); - - // check lastDragPosition is unchanged - expect(wrapper.vm.lastDragPosition).toStrictEqual(lastDragPosition); - }); - }); - - describe('getAnnotationPositon', () => { - it.each` - coordinates | overlayDimensions | position - ${{ x: 100, y: 100 }} | ${{ width: 50, height: 50 }} | ${{ x: 100, y: 100, width: 50, height: 50 }} - ${{ x: 100.2, y: 100.5 }} | ${{ width: 50.6, height: 50.0 }} | ${{ x: 100, y: 101, width: 51, height: 50 }} - `('returns correct annotation position', ({ coordinates, overlayDimensions, position }) => { - createComponent(undefined, { - overlayDimensions: { - width: overlayDimensions.width, - height: overlayDimensions.height, - }, - }); - - expect(wrapper.vm.getAnnotationPositon(coordinates)).toStrictEqual(position); - }); - }); - - describe('when design is overflowing', () => { - beforeEach(() => { - createComponent( - { - image: 'test.jpg', - imageName: 'test', - }, - mockOverlayData, - { - 'design-overlay': DesignOverlay, - }, - ); - - // mock a design that overflows - mockRefDimensions( - wrapper.vm.$refs.presentationViewport, - { width: 10, height: 10 }, - { width: 20, height: 20 }, - 0, - 0, - ); - }); - - it('opens a comment form if design was not dragged', () => { - const addCommentOverlay = findOverlayCommentButton(); - const startCoords = { - clientX: 1, - clientY: 1, - }; - - addCommentOverlay.trigger('mousedown', { - clientX: startCoords.clientX, - clientY: startCoords.clientY, - }); - - return wrapper.vm - .$nextTick() - .then(() => { - addCommentOverlay.trigger('mouseup'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.emitted('openCommentForm')).toBeDefined(); - }); - }); - - describe('when clicking and dragging', () => { - it.each` - description | useTouchEvents - ${'with touch events'} | ${true} - ${'without touch events'} | ${false} - `('calls scrollTo with correct arguments $description', ({ useTouchEvents }) => { - return clickDragExplore( - { clientX: 0, clientY: 0 }, - { clientX: 10, clientY: 10 }, - { useTouchEvents }, - ).then(() => { - expect(wrapper.element.scrollTo).toHaveBeenCalledTimes(1); - expect(wrapper.element.scrollTo).toHaveBeenCalledWith(-10, -10); - }); - }); - - it('does not open a comment form when drag position exceeds buffer', () => { - return clickDragExplore( - { clientX: 0, clientY: 0 }, - { clientX: 10, clientY: 10 }, - { mouseup: true }, - ).then(() => { - expect(wrapper.emitted('openCommentForm')).toBeFalsy(); - }); - }); - - it('opens a comment form when drag position is within buffer', () => { - return clickDragExplore( - { clientX: 0, clientY: 0 }, - { clientX: 1, clientY: 0 }, - { mouseup: true }, - ).then(() => { - expect(wrapper.emitted('openCommentForm')).toBeDefined(); - }); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_scaler_spec.js b/spec/frontend/design_management_legacy/components/design_scaler_spec.js deleted file mode 100644 index 30ef5ab159b..00000000000 --- a/spec/frontend/design_management_legacy/components/design_scaler_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DesignScaler from '~/design_management_legacy/components/design_scaler.vue'; - -describe('Design management design scaler component', () => { - let wrapper; - - function createComponent(propsData, data = {}) { - wrapper = shallowMount(DesignScaler, { - propsData, - }); - wrapper.setData(data); - } - - afterEach(() => { - wrapper.destroy(); - }); - - const getButton = type => { - const buttonTypeOrder = ['minus', 'reset', 'plus']; - const buttons = wrapper.findAll('button'); - return buttons.at(buttonTypeOrder.indexOf(type)); - }; - - it('emits @scale event when "plus" button clicked', () => { - createComponent(); - - getButton('plus').trigger('click'); - expect(wrapper.emitted('scale')).toEqual([[1.2]]); - }); - - it('emits @scale event when "reset" button clicked (scale > 1)', () => { - createComponent({}, { scale: 1.6 }); - return wrapper.vm.$nextTick().then(() => { - getButton('reset').trigger('click'); - expect(wrapper.emitted('scale')).toEqual([[1]]); - }); - }); - - it('emits @scale event when "minus" button clicked (scale > 1)', () => { - createComponent({}, { scale: 1.6 }); - - return wrapper.vm.$nextTick().then(() => { - getButton('minus').trigger('click'); - expect(wrapper.emitted('scale')).toEqual([[1.4]]); - }); - }); - - it('minus and reset buttons are disabled when scale === 1', () => { - createComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('minus and reset buttons are enabled when scale > 1', () => { - createComponent({}, { scale: 1.2 }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('plus button is disabled when scale === 2', () => { - createComponent({}, { scale: 2 }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/design_sidebar_spec.js b/spec/frontend/design_management_legacy/components/design_sidebar_spec.js deleted file mode 100644 index fc0f618c359..00000000000 --- a/spec/frontend/design_management_legacy/components/design_sidebar_spec.js +++ /dev/null @@ -1,236 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlCollapse, GlPopover } from '@gitlab/ui'; -import Cookies from 'js-cookie'; -import DesignSidebar from '~/design_management_legacy/components/design_sidebar.vue'; -import Participants from '~/sidebar/components/participants/participants.vue'; -import DesignDiscussion from '~/design_management_legacy/components/design_notes/design_discussion.vue'; -import design from '../mock_data/design'; -import updateActiveDiscussionMutation from '~/design_management_legacy/graphql/mutations/update_active_discussion.mutation.graphql'; - -const updateActiveDiscussionMutationVariables = { - mutation: updateActiveDiscussionMutation, - variables: { - id: design.discussions.nodes[0].notes.nodes[0].id, - source: 'discussion', - }, -}; - -const $route = { - params: { - id: '1', - }, -}; - -const cookieKey = 'hide_design_resolved_comments_popover'; - -const mutate = jest.fn().mockResolvedValue(); - -describe('Design management design sidebar component', () => { - let wrapper; - - const findDiscussions = () => wrapper.findAll(DesignDiscussion); - const findFirstDiscussion = () => findDiscussions().at(0); - const findUnresolvedDiscussions = () => wrapper.findAll('[data-testid="unresolved-discussion"]'); - const findResolvedDiscussions = () => wrapper.findAll('[data-testid="resolved-discussion"]'); - const findParticipants = () => wrapper.find(Participants); - const findCollapsible = () => wrapper.find(GlCollapse); - const findToggleResolvedCommentsButton = () => wrapper.find('[data-testid="resolved-comments"]'); - const findPopover = () => wrapper.find(GlPopover); - const findNewDiscussionDisclaimer = () => - wrapper.find('[data-testid="new-discussion-disclaimer"]'); - - function createComponent(props = {}) { - wrapper = shallowMount(DesignSidebar, { - propsData: { - design, - resolvedDiscussionsExpanded: false, - markdownPreviewPath: '', - ...props, - }, - mocks: { - $route, - $apollo: { - mutate, - }, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders participants', () => { - createComponent(); - - expect(findParticipants().exists()).toBe(true); - }); - - it('passes the correct amount of participants to the Participants component', () => { - createComponent(); - - expect(findParticipants().props('participants')).toHaveLength(1); - }); - - describe('when has no discussions', () => { - beforeEach(() => { - createComponent({ - design: { - ...design, - discussions: { - nodes: [], - }, - }, - }); - }); - - it('does not render discussions', () => { - expect(findDiscussions().exists()).toBe(false); - }); - - it('renders a message about possibility to create a new discussion', () => { - expect(findNewDiscussionDisclaimer().exists()).toBe(true); - }); - }); - - describe('when has discussions', () => { - beforeEach(() => { - Cookies.set(cookieKey, true); - createComponent(); - }); - - it('renders correct amount of unresolved discussions', () => { - expect(findUnresolvedDiscussions()).toHaveLength(1); - }); - - it('renders correct amount of resolved discussions', () => { - expect(findResolvedDiscussions()).toHaveLength(1); - }); - - it('has resolved comments collapsible collapsed', () => { - expect(findCollapsible().attributes('visible')).toBeUndefined(); - }); - - it('emits toggleResolveComments event on resolve comments button click', () => { - findToggleResolvedCommentsButton().vm.$emit('click'); - expect(wrapper.emitted('toggleResolvedComments')).toHaveLength(1); - }); - - it('opens a collapsible when resolvedDiscussionsExpanded prop changes to true', () => { - expect(findCollapsible().attributes('visible')).toBeUndefined(); - wrapper.setProps({ - resolvedDiscussionsExpanded: true, - }); - return wrapper.vm.$nextTick().then(() => { - expect(findCollapsible().attributes('visible')).toBe('true'); - }); - }); - - it('does not popover about resolved comments', () => { - expect(findPopover().exists()).toBe(false); - }); - - it('sends a mutation to set an active discussion when clicking on a discussion', () => { - findFirstDiscussion().trigger('click'); - - expect(mutate).toHaveBeenCalledWith(updateActiveDiscussionMutationVariables); - }); - - it('sends a mutation to reset an active discussion when clicking outside of discussion', () => { - wrapper.trigger('click'); - - expect(mutate).toHaveBeenCalledWith({ - ...updateActiveDiscussionMutationVariables, - variables: { id: undefined, source: 'discussion' }, - }); - }); - - it('emits correct event on discussion create note error', () => { - findFirstDiscussion().vm.$emit('createNoteError', 'payload'); - expect(wrapper.emitted('onDesignDiscussionError')).toEqual([['payload']]); - }); - - it('emits correct event on discussion update note error', () => { - findFirstDiscussion().vm.$emit('updateNoteError', 'payload'); - expect(wrapper.emitted('updateNoteError')).toEqual([['payload']]); - }); - - it('emits correct event on discussion resolve error', () => { - findFirstDiscussion().vm.$emit('resolveDiscussionError', 'payload'); - expect(wrapper.emitted('resolveDiscussionError')).toEqual([['payload']]); - }); - - it('changes prop correctly on opening discussion form', () => { - findFirstDiscussion().vm.$emit('openForm', 'some-id'); - - return wrapper.vm.$nextTick().then(() => { - expect(findFirstDiscussion().props('discussionWithOpenForm')).toBe('some-id'); - }); - }); - }); - - describe('when all discussions are resolved', () => { - beforeEach(() => { - createComponent({ - design: { - ...design, - discussions: { - nodes: [ - { - id: 'discussion-id', - replyId: 'discussion-reply-id', - resolved: true, - notes: { - nodes: [ - { - id: 'note-id', - body: '123', - author: { - name: 'Administrator', - username: 'root', - webUrl: 'link-to-author', - avatarUrl: 'link-to-avatar', - }, - }, - ], - }, - }, - ], - }, - }, - }); - }); - - it('renders a message about possibility to create a new discussion', () => { - expect(findNewDiscussionDisclaimer().exists()).toBe(true); - }); - - it('does not render unresolved discussions', () => { - expect(findUnresolvedDiscussions()).toHaveLength(0); - }); - }); - - describe('when showing resolved discussions for the first time', () => { - beforeEach(() => { - Cookies.set(cookieKey, false); - createComponent(); - }); - - it('renders a popover if we show resolved comments collapsible for the first time', () => { - expect(findPopover().exists()).toBe(true); - }); - - it('dismisses a popover on the outside click', () => { - wrapper.trigger('click'); - return wrapper.vm.$nextTick(() => { - expect(findPopover().exists()).toBe(false); - }); - }); - - it(`sets a ${cookieKey} cookie on clicking outside the popover`, () => { - jest.spyOn(Cookies, 'set'); - wrapper.trigger('click'); - expect(Cookies.set).toHaveBeenCalledWith(cookieKey, 'true', { expires: 365 * 10 }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/image_spec.js b/spec/frontend/design_management_legacy/components/image_spec.js deleted file mode 100644 index 265c91abb4e..00000000000 --- a/spec/frontend/design_management_legacy/components/image_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlIcon } from '@gitlab/ui'; -import DesignImage from '~/design_management_legacy/components/image.vue'; - -describe('Design management large image component', () => { - let wrapper; - - function createComponent(propsData, data = {}) { - wrapper = shallowMount(DesignImage, { - propsData, - }); - wrapper.setData(data); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders loading state', () => { - createComponent({ - isLoading: true, - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders image', () => { - createComponent({ - isLoading: false, - image: 'test.jpg', - name: 'test', - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('sets correct classes and styles if imageStyle is set', () => { - createComponent( - { - isLoading: false, - image: 'test.jpg', - name: 'test', - }, - { - imageStyle: { - width: '100px', - height: '100px', - }, - }, - ); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders media broken icon on error', () => { - createComponent({ - isLoading: false, - image: 'test.jpg', - name: 'test', - }); - - const image = wrapper.find('img'); - image.trigger('error'); - return wrapper.vm.$nextTick().then(() => { - expect(image.isVisible()).toBe(false); - expect(wrapper.find(GlIcon).element).toMatchSnapshot(); - }); - }); - - describe('zoom', () => { - const baseImageWidth = 100; - const baseImageHeight = 100; - - beforeEach(() => { - createComponent( - { - isLoading: false, - image: 'test.jpg', - name: 'test', - }, - { - imageStyle: { - width: `${baseImageWidth}px`, - height: `${baseImageHeight}px`, - }, - baseImageSize: { - width: baseImageWidth, - height: baseImageHeight, - }, - }, - ); - - jest.spyOn(wrapper.vm.$refs.contentImg, 'offsetWidth', 'get').mockReturnValue(baseImageWidth); - jest - .spyOn(wrapper.vm.$refs.contentImg, 'offsetHeight', 'get') - .mockReturnValue(baseImageHeight); - }); - - it('emits @resize event on zoom', () => { - const zoomAmount = 2; - wrapper.vm.zoom(zoomAmount); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('resize')).toEqual([ - [{ width: baseImageWidth * zoomAmount, height: baseImageHeight * zoomAmount }], - ]); - }); - }); - - it('emits @resize event with base image size when scale=1', () => { - wrapper.vm.zoom(1); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted('resize')).toEqual([ - [{ width: baseImageWidth, height: baseImageHeight }], - ]); - }); - }); - - it('sets image style when zoomed', () => { - const zoomAmount = 2; - wrapper.vm.zoom(zoomAmount); - expect(wrapper.vm.imageStyle).toEqual({ - width: `${baseImageWidth * zoomAmount}px`, - height: `${baseImageHeight * zoomAmount}px`, - }); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/list/__snapshots__/item_spec.js.snap b/spec/frontend/design_management_legacy/components/list/__snapshots__/item_spec.js.snap deleted file mode 100644 index 168b9424006..00000000000 --- a/spec/frontend/design_management_legacy/components/list/__snapshots__/item_spec.js.snap +++ /dev/null @@ -1,149 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management list item component when item appears in view after image is loaded renders media broken icon when image onerror triggered 1`] = ` -<gl-icon-stub - class="text-secondary" - name="media-broken" - size="32" -/> -`; - -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" - to="[object Object]" -> - <div - class="card-body p-0 d-flex-center overflow-hidden position-relative" - > - <!----> - - <gl-intersection-observer-stub> - <!----> - - <img - alt="test" - class="block mx-auto mw-100 mh-100 design-img" - data-qa-selector="design_image" - src="" - /> - </gl-intersection-observer-stub> - </div> - - <div - class="card-footer d-flex w-100" - > - <div - class="d-flex flex-column str-truncated-100" - > - <span - class="bold str-truncated-100" - data-qa-selector="design_file_name" - > - test - </span> - - <span - class="str-truncated-100" - > - - Updated - <timeago-stub - cssclass="" - time="01-01-2019" - tooltipplacement="bottom" - /> - </span> - </div> - - <div - class="ml-auto d-flex align-items-center text-secondary" - > - <icon-stub - class="ml-1" - name="comments" - size="16" - /> - - <span - aria-label="2 comments" - class="ml-1" - > - - 2 - - </span> - </div> - </div> -</router-link-stub> -`; - -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" - to="[object Object]" -> - <div - class="card-body p-0 d-flex-center overflow-hidden position-relative" - > - <!----> - - <gl-intersection-observer-stub> - <!----> - - <img - alt="test" - class="block mx-auto mw-100 mh-100 design-img" - data-qa-selector="design_image" - src="" - /> - </gl-intersection-observer-stub> - </div> - - <div - class="card-footer d-flex w-100" - > - <div - class="d-flex flex-column str-truncated-100" - > - <span - class="bold str-truncated-100" - data-qa-selector="design_file_name" - > - test - </span> - - <span - class="str-truncated-100" - > - - Updated - <timeago-stub - cssclass="" - time="01-01-2019" - tooltipplacement="bottom" - /> - </span> - </div> - - <div - class="ml-auto d-flex align-items-center text-secondary" - > - <icon-stub - class="ml-1" - name="comments" - size="16" - /> - - <span - aria-label="1 comment" - class="ml-1" - > - - 1 - - </span> - </div> - </div> -</router-link-stub> -`; diff --git a/spec/frontend/design_management_legacy/components/list/item_spec.js b/spec/frontend/design_management_legacy/components/list/item_spec.js deleted file mode 100644 index e9bb0fc3f29..00000000000 --- a/spec/frontend/design_management_legacy/components/list/item_spec.js +++ /dev/null @@ -1,169 +0,0 @@ -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_legacy/components/list/item.vue'; - -const localVue = createLocalVue(); -localVue.use(VueRouter); -const router = new VueRouter(); - -// Referenced from: doc/api/graphql/reference/gitlab_schema.graphql:DesignVersionEvent -const DESIGN_VERSION_EVENT = { - CREATION: 'CREATION', - DELETION: 'DELETION', - MODIFICATION: 'MODIFICATION', - NO_CHANGE: 'NONE', -}; - -describe('Design management list item component', () => { - let wrapper; - - const findDesignEvent = () => wrapper.find('[data-testid="designEvent"]'); - const findEventIcon = () => findDesignEvent().find(Icon); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); - - function createComponent({ - notesCount = 0, - event = DESIGN_VERSION_EVENT.NO_CHANGE, - isUploading = false, - imageLoading = false, - } = {}) { - wrapper = shallowMount(Item, { - localVue, - router, - propsData: { - id: 1, - filename: 'test', - image: 'http://via.placeholder.com/300', - isUploading, - event, - notesCount, - updatedAt: '01-01-2019', - }, - data() { - return { - imageLoading, - }; - }, - stubs: ['router-link'], - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when item is not in view', () => { - it('image is not rendered', () => { - createComponent(); - - const image = wrapper.find('img'); - expect(image.attributes('src')).toBe(''); - }); - }); - - describe('when item appears in view', () => { - let image; - let glIntersectionObserver; - - beforeEach(() => { - createComponent(); - image = wrapper.find('img'); - glIntersectionObserver = wrapper.find(GlIntersectionObserver); - - glIntersectionObserver.vm.$emit('appear'); - return wrapper.vm.$nextTick(); - }); - - describe('before image is loaded', () => { - it('renders loading spinner', () => { - expect(wrapper.find(GlLoadingIcon)).toExist(); - }); - }); - - describe('after image is loaded', () => { - beforeEach(() => { - image.trigger('load'); - return wrapper.vm.$nextTick(); - }); - - it('renders an image', () => { - expect(image.attributes('src')).toBe('http://via.placeholder.com/300'); - expect(image.isVisible()).toBe(true); - }); - - it('renders media broken icon when image onerror triggered', () => { - image.trigger('error'); - return wrapper.vm.$nextTick().then(() => { - expect(image.isVisible()).toBe(false); - expect(wrapper.find(GlIcon).element).toMatchSnapshot(); - }); - }); - - describe('when imageV432x230 and image provided', () => { - it('renders imageV432x230 image', () => { - const mockSrc = 'mock-imageV432x230-url'; - wrapper.setProps({ imageV432x230: mockSrc }); - - return wrapper.vm.$nextTick().then(() => { - expect(image.attributes('src')).toBe(mockSrc); - }); - }); - }); - - describe('when image disappears from view and then reappears', () => { - beforeEach(() => { - glIntersectionObserver.vm.$emit('appear'); - return wrapper.vm.$nextTick(); - }); - - it('renders an image', () => { - expect(image.isVisible()).toBe(true); - }); - }); - }); - }); - - describe('with notes', () => { - it('renders item with single comment', () => { - createComponent({ notesCount: 1 }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders item with multiple comments', () => { - createComponent({ notesCount: 2 }); - - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders loading spinner when isUploading is true', () => { - createComponent({ isUploading: true }); - - expect(findLoadingIcon().exists()).toBe(true); - }); - - it('renders item with no status icon for none event', () => { - createComponent(); - - expect(findDesignEvent().exists()).toBe(false); - }); - - describe('with associated event', () => { - it.each` - event | icon | className - ${DESIGN_VERSION_EVENT.MODIFICATION} | ${'file-modified-solid'} | ${'text-primary-500'} - ${DESIGN_VERSION_EVENT.DELETION} | ${'file-deletion-solid'} | ${'text-danger-500'} - ${DESIGN_VERSION_EVENT.CREATION} | ${'file-addition-solid'} | ${'text-success-500'} - `('renders item with correct status icon for $event event', ({ event, icon, className }) => { - createComponent({ event }); - const eventIcon = findEventIcon(); - - expect(eventIcon.exists()).toBe(true); - expect(eventIcon.props('name')).toBe(icon); - expect(eventIcon.classes()).toContain(className); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/index_spec.js.snap b/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/index_spec.js.snap deleted file mode 100644 index e55cff8de3d..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/index_spec.js.snap +++ /dev/null @@ -1,61 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management toolbar component renders design and updated data 1`] = ` -<header - class="d-flex p-2 bg-white align-items-center js-design-header" -> - <a - aria-label="Go back to designs" - class="mr-3 text-plain d-flex justify-content-center align-items-center" - > - <icon-stub - name="close" - size="18" - /> - </a> - - <div - class="overflow-hidden d-flex align-items-center" - > - <h2 - class="m-0 str-truncated-100 gl-font-base" - > - test.jpg - </h2> - - <small - class="text-secondary" - > - Updated 1 hour ago by Test Name - </small> - </div> - - <pagination-stub - class="ml-auto flex-shrink-0" - id="1" - /> - - <gl-deprecated-button-stub - class="mr-2" - href="/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d" - size="md" - variant="secondary" - > - <icon-stub - name="download" - size="18" - /> - </gl-deprecated-button-stub> - - <delete-button-stub - buttonclass="" - buttonvariant="danger" - hasselecteddesigns="true" - > - <icon-stub - name="remove" - size="18" - /> - </delete-button-stub> -</header> -`; diff --git a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_button_spec.js.snap b/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_button_spec.js.snap deleted file mode 100644 index 08662a04f15..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_button_spec.js.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management pagination button component disables button when no design is passed 1`] = ` -<router-link-stub - aria-label="Test title" - class="btn btn-default disabled" - disabled="true" - to="[object Object]" -> - <icon-stub - name="angle-right" - size="16" - /> -</router-link-stub> -`; - -exports[`Design management pagination button component renders router-link 1`] = ` -<router-link-stub - aria-label="Test title" - class="btn btn-default" - to="[object Object]" -> - <icon-stub - name="angle-right" - size="16" - /> -</router-link-stub> -`; diff --git a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_spec.js.snap b/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_spec.js.snap deleted file mode 100644 index 0197b4bff79..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/__snapshots__/pagination_spec.js.snap +++ /dev/null @@ -1,29 +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 pagination buttons 1`] = ` -<div - class="d-flex align-items-center" -> - - 0 of 2 - - <div - class="btn-group ml-3 mr-3" - > - <pagination-button-stub - class="js-previous-design" - iconname="angle-left" - title="Go to previous design" - /> - - <pagination-button-stub - class="js-next-design" - design="[object Object]" - iconname="angle-right" - title="Go to next design" - /> - </div> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/toolbar/index_spec.js b/spec/frontend/design_management_legacy/components/toolbar/index_spec.js deleted file mode 100644 index 8207cad4136..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/index_spec.js +++ /dev/null @@ -1,123 +0,0 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import VueRouter from 'vue-router'; -import { GlDeprecatedButton } from '@gitlab/ui'; -import Toolbar from '~/design_management_legacy/components/toolbar/index.vue'; -import DeleteButton from '~/design_management_legacy/components/delete_button.vue'; -import { DESIGNS_ROUTE_NAME } from '~/design_management_legacy/router/constants'; - -const localVue = createLocalVue(); -localVue.use(VueRouter); -const router = new VueRouter(); - -const RouterLinkStub = { - props: { - to: { - type: Object, - }, - }, - render(createElement) { - return createElement('a', {}, this.$slots.default); - }, -}; - -describe('Design management toolbar component', () => { - let wrapper; - - function createComponent(isLoading = false, createDesign = true, props) { - const updatedAt = new Date(); - updatedAt.setHours(updatedAt.getHours() - 1); - - wrapper = shallowMount(Toolbar, { - localVue, - router, - propsData: { - id: '1', - isLatestVersion: true, - isLoading, - isDeleting: false, - filename: 'test.jpg', - updatedAt: updatedAt.toString(), - updatedBy: { - name: 'Test Name', - }, - image: '/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d', - ...props, - }, - stubs: { - 'router-link': RouterLinkStub, - }, - }); - - wrapper.setData({ - permissions: { - createDesign, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders design and updated data', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('links back to designs list', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - const link = wrapper.find('a'); - - expect(link.props('to')).toEqual({ - name: DESIGNS_ROUTE_NAME, - query: { - version: undefined, - }, - }); - }); - }); - - it('renders delete button on latest designs version with logged in user', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(DeleteButton).exists()).toBe(true); - }); - }); - - it('does not render delete button on non-latest version', () => { - createComponent(false, true, { isLatestVersion: false }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(DeleteButton).exists()).toBe(false); - }); - }); - - it('does not render delete button when user is not logged in', () => { - createComponent(false, false); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(DeleteButton).exists()).toBe(false); - }); - }); - - it('emits `delete` event on deleteButton `deleteSelectedDesigns` event', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - wrapper.find(DeleteButton).vm.$emit('deleteSelectedDesigns'); - expect(wrapper.emitted().delete).toBeTruthy(); - }); - }); - - it('renders download button with correct link', () => { - expect(wrapper.find(GlDeprecatedButton).attributes('href')).toBe( - '/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d', - ); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/toolbar/pagination_button_spec.js b/spec/frontend/design_management_legacy/components/toolbar/pagination_button_spec.js deleted file mode 100644 index d2153adca45..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/pagination_button_spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import VueRouter from 'vue-router'; -import PaginationButton from '~/design_management_legacy/components/toolbar/pagination_button.vue'; -import { DESIGN_ROUTE_NAME } from '~/design_management_legacy/router/constants'; - -const localVue = createLocalVue(); -localVue.use(VueRouter); -const router = new VueRouter(); - -describe('Design management pagination button component', () => { - let wrapper; - - function createComponent(design = null) { - wrapper = shallowMount(PaginationButton, { - localVue, - router, - propsData: { - design, - title: 'Test title', - iconName: 'angle-right', - }, - stubs: ['router-link'], - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('disables button when no design is passed', () => { - createComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders router-link', () => { - createComponent({ id: '2' }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - describe('designLink', () => { - it('returns empty link when design is null', () => { - createComponent(); - - expect(wrapper.vm.designLink).toEqual({}); - }); - - it('returns design link', () => { - createComponent({ id: '2', filename: 'test' }); - - wrapper.vm.$router.replace('/root/test-project/issues/1/designs/test?version=1'); - - expect(wrapper.vm.designLink).toEqual({ - name: DESIGN_ROUTE_NAME, - params: { id: 'test' }, - query: { version: '1' }, - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/toolbar/pagination_spec.js b/spec/frontend/design_management_legacy/components/toolbar/pagination_spec.js deleted file mode 100644 index 21b55113a6e..00000000000 --- a/spec/frontend/design_management_legacy/components/toolbar/pagination_spec.js +++ /dev/null @@ -1,79 +0,0 @@ -/* global Mousetrap */ -import 'mousetrap'; -import { shallowMount } from '@vue/test-utils'; -import Pagination from '~/design_management_legacy/components/toolbar/pagination.vue'; -import { DESIGN_ROUTE_NAME } from '~/design_management_legacy/router/constants'; - -const push = jest.fn(); -const $router = { - push, -}; - -const $route = { - path: '/designs/design-2', - query: {}, -}; - -describe('Design management pagination component', () => { - let wrapper; - - function createComponent() { - wrapper = shallowMount(Pagination, { - propsData: { - id: '2', - }, - mocks: { - $router, - $route, - }, - }); - } - - beforeEach(() => { - createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('hides components when designs are empty', () => { - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders pagination buttons', () => { - wrapper.setData({ - designs: [{ id: '1' }, { id: '2' }], - }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('keyboard buttons navigation', () => { - beforeEach(() => { - wrapper.setData({ - designs: [{ filename: '1' }, { filename: '2' }, { filename: '3' }], - }); - }); - - it('routes to previous design on Left button', () => { - Mousetrap.trigger('left'); - expect(push).toHaveBeenCalledWith({ - name: DESIGN_ROUTE_NAME, - params: { id: '1' }, - query: {}, - }); - }); - - it('routes to next design on Right button', () => { - Mousetrap.trigger('right'); - expect(push).toHaveBeenCalledWith({ - name: DESIGN_ROUTE_NAME, - params: { id: '3' }, - query: {}, - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/upload/__snapshots__/button_spec.js.snap b/spec/frontend/design_management_legacy/components/upload/__snapshots__/button_spec.js.snap deleted file mode 100644 index 27c0ba589e6..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/__snapshots__/button_spec.js.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management upload button component renders inverted upload design button 1`] = ` -<div - isinverted="true" -> - <gl-deprecated-button-stub - size="md" - title="Adding a design with the same filename replaces the file in a new version." - variant="success" - > - - Upload designs - - <!----> - </gl-deprecated-button-stub> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> -</div> -`; - -exports[`Design management upload button component renders loading icon 1`] = ` -<div> - <gl-deprecated-button-stub - disabled="true" - size="md" - title="Adding a design with the same filename replaces the file in a new version." - variant="success" - > - - Upload designs - - <gl-loading-icon-stub - class="ml-1" - color="orange" - inline="true" - label="Loading" - size="sm" - /> - </gl-deprecated-button-stub> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> -</div> -`; - -exports[`Design management upload button component renders upload design button 1`] = ` -<div> - <gl-deprecated-button-stub - size="md" - title="Adding a design with the same filename replaces the file in a new version." - variant="success" - > - - Upload designs - - <!----> - </gl-deprecated-button-stub> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_dropzone_spec.js.snap b/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_dropzone_spec.js.snap deleted file mode 100644 index 0737b9729a2..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_dropzone_spec.js.snap +++ /dev/null @@ -1,455 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management dropzone component when dragging renders correct template when drag event contains files 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="" - > - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when dragging renders correct template when drag event contains files and text 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="" - > - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when dragging renders correct template when drag event contains text 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="" - > - <div - class="mw-50 text-center" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when dragging renders correct template when drag event is empty 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="" - > - <div - class="mw-50 text-center" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when dragging renders correct template when dragging stops 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="display: none;" - > - <div - class="mw-50 text-center" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when no slot provided renders default dropzone card 1`] = ` -<div - class="w-100 position-relative" -> - <button - class="card design-dropzone-card design-dropzone-border w-100 h-100 d-flex-center p-3" - > - <div - class="d-flex-center flex-column text-center" - > - <gl-icon-stub - class="mb-4" - name="doc-new" - size="48" - /> - - <p> - <gl-sprintf-stub - message="%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}." - /> - </p> - </div> - </button> - - <input - accept="image/*" - class="hide" - multiple="multiple" - name="design_file" - type="file" - /> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="display: none;" - > - <div - class="mw-50 text-center" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; - -exports[`Design management dropzone component when slot provided renders dropzone with slot content 1`] = ` -<div - class="w-100 position-relative" -> - <div> - dropzone slot - </div> - - <transition-stub - name="design-dropzone-fade" - > - <div - class="card design-dropzone-border design-dropzone-overlay w-100 h-100 position-absolute d-flex-center p-3 bg-white" - style="display: none;" - > - <div - class="mw-50 text-center" - > - <h3> - Oh no! - </h3> - - <span> - You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico. - </span> - </div> - - <div - class="mw-50 text-center" - style="display: none;" - > - <h3> - Incoming! - </h3> - - <span> - Drop your designs to start your upload. - </span> - </div> - </div> - </transition-stub> -</div> -`; diff --git a/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_version_dropdown_spec.js.snap b/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_version_dropdown_spec.js.snap deleted file mode 100644 index d34b925f33d..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/__snapshots__/design_version_dropdown_spec.js.snap +++ /dev/null @@ -1,111 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management design version dropdown component renders design version dropdown button 1`] = ` -<gl-deprecated-dropdown-stub - class="design-version-dropdown" - issueiid="" - projectpath="" - text="Showing Latest Version" - variant="link" -> - <gl-deprecated-dropdown-item-stub> - <router-link-stub - class="d-flex js-version-link" - to="[object Object]" - > - <div - class="flex-grow-1 ml-2" - > - <div> - <strong> - Version 2 - - <span> - (latest) - </span> - </strong> - </div> - </div> - - <i - class="fa fa-check float-right gl-mr-2" - /> - </router-link-stub> - </gl-deprecated-dropdown-item-stub> - <gl-deprecated-dropdown-item-stub> - <router-link-stub - class="d-flex js-version-link" - to="[object Object]" - > - <div - class="flex-grow-1 ml-2" - > - <div> - <strong> - Version 1 - - <!----> - </strong> - </div> - </div> - - <!----> - </router-link-stub> - </gl-deprecated-dropdown-item-stub> -</gl-deprecated-dropdown-stub> -`; - -exports[`Design management design version dropdown component renders design version list 1`] = ` -<gl-deprecated-dropdown-stub - class="design-version-dropdown" - issueiid="" - projectpath="" - text="Showing Latest Version" - variant="link" -> - <gl-deprecated-dropdown-item-stub> - <router-link-stub - class="d-flex js-version-link" - to="[object Object]" - > - <div - class="flex-grow-1 ml-2" - > - <div> - <strong> - Version 2 - - <span> - (latest) - </span> - </strong> - </div> - </div> - - <i - class="fa fa-check float-right gl-mr-2" - /> - </router-link-stub> - </gl-deprecated-dropdown-item-stub> - <gl-deprecated-dropdown-item-stub> - <router-link-stub - class="d-flex js-version-link" - to="[object Object]" - > - <div - class="flex-grow-1 ml-2" - > - <div> - <strong> - Version 1 - - <!----> - </strong> - </div> - </div> - - <!----> - </router-link-stub> - </gl-deprecated-dropdown-item-stub> -</gl-deprecated-dropdown-stub> -`; diff --git a/spec/frontend/design_management_legacy/components/upload/button_spec.js b/spec/frontend/design_management_legacy/components/upload/button_spec.js deleted file mode 100644 index dde5c694194..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/button_spec.js +++ /dev/null @@ -1,59 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import UploadButton from '~/design_management_legacy/components/upload/button.vue'; - -describe('Design management upload button component', () => { - let wrapper; - - function createComponent(isSaving = false, isInverted = false) { - wrapper = shallowMount(UploadButton, { - propsData: { - isSaving, - isInverted, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders upload design button', () => { - createComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders inverted upload design button', () => { - createComponent(false, true); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders loading icon', () => { - createComponent(true); - - expect(wrapper.element).toMatchSnapshot(); - }); - - describe('onFileUploadChange', () => { - it('emits upload event', () => { - createComponent(); - - wrapper.vm.onFileUploadChange({ target: { files: 'test' } }); - - expect(wrapper.emitted().upload[0]).toEqual(['test']); - }); - }); - - describe('openFileUpload', () => { - it('triggers click on input', () => { - createComponent(); - - const clickSpy = jest.spyOn(wrapper.find('input').element, 'click'); - - wrapper.vm.openFileUpload(); - - expect(clickSpy).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/upload/design_dropzone_spec.js b/spec/frontend/design_management_legacy/components/upload/design_dropzone_spec.js deleted file mode 100644 index 1907a3124a6..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/design_dropzone_spec.js +++ /dev/null @@ -1,132 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DesignDropzone from '~/design_management_legacy/components/upload/design_dropzone.vue'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; - -jest.mock('~/flash'); - -describe('Design management dropzone component', () => { - let wrapper; - - const mockDragEvent = ({ types = ['Files'], files = [] }) => { - return { dataTransfer: { types, files } }; - }; - - const findDropzoneCard = () => wrapper.find('.design-dropzone-card'); - - function createComponent({ slots = {}, data = {} } = {}) { - wrapper = shallowMount(DesignDropzone, { - slots, - data() { - return data; - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when slot provided', () => { - it('renders dropzone with slot content', () => { - createComponent({ - slots: { - default: ['<div>dropzone slot</div>'], - }, - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('when no slot provided', () => { - it('renders default dropzone card', () => { - createComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('triggers click event on file input element when clicked', () => { - createComponent(); - const clickSpy = jest.spyOn(wrapper.find('input').element, 'click'); - - findDropzoneCard().trigger('click'); - expect(clickSpy).toHaveBeenCalled(); - }); - }); - - describe('when dragging', () => { - it.each` - description | eventPayload - ${'is empty'} | ${{}} - ${'contains text'} | ${mockDragEvent({ types: ['text'] })} - ${'contains files and text'} | ${mockDragEvent({ types: ['Files', 'text'] })} - ${'contains files'} | ${mockDragEvent({ types: ['Files'] })} - `('renders correct template when drag event $description', ({ eventPayload }) => { - createComponent(); - - wrapper.trigger('dragenter', eventPayload); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders correct template when dragging stops', () => { - createComponent(); - - wrapper.trigger('dragenter'); - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.trigger('dragleave'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - }); - - describe('when dropping', () => { - it('emits upload event', () => { - createComponent(); - const mockFile = { name: 'test', type: 'image/jpg' }; - const mockEvent = mockDragEvent({ files: [mockFile] }); - - wrapper.trigger('dragenter', mockEvent); - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.trigger('drop', mockEvent); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.emitted().change[0]).toEqual([[mockFile]]); - }); - }); - }); - - describe('ondrop', () => { - const mockData = { dragCounter: 1, isDragDataValid: true }; - - describe('when drag data is valid', () => { - it('emits upload event for valid files', () => { - createComponent({ data: mockData }); - - const mockFile = { type: 'image/jpg' }; - const mockEvent = mockDragEvent({ files: [mockFile] }); - - wrapper.vm.ondrop(mockEvent); - expect(wrapper.emitted().change[0]).toEqual([[mockFile]]); - }); - - it('calls createFlash when files are invalid', () => { - createComponent({ data: mockData }); - - const mockEvent = mockDragEvent({ files: [{ type: 'audio/midi' }] }); - - wrapper.vm.ondrop(mockEvent); - expect(createFlash).toHaveBeenCalledTimes(1); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/upload/design_version_dropdown_spec.js b/spec/frontend/design_management_legacy/components/upload/design_version_dropdown_spec.js deleted file mode 100644 index 7fb85f357c7..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/design_version_dropdown_spec.js +++ /dev/null @@ -1,122 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlDeprecatedDropdown, GlDeprecatedDropdownItem } from '@gitlab/ui'; -import DesignVersionDropdown from '~/design_management_legacy/components/upload/design_version_dropdown.vue'; -import mockAllVersions from './mock_data/all_versions'; - -const LATEST_VERSION_ID = 3; -const PREVIOUS_VERSION_ID = 2; - -const designRouteFactory = versionId => ({ - path: `/designs?version=${versionId}`, - query: { - version: `${versionId}`, - }, -}); - -const MOCK_ROUTE = { - path: '/designs', - query: {}, -}; - -describe('Design management design version dropdown component', () => { - let wrapper; - - function createComponent({ maxVersions = -1, $route = MOCK_ROUTE } = {}) { - wrapper = shallowMount(DesignVersionDropdown, { - propsData: { - projectPath: '', - issueIid: '', - }, - mocks: { - $route, - }, - stubs: ['router-link'], - }); - - wrapper.setData({ - allVersions: maxVersions > -1 ? mockAllVersions.slice(0, maxVersions) : mockAllVersions, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - const findVersionLink = index => wrapper.findAll('.js-version-link').at(index); - - it('renders design version dropdown button', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders design version list', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('selected version name', () => { - it('has "latest" on most recent version item', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(findVersionLink(0).text()).toContain('latest'); - }); - }); - }); - - describe('versions list', () => { - it('displays latest version text by default', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(GlDeprecatedDropdown).attributes('text')).toBe( - 'Showing Latest Version', - ); - }); - }); - - it('displays latest version text when only 1 version is present', () => { - createComponent({ maxVersions: 1 }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(GlDeprecatedDropdown).attributes('text')).toBe( - 'Showing Latest Version', - ); - }); - }); - - it('displays version text when the current version is not the latest', () => { - createComponent({ $route: designRouteFactory(PREVIOUS_VERSION_ID) }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(GlDeprecatedDropdown).attributes('text')).toBe(`Showing Version #1`); - }); - }); - - it('displays latest version text when the current version is the latest', () => { - createComponent({ $route: designRouteFactory(LATEST_VERSION_ID) }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(GlDeprecatedDropdown).attributes('text')).toBe( - 'Showing Latest Version', - ); - }); - }); - - it('should have the same length as apollo query', () => { - createComponent(); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.findAll(GlDeprecatedDropdownItem)).toHaveLength( - wrapper.vm.allVersions.length, - ); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/components/upload/mock_data/all_versions.js b/spec/frontend/design_management_legacy/components/upload/mock_data/all_versions.js deleted file mode 100644 index e76bbd261bd..00000000000 --- a/spec/frontend/design_management_legacy/components/upload/mock_data/all_versions.js +++ /dev/null @@ -1,14 +0,0 @@ -export default [ - { - node: { - id: 'gid://gitlab/DesignManagement::Version/3', - sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55', - }, - }, - { - node: { - id: 'gid://gitlab/DesignManagement::Version/2', - sha: '5b063fef0cd7213b312db65b30e24f057df21b20', - }, - }, -]; diff --git a/spec/frontend/design_management_legacy/mock_data/all_versions.js b/spec/frontend/design_management_legacy/mock_data/all_versions.js deleted file mode 100644 index c389fdb8747..00000000000 --- a/spec/frontend/design_management_legacy/mock_data/all_versions.js +++ /dev/null @@ -1,8 +0,0 @@ -export default [ - { - node: { - id: 'gid://gitlab/DesignManagement::Version/1', - sha: 'b389071a06c153509e11da1f582005b316667001', - }, - }, -]; diff --git a/spec/frontend/design_management_legacy/mock_data/design.js b/spec/frontend/design_management_legacy/mock_data/design.js deleted file mode 100644 index 675198b9408..00000000000 --- a/spec/frontend/design_management_legacy/mock_data/design.js +++ /dev/null @@ -1,74 +0,0 @@ -export default { - id: 'design-id', - filename: 'test.jpg', - fullPath: 'full-design-path', - image: 'test.jpg', - updatedAt: '01-01-2019', - updatedBy: { - name: 'test', - }, - issue: { - title: 'My precious issue', - webPath: 'full-issue-path', - webUrl: 'full-issue-url', - participants: { - edges: [ - { - node: { - name: 'Administrator', - username: 'root', - webUrl: 'link-to-author', - avatarUrl: 'link-to-avatar', - }, - }, - ], - }, - }, - discussions: { - nodes: [ - { - id: 'discussion-id', - replyId: 'discussion-reply-id', - resolved: false, - notes: { - nodes: [ - { - id: 'note-id', - body: '123', - author: { - name: 'Administrator', - username: 'root', - webUrl: 'link-to-author', - avatarUrl: 'link-to-avatar', - }, - }, - ], - }, - }, - { - id: 'discussion-resolved', - replyId: 'discussion-reply-resolved', - resolved: true, - notes: { - nodes: [ - { - id: 'note-resolved', - body: '123', - author: { - name: 'Administrator', - username: 'root', - webUrl: 'link-to-author', - avatarUrl: 'link-to-avatar', - }, - }, - ], - }, - }, - ], - }, - diffRefs: { - headSha: 'headSha', - baseSha: 'baseSha', - startSha: 'startSha', - }, -}; diff --git a/spec/frontend/design_management_legacy/mock_data/designs.js b/spec/frontend/design_management_legacy/mock_data/designs.js deleted file mode 100644 index 07f5c1b7457..00000000000 --- a/spec/frontend/design_management_legacy/mock_data/designs.js +++ /dev/null @@ -1,17 +0,0 @@ -import design from './design'; - -export default { - project: { - issue: { - designCollection: { - designs: { - edges: [ - { - node: design, - }, - ], - }, - }, - }, - }, -}; diff --git a/spec/frontend/design_management_legacy/mock_data/no_designs.js b/spec/frontend/design_management_legacy/mock_data/no_designs.js deleted file mode 100644 index 9db0ffcade2..00000000000 --- a/spec/frontend/design_management_legacy/mock_data/no_designs.js +++ /dev/null @@ -1,11 +0,0 @@ -export default { - project: { - issue: { - designCollection: { - designs: { - edges: [], - }, - }, - }, - }, -}; diff --git a/spec/frontend/design_management_legacy/mock_data/notes.js b/spec/frontend/design_management_legacy/mock_data/notes.js deleted file mode 100644 index 80cb3944786..00000000000 --- a/spec/frontend/design_management_legacy/mock_data/notes.js +++ /dev/null @@ -1,46 +0,0 @@ -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: { - id: 'discussion-id-1', - }, - 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, - }, -]; diff --git a/spec/frontend/design_management_legacy/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management_legacy/pages/__snapshots__/index_spec.js.snap deleted file mode 100644 index 3ba63fd14f0..00000000000 --- a/spec/frontend/design_management_legacy/pages/__snapshots__/index_spec.js.snap +++ /dev/null @@ -1,263 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management index page designs does not render toolbar when there is no permission 1`] = ` -<div> - <!----> - - <div - class="mt-4" - > - <ol - class="list-unstyled row" - > - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub - class="design-list-item" - /> - </li> - - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-1-name" - id="design-1" - image="design-1-image" - notescount="0" - /> - </design-dropzone-stub> - - <!----> - </li> - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-2-name" - id="design-2" - image="design-2-image" - notescount="1" - /> - </design-dropzone-stub> - - <!----> - </li> - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-3-name" - id="design-3" - image="design-3-image" - notescount="0" - /> - </design-dropzone-stub> - - <!----> - </li> - </ol> - </div> - - <router-view-stub - name="default" - /> -</div> -`; - -exports[`Design management index page designs renders designs list and header with upload button 1`] = ` -<div> - <header - class="row-content-block border-top-0 p-2 d-flex" - > - <div - class="d-flex justify-content-between align-items-center w-100" - > - <design-version-dropdown-stub /> - - <div - class="qa-selector-toolbar d-flex" - > - <gl-deprecated-button-stub - class="mr-2 js-select-all" - size="md" - variant="link" - > - Select all - </gl-deprecated-button-stub> - - <div> - <delete-button-stub - buttonclass="btn-danger btn-inverted mr-2" - buttonvariant="" - > - - Delete selected - - <!----> - </delete-button-stub> - </div> - - <upload-button-stub /> - </div> - </div> - </header> - - <div - class="mt-4" - > - <ol - class="list-unstyled row" - > - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub - class="design-list-item" - /> - </li> - - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-1-name" - id="design-1" - image="design-1-image" - notescount="0" - /> - </design-dropzone-stub> - - <input - class="design-checkbox" - type="checkbox" - /> - </li> - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-2-name" - id="design-2" - image="design-2-image" - notescount="1" - /> - </design-dropzone-stub> - - <input - class="design-checkbox" - type="checkbox" - /> - </li> - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub> - <design-stub - event="NONE" - filename="design-3-name" - id="design-3" - image="design-3-image" - notescount="0" - /> - </design-dropzone-stub> - - <input - class="design-checkbox" - type="checkbox" - /> - </li> - </ol> - </div> - - <router-view-stub - name="default" - /> -</div> -`; - -exports[`Design management index page designs renders error 1`] = ` -<div> - <!----> - - <div - class="mt-4" - > - <gl-alert-stub - dismisslabel="Dismiss" - primarybuttonlink="" - primarybuttontext="" - secondarybuttonlink="" - secondarybuttontext="" - title="" - variant="danger" - > - - An error occurred while loading designs. Please try again. - - </gl-alert-stub> - </div> - - <router-view-stub - name="default" - /> -</div> -`; - -exports[`Design management index page designs renders loading icon 1`] = ` -<div> - <!----> - - <div - class="mt-4" - > - <gl-loading-icon-stub - color="orange" - label="Loading" - size="md" - /> - </div> - - <router-view-stub - name="default" - /> -</div> -`; - -exports[`Design management index page when has no designs renders empty text 1`] = ` -<div> - <!----> - - <div - class="mt-4" - > - <ol - class="list-unstyled row" - > - <li - class="col-md-6 col-lg-4 mb-3" - > - <design-dropzone-stub - class="design-list-item" - /> - </li> - - </ol> - </div> - - <router-view-stub - name="default" - /> -</div> -`; diff --git a/spec/frontend/design_management_legacy/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management_legacy/pages/design/__snapshots__/index_spec.js.snap deleted file mode 100644 index dc5baf37fc6..00000000000 --- a/spec/frontend/design_management_legacy/pages/design/__snapshots__/index_spec.js.snap +++ /dev/null @@ -1,216 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Design management design index page renders design index 1`] = ` -<div - class="design-detail js-design-detail fixed-top w-100 position-bottom-0 d-flex justify-content-center flex-column flex-lg-row" -> - <div - class="d-flex overflow-hidden flex-grow-1 flex-column position-relative" - > - <design-destroyer-stub - filenames="test.jpg" - iid="1" - projectpath="" - /> - - <!----> - - <design-presentation-stub - discussions="[object Object],[object Object]" - image="test.jpg" - imagename="test.jpg" - scale="1" - /> - - <div - class="design-scaler-wrapper position-absolute mb-4 d-flex-center" - > - <design-scaler-stub /> - </div> - </div> - - <div - class="image-notes" - > - <h2 - class="gl-font-weight-bold gl-mt-0" - > - - My precious issue - - </h2> - - <a - class="gl-text-gray-400 gl-text-decoration-none gl-mb-6 gl-display-block" - href="full-issue-url" - > - ull-issue-path - </a> - - <participants-stub - class="gl-mb-4" - numberoflessparticipants="7" - participants="[object Object]" - /> - - <!----> - - <design-discussion-stub - data-testid="unresolved-discussion" - designid="test" - discussion="[object Object]" - discussionwithopenform="" - markdownpreviewpath="//preview_markdown?target_type=Issue" - noteableid="design-id" - /> - - <gl-button-stub - category="primary" - class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-mb-4" - data-testid="resolved-comments" - icon="chevron-right" - id="resolved-comments" - size="medium" - variant="link" - > - Resolved Comments (1) - - </gl-button-stub> - - <gl-popover-stub - container="popovercontainer" - cssclasses="" - placement="top" - show="true" - target="resolved-comments" - title="Resolved Comments" - > - <p> - - Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below - - </p> - - <a - href="#" - rel="noopener noreferrer" - target="_blank" - > - Learn more about resolving comments - </a> - </gl-popover-stub> - - <gl-collapse-stub - class="gl-mt-3" - > - <design-discussion-stub - data-testid="resolved-discussion" - designid="test" - discussion="[object Object]" - discussionwithopenform="" - markdownpreviewpath="//preview_markdown?target_type=Issue" - noteableid="design-id" - /> - </gl-collapse-stub> - - </div> -</div> -`; - -exports[`Design management design index page sets loading state 1`] = ` -<div - class="design-detail js-design-detail fixed-top w-100 position-bottom-0 d-flex justify-content-center flex-column flex-lg-row" -> - <gl-loading-icon-stub - class="align-self-center" - color="orange" - label="Loading" - size="xl" - /> -</div> -`; - -exports[`Design management design index page with error GlAlert is rendered in correct position with correct content 1`] = ` -<div - class="design-detail js-design-detail fixed-top w-100 position-bottom-0 d-flex justify-content-center flex-column flex-lg-row" -> - <div - class="d-flex overflow-hidden flex-grow-1 flex-column position-relative" - > - <design-destroyer-stub - filenames="test.jpg" - iid="1" - projectpath="" - /> - - <div - class="p-3" - > - <gl-alert-stub - dismissible="true" - dismisslabel="Dismiss" - primarybuttonlink="" - primarybuttontext="" - secondarybuttonlink="" - secondarybuttontext="" - title="" - variant="danger" - > - - woops - - </gl-alert-stub> - </div> - - <design-presentation-stub - discussions="" - image="test.jpg" - imagename="test.jpg" - scale="1" - /> - - <div - class="design-scaler-wrapper position-absolute mb-4 d-flex-center" - > - <design-scaler-stub /> - </div> - </div> - - <div - class="image-notes" - > - <h2 - class="gl-font-weight-bold gl-mt-0" - > - - My precious issue - - </h2> - - <a - class="gl-text-gray-400 gl-text-decoration-none gl-mb-6 gl-display-block" - href="full-issue-url" - > - ull-issue-path - </a> - - <participants-stub - class="gl-mb-4" - numberoflessparticipants="7" - participants="[object Object]" - /> - - <h2 - class="new-discussion-disclaimer gl-font-base gl-m-0 gl-mb-4" - data-testid="new-discussion-disclaimer" - > - - Click the image where you'd like to start a new discussion - - </h2> - - <!----> - - </div> -</div> -`; diff --git a/spec/frontend/design_management_legacy/pages/design/index_spec.js b/spec/frontend/design_management_legacy/pages/design/index_spec.js deleted file mode 100644 index 5eb4158c715..00000000000 --- a/spec/frontend/design_management_legacy/pages/design/index_spec.js +++ /dev/null @@ -1,291 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import VueRouter from 'vue-router'; -import { GlAlert } from '@gitlab/ui'; -import { ApolloMutation } from 'vue-apollo'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; -import DesignIndex from '~/design_management_legacy/pages/design/index.vue'; -import DesignSidebar from '~/design_management_legacy/components/design_sidebar.vue'; -import DesignPresentation from '~/design_management_legacy/components/design_presentation.vue'; -import createImageDiffNoteMutation from '~/design_management_legacy/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 { - DESIGN_NOT_FOUND_ERROR, - DESIGN_VERSION_NOT_EXIST_ERROR, -} from '~/design_management_legacy/utils/error_messages'; -import { DESIGNS_ROUTE_NAME } from '~/design_management_legacy/router/constants'; -import createRouter from '~/design_management_legacy/router'; -import * as utils from '~/design_management_legacy/utils/design_management_utils'; -import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management_legacy/constants'; - -jest.mock('~/flash'); -jest.mock('mousetrap', () => ({ - bind: jest.fn(), - unbind: jest.fn(), -})); - -const focusInput = jest.fn(); - -const DesignReplyForm = { - template: '<div><textarea ref="textarea"></textarea></div>', - methods: { - focusInput, - }, -}; - -const localVue = createLocalVue(); -localVue.use(VueRouter); - -describe('Design management design index page', () => { - let wrapper; - let router; - - const newComment = 'new comment'; - const annotationCoordinates = { - x: 10, - y: 10, - width: 100, - height: 100, - }; - const createDiscussionMutationVariables = { - mutation: createImageDiffNoteMutation, - update: expect.anything(), - variables: { - input: { - body: newComment, - noteableId: design.id, - position: { - headSha: 'headSha', - baseSha: 'baseSha', - startSha: 'startSha', - paths: { - newPath: 'full-design-path', - }, - ...annotationCoordinates, - }, - }, - }, - }; - - const mutate = jest.fn().mockResolvedValue(); - - const findDiscussionForm = () => wrapper.find(DesignReplyForm); - const findSidebar = () => wrapper.find(DesignSidebar); - const findDesignPresentation = () => wrapper.find(DesignPresentation); - - function createComponent(loading = false, data = {}) { - const $apollo = { - queries: { - design: { - loading, - }, - }, - mutate, - }; - - router = createRouter(); - - wrapper = shallowMount(DesignIndex, { - propsData: { id: '1' }, - mocks: { $apollo }, - stubs: { - ApolloMutation, - DesignSidebar, - DesignReplyForm, - }, - data() { - return { - issueIid: '1', - activeDiscussion: { - id: null, - source: null, - }, - ...data, - }; - }, - localVue, - router, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when navigating', () => { - it('applies fullscreen layout', () => { - const mockEl = { - classList: { - add: jest.fn(), - remove: jest.fn(), - }, - }; - jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl); - createComponent(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); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('renders design index', () => { - createComponent(false, { design }); - - expect(wrapper.element).toMatchSnapshot(); - expect(wrapper.find(GlAlert).exists()).toBe(false); - }); - - it('passes correct props to sidebar component', () => { - createComponent(false, { design }); - - expect(findSidebar().props()).toEqual({ - design, - markdownPreviewPath: '//preview_markdown?target_type=Issue', - resolvedDiscussionsExpanded: false, - }); - }); - - it('opens a new discussion form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], - }, - }, - }); - - findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 }); - - return wrapper.vm.$nextTick().then(() => { - expect(findDiscussionForm().exists()).toBe(true); - }); - }); - - it('keeps new discussion form focused', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], - }, - }, - annotationCoordinates, - }); - - findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 }); - - expect(focusInput).toHaveBeenCalled(); - }); - - it('sends a mutation on submitting form and closes form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], - }, - }, - annotationCoordinates, - comment: newComment, - }); - - findDiscussionForm().vm.$emit('submitForm'); - expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables); - - return wrapper.vm - .$nextTick() - .then(() => { - return mutate({ variables: createDiscussionMutationVariables }); - }) - .then(() => { - expect(findDiscussionForm().exists()).toBe(false); - }); - }); - - it('closes the form and clears the comment on canceling form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], - }, - }, - annotationCoordinates, - comment: newComment, - }); - - findDiscussionForm().vm.$emit('cancelForm'); - - expect(wrapper.vm.comment).toBe(''); - - return wrapper.vm.$nextTick().then(() => { - expect(findDiscussionForm().exists()).toBe(false); - }); - }); - - describe('with error', () => { - beforeEach(() => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], - }, - }, - errorMessage: 'woops', - }); - }); - - it('GlAlert is rendered in correct position with correct content', () => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('onDesignQueryResult', () => { - describe('with no designs', () => { - it('redirects to /designs', () => { - createComponent(true); - router.push = jest.fn(); - - wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false }); - return wrapper.vm.$nextTick().then(() => { - expect(createFlash).toHaveBeenCalledTimes(1); - expect(createFlash).toHaveBeenCalledWith(DESIGN_NOT_FOUND_ERROR); - expect(router.push).toHaveBeenCalledTimes(1); - expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME }); - }); - }); - }); - - describe('when no design exists for given version', () => { - it('redirects to /designs', () => { - createComponent(true); - wrapper.setData({ - allVersions: mockAllVersions, - }); - - // attempt to query for a version of the design that doesn't exist - router.push({ query: { version: '999' } }); - router.push = jest.fn(); - - wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false }); - return wrapper.vm.$nextTick().then(() => { - expect(createFlash).toHaveBeenCalledTimes(1); - expect(createFlash).toHaveBeenCalledWith(DESIGN_VERSION_NOT_EXIST_ERROR); - expect(router.push).toHaveBeenCalledTimes(1); - expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME }); - }); - }); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/pages/index_spec.js b/spec/frontend/design_management_legacy/pages/index_spec.js deleted file mode 100644 index 5b7512aab7b..00000000000 --- a/spec/frontend/design_management_legacy/pages/index_spec.js +++ /dev/null @@ -1,543 +0,0 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import { ApolloMutation } from 'vue-apollo'; -import VueRouter from 'vue-router'; -import { GlEmptyState } from '@gitlab/ui'; -import Index from '~/design_management_legacy/pages/index.vue'; -import uploadDesignQuery from '~/design_management_legacy/graphql/mutations/upload_design.mutation.graphql'; -import DesignDestroyer from '~/design_management_legacy/components/design_destroyer.vue'; -import DesignDropzone from '~/design_management_legacy/components/upload/design_dropzone.vue'; -import DeleteButton from '~/design_management_legacy/components/delete_button.vue'; -import { DESIGNS_ROUTE_NAME } from '~/design_management_legacy/router/constants'; -import { - EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE, - EXISTING_DESIGN_DROP_INVALID_FILENAME_MESSAGE, -} from '~/design_management_legacy/utils/error_messages'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; -import createRouter from '~/design_management_legacy/router'; -import * as utils from '~/design_management_legacy/utils/design_management_utils'; -import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management_legacy/constants'; - -jest.mock('~/flash.js'); -const mockPageEl = { - classList: { - remove: jest.fn(), - }, -}; -jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageEl); - -const localVue = createLocalVue(); -const router = createRouter(); -localVue.use(VueRouter); - -const mockDesigns = [ - { - id: 'design-1', - image: 'design-1-image', - filename: 'design-1-name', - event: 'NONE', - notesCount: 0, - }, - { - id: 'design-2', - image: 'design-2-image', - filename: 'design-2-name', - event: 'NONE', - notesCount: 1, - }, - { - id: 'design-3', - image: 'design-3-image', - filename: 'design-3-name', - event: 'NONE', - notesCount: 0, - }, -]; - -const mockVersion = { - node: { - id: 'gid://gitlab/DesignManagement::Version/1', - }, -}; - -describe('Design management index page', () => { - let mutate; - let wrapper; - - const findDesignCheckboxes = () => wrapper.findAll('.design-checkbox'); - const findSelectAllButton = () => wrapper.find('.js-select-all'); - const findToolbar = () => wrapper.find('.qa-selector-toolbar'); - const findDeleteButton = () => wrapper.find(DeleteButton); - const findDropzone = () => wrapper.findAll(DesignDropzone).at(0); - const findFirstDropzoneWithDesign = () => wrapper.findAll(DesignDropzone).at(1); - - function createComponent({ - loading = false, - designs = [], - allVersions = [], - createDesign = true, - stubs = {}, - mockMutate = jest.fn().mockResolvedValue(), - } = {}) { - mutate = mockMutate; - const $apollo = { - queries: { - designs: { - loading, - }, - permissions: { - loading, - }, - }, - mutate, - }; - - wrapper = shallowMount(Index, { - mocks: { $apollo }, - localVue, - router, - stubs: { DesignDestroyer, ApolloMutation, ...stubs }, - attachToDocument: true, - }); - - wrapper.setData({ - designs, - allVersions, - issueIid: '1', - permissions: { - createDesign, - }, - }); - } - - afterEach(() => { - wrapper.destroy(); - }); - - describe('designs', () => { - it('renders loading icon', () => { - createComponent({ loading: true }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders error', () => { - createComponent(); - - wrapper.setData({ error: true }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('renders a toolbar with buttons when there are designs', () => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - - return wrapper.vm.$nextTick().then(() => { - expect(findToolbar().exists()).toBe(true); - }); - }); - - it('renders designs list and header with upload button', () => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - it('does not render toolbar when there is no permission', () => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion], createDesign: false }); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - }); - - describe('when has no designs', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders empty text', () => - wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - })); - - it('does not render a toolbar with buttons', () => - wrapper.vm.$nextTick().then(() => { - expect(findToolbar().exists()).toBe(false); - })); - }); - - describe('uploading designs', () => { - it('calls mutation on upload', () => { - createComponent({ stubs: { GlEmptyState } }); - - const mutationVariables = { - update: expect.anything(), - context: { - hasUpload: true, - }, - mutation: uploadDesignQuery, - variables: { - files: [{ name: 'test' }], - projectPath: '', - iid: '1', - }, - optimisticResponse: { - __typename: 'Mutation', - designManagementUpload: { - __typename: 'DesignManagementUploadPayload', - designs: [ - { - __typename: 'Design', - id: expect.anything(), - image: '', - imageV432x230: '', - filename: 'test', - fullPath: '', - event: 'NONE', - notesCount: 0, - diffRefs: { - __typename: 'DiffRefs', - baseSha: '', - startSha: '', - headSha: '', - }, - discussions: { - __typename: 'DesignDiscussion', - nodes: [], - }, - versions: { - __typename: 'DesignVersionConnection', - edges: { - __typename: 'DesignVersionEdge', - node: { - __typename: 'DesignVersion', - id: expect.anything(), - sha: expect.anything(), - }, - }, - }, - }, - ], - skippedDesigns: [], - errors: [], - }, - }, - }; - - return wrapper.vm.$nextTick().then(() => { - findDropzone().vm.$emit('change', [{ name: 'test' }]); - expect(mutate).toHaveBeenCalledWith(mutationVariables); - expect(wrapper.vm.filesToBeSaved).toEqual([{ name: 'test' }]); - expect(wrapper.vm.isSaving).toBeTruthy(); - }); - }); - - it('sets isSaving', () => { - createComponent(); - - const uploadDesign = wrapper.vm.onUploadDesign([ - { - name: 'test', - }, - ]); - - expect(wrapper.vm.isSaving).toBe(true); - - return uploadDesign.then(() => { - expect(wrapper.vm.isSaving).toBe(false); - }); - }); - - it('updates state appropriately after upload complete', () => { - createComponent({ stubs: { GlEmptyState } }); - wrapper.setData({ filesToBeSaved: [{ name: 'test' }] }); - - wrapper.vm.onUploadDesignDone(); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.filesToBeSaved).toEqual([]); - expect(wrapper.vm.isSaving).toBeFalsy(); - expect(wrapper.vm.isLatestVersion).toBe(true); - }); - }); - - it('updates state appropriately after upload error', () => { - createComponent({ stubs: { GlEmptyState } }); - wrapper.setData({ filesToBeSaved: [{ name: 'test' }] }); - - wrapper.vm.onUploadDesignError(); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.filesToBeSaved).toEqual([]); - expect(wrapper.vm.isSaving).toBeFalsy(); - expect(createFlash).toHaveBeenCalled(); - - createFlash.mockReset(); - }); - }); - - it('does not call mutation if createDesign is false', () => { - createComponent({ createDesign: false }); - - wrapper.vm.onUploadDesign([]); - - expect(mutate).not.toHaveBeenCalled(); - }); - - describe('upload count limit', () => { - const MAXIMUM_FILE_UPLOAD_LIMIT = 10; - - afterEach(() => { - createFlash.mockReset(); - }); - - it('does not warn when the max files are uploaded', () => { - createComponent(); - - wrapper.vm.onUploadDesign(new Array(MAXIMUM_FILE_UPLOAD_LIMIT).fill(mockDesigns[0])); - - expect(createFlash).not.toHaveBeenCalled(); - }); - - it('warns when too many files are uploaded', () => { - createComponent(); - - wrapper.vm.onUploadDesign(new Array(MAXIMUM_FILE_UPLOAD_LIMIT + 1).fill(mockDesigns[0])); - - expect(createFlash).toHaveBeenCalled(); - }); - }); - - it('flashes warning if designs are skipped', () => { - createComponent({ - mockMutate: () => - Promise.resolve({ - data: { designManagementUpload: { skippedDesigns: [{ filename: 'test.jpg' }] } }, - }), - }); - - const uploadDesign = wrapper.vm.onUploadDesign([ - { - name: 'test', - }, - ]); - - return uploadDesign.then(() => { - expect(createFlash).toHaveBeenCalledTimes(1); - expect(createFlash).toHaveBeenCalledWith( - 'Upload skipped. test.jpg did not change.', - 'warning', - ); - }); - }); - - describe('dragging onto an existing design', () => { - beforeEach(() => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - }); - - it('calls onUploadDesign with valid upload', () => { - wrapper.setMethods({ - onUploadDesign: jest.fn(), - }); - - const mockUploadPayload = [ - { - name: mockDesigns[0].filename, - }, - ]; - - const designDropzone = findFirstDropzoneWithDesign(); - designDropzone.vm.$emit('change', mockUploadPayload); - - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1); - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith(mockUploadPayload); - }); - - it.each` - description | eventPayload | message - ${'> 1 file'} | ${[{ name: 'test' }, { name: 'test-2' }]} | ${EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE} - ${'different filename'} | ${[{ name: 'wrong-name' }]} | ${EXISTING_DESIGN_DROP_INVALID_FILENAME_MESSAGE} - `('calls createFlash when upload has $description', ({ eventPayload, message }) => { - const designDropzone = findFirstDropzoneWithDesign(); - designDropzone.vm.$emit('change', eventPayload); - - expect(createFlash).toHaveBeenCalledTimes(1); - expect(createFlash).toHaveBeenCalledWith(message); - }); - }); - }); - - describe('on latest version when has designs', () => { - beforeEach(() => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - }); - - it('renders design checkboxes', () => { - expect(findDesignCheckboxes()).toHaveLength(mockDesigns.length); - }); - - it('renders toolbar buttons', () => { - expect(findToolbar().exists()).toBe(true); - expect(findToolbar().classes()).toContain('d-flex'); - expect(findToolbar().classes()).not.toContain('d-none'); - }); - - it('adds two designs to selected designs when their checkboxes are checked', () => { - findDesignCheckboxes() - .at(0) - .trigger('click'); - - return wrapper.vm - .$nextTick() - .then(() => { - findDesignCheckboxes() - .at(1) - .trigger('click'); - - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(findDeleteButton().exists()).toBe(true); - expect(findSelectAllButton().text()).toBe('Deselect all'); - findDeleteButton().vm.$emit('deleteSelectedDesigns'); - const [{ variables }] = mutate.mock.calls[0]; - expect(variables.filenames).toStrictEqual([ - mockDesigns[0].filename, - mockDesigns[1].filename, - ]); - }); - }); - - it('adds all designs to selected designs when Select All button is clicked', () => { - findSelectAllButton().vm.$emit('click'); - - return wrapper.vm.$nextTick().then(() => { - expect(findDeleteButton().props().hasSelectedDesigns).toBe(true); - expect(findSelectAllButton().text()).toBe('Deselect all'); - expect(wrapper.vm.selectedDesigns).toEqual(mockDesigns.map(design => design.filename)); - }); - }); - - it('removes all designs from selected designs when at least one design was selected', () => { - findDesignCheckboxes() - .at(0) - .trigger('click'); - - return wrapper.vm - .$nextTick() - .then(() => { - findSelectAllButton().vm.$emit('click'); - }) - .then(() => { - expect(findDeleteButton().props().hasSelectedDesigns).toBe(false); - expect(findSelectAllButton().text()).toBe('Select all'); - expect(wrapper.vm.selectedDesigns).toEqual([]); - }); - }); - }); - - it('on latest version when has no designs does not render toolbar buttons', () => { - createComponent({ designs: [], allVersions: [mockVersion] }); - expect(findToolbar().exists()).toBe(false); - }); - - describe('on non-latest version', () => { - beforeEach(() => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - - router.replace({ - name: DESIGNS_ROUTE_NAME, - query: { - version: '2', - }, - }); - }); - - it('does not render design checkboxes', () => { - expect(findDesignCheckboxes()).toHaveLength(0); - }); - - it('does not render Delete selected button', () => { - expect(findDeleteButton().exists()).toBe(false); - }); - - it('does not render Select All button', () => { - expect(findSelectAllButton().exists()).toBe(false); - }); - }); - - describe('pasting a design', () => { - let event; - beforeEach(() => { - createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); - - wrapper.setMethods({ - onUploadDesign: jest.fn(), - }); - - event = new Event('paste'); - - router.replace({ - name: DESIGNS_ROUTE_NAME, - query: { - version: '2', - }, - }); - }); - - it('calls onUploadDesign with valid paste', () => { - event.clipboardData = { - files: [{ name: 'image.png', type: 'image/png' }], - getData: () => 'test.png', - }; - - document.dispatchEvent(event); - - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1); - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith([ - new File([{ name: 'image.png' }], 'test.png'), - ]); - }); - - it('renames a design if it has an image.png filename', () => { - event.clipboardData = { - files: [{ name: 'image.png', type: 'image/png' }], - getData: () => 'image.png', - }; - - document.dispatchEvent(event); - - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1); - expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith([ - new File([{ name: 'image.png' }], `design_${Date.now()}.png`), - ]); - }); - - it('does not call onUploadDesign with invalid paste', () => { - event.clipboardData = { - items: [{ type: 'text/plain' }, { type: 'text' }], - files: [], - }; - - document.dispatchEvent(event); - - expect(wrapper.vm.onUploadDesign).not.toHaveBeenCalled(); - }); - }); - - describe('when navigating', () => { - it('ensures fullscreen layout is not applied', () => { - createComponent(true); - - wrapper.vm.$router.push('/designs'); - expect(mockPageEl.classList.remove).toHaveBeenCalledTimes(1); - expect(mockPageEl.classList.remove).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/router_spec.js b/spec/frontend/design_management_legacy/router_spec.js deleted file mode 100644 index 5f62793a243..00000000000 --- a/spec/frontend/design_management_legacy/router_spec.js +++ /dev/null @@ -1,82 +0,0 @@ -import { mount, createLocalVue } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import VueRouter from 'vue-router'; -import App from '~/design_management_legacy/components/app.vue'; -import Designs from '~/design_management_legacy/pages/index.vue'; -import DesignDetail from '~/design_management_legacy/pages/design/index.vue'; -import createRouter from '~/design_management_legacy/router'; -import { - ROOT_ROUTE_NAME, - DESIGNS_ROUTE_NAME, - DESIGN_ROUTE_NAME, -} from '~/design_management_legacy/router/constants'; -import '~/commons/bootstrap'; - -function factory(routeArg) { - const localVue = createLocalVue(); - localVue.use(VueRouter); - - window.gon = { sprite_icons: '' }; - - const router = createRouter('/'); - if (routeArg !== undefined) { - router.push(routeArg); - } - - return mount(App, { - localVue, - router, - mocks: { - $apollo: { - queries: { - designs: { loading: true }, - design: { loading: true }, - permissions: { loading: true }, - }, - mutate: jest.fn(), - }, - }, - }); -} - -jest.mock('mousetrap', () => ({ - bind: jest.fn(), - unbind: jest.fn(), -})); - -describe('Design management router', () => { - afterEach(() => { - window.location.hash = ''; - }); - - describe.each([['/'], [{ name: ROOT_ROUTE_NAME }]])('root route', routeArg => { - it('pushes home component', () => { - const wrapper = factory(routeArg); - - expect(wrapper.find(Designs).exists()).toBe(true); - }); - }); - - describe.each([['/designs'], [{ name: DESIGNS_ROUTE_NAME }]])('designs route', routeArg => { - it('pushes designs root component', () => { - const wrapper = factory(routeArg); - - expect(wrapper.find(Designs).exists()).toBe(true); - }); - }); - - describe.each([['/designs/1'], [{ name: DESIGN_ROUTE_NAME, params: { id: '1' } }]])( - 'designs detail route', - routeArg => { - it('pushes designs detail component', () => { - const wrapper = factory(routeArg); - - return nextTick().then(() => { - const detail = wrapper.find(DesignDetail); - expect(detail.exists()).toBe(true); - expect(detail.props('id')).toEqual('1'); - }); - }); - }, - ); -}); diff --git a/spec/frontend/design_management_legacy/utils/cache_update_spec.js b/spec/frontend/design_management_legacy/utils/cache_update_spec.js deleted file mode 100644 index dce91b5e59b..00000000000 --- a/spec/frontend/design_management_legacy/utils/cache_update_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import { InMemoryCache } from 'apollo-cache-inmemory'; -import { - updateStoreAfterDesignsDelete, - updateStoreAfterAddDiscussionComment, - updateStoreAfterAddImageDiffNote, - updateStoreAfterUploadDesign, - updateStoreAfterUpdateImageDiffNote, -} from '~/design_management_legacy/utils/cache_update'; -import { - designDeletionError, - ADD_DISCUSSION_COMMENT_ERROR, - ADD_IMAGE_DIFF_NOTE_ERROR, - UPDATE_IMAGE_DIFF_NOTE_ERROR, -} from '~/design_management_legacy/utils/error_messages'; -import design from '../mock_data/design'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; - -jest.mock('~/flash.js'); - -describe('Design Management cache update', () => { - const mockErrors = ['code red!']; - - let mockStore; - - beforeEach(() => { - mockStore = new InMemoryCache(); - }); - - 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 handles errors in response', ({ subject, extraArgs, errorMessage }) => { - expect(createFlash).not.toHaveBeenCalled(); - expect(() => subject(mockStore, { errors: mockErrors }, {}, ...extraArgs)).toThrow(); - expect(createFlash).toHaveBeenCalledTimes(1); - expect(createFlash).toHaveBeenCalledWith(errorMessage); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/utils/design_management_utils_spec.js b/spec/frontend/design_management_legacy/utils/design_management_utils_spec.js deleted file mode 100644 index 97e85a24a35..00000000000 --- a/spec/frontend/design_management_legacy/utils/design_management_utils_spec.js +++ /dev/null @@ -1,176 +0,0 @@ -import { - extractCurrentDiscussion, - extractDiscussions, - findVersionId, - designUploadOptimisticResponse, - updateImageDiffNoteOptimisticResponse, - isValidDesignFile, - extractDesign, -} from '~/design_management_legacy/utils/design_management_utils'; -import mockResponseNoDesigns from '../mock_data/no_designs'; -import mockResponseWithDesigns from '../mock_data/designs'; -import mockDesign from '../mock_data/design'; - -jest.mock('lodash/uniqueId', () => () => 1); - -describe('extractCurrentDiscussion', () => { - let discussions; - - beforeEach(() => { - discussions = { - nodes: [ - { id: 101, payload: 'w' }, - { id: 102, payload: 'x' }, - { id: 103, payload: 'y' }, - { id: 104, payload: 'z' }, - ], - }; - }); - - it('finds the relevant discussion if it exists', () => { - const id = 103; - expect(extractCurrentDiscussion(discussions, id)).toEqual({ id, payload: 'y' }); - }); - - it('returns null if the relevant discussion does not exist', () => { - expect(extractCurrentDiscussion(discussions, 0)).not.toBeDefined(); - }); -}); - -describe('extractDiscussions', () => { - let discussions; - - beforeEach(() => { - discussions = { - nodes: [ - { id: 1, notes: { nodes: ['a'] } }, - { id: 2, notes: { nodes: ['b'] } }, - { id: 3, notes: { nodes: ['c'] } }, - { id: 4, notes: { nodes: ['d'] } }, - ], - }; - }); - - it('discards the edges.node artifacts of GraphQL', () => { - expect(extractDiscussions(discussions)).toEqual([ - { id: 1, notes: ['a'], index: 1 }, - { id: 2, notes: ['b'], index: 2 }, - { id: 3, notes: ['c'], index: 3 }, - { id: 4, notes: ['d'], index: 4 }, - ]); - }); -}); - -describe('version parser', () => { - it('correctly extracts version ID from a valid version string', () => { - const testVersionId = '123'; - const testVersionString = `gid://gitlab/DesignManagement::Version/${testVersionId}`; - - expect(findVersionId(testVersionString)).toEqual(testVersionId); - }); - - it('fails to extract version ID from an invalid version string', () => { - const testInvalidVersionString = `gid://gitlab/DesignManagement::Version`; - - expect(findVersionId(testInvalidVersionString)).toBeUndefined(); - }); -}); - -describe('optimistic responses', () => { - it('correctly generated for designManagementUpload', () => { - const expectedResponse = { - __typename: 'Mutation', - designManagementUpload: { - __typename: 'DesignManagementUploadPayload', - designs: [ - { - __typename: 'Design', - id: -1, - image: '', - imageV432x230: '', - filename: 'test', - fullPath: '', - notesCount: 0, - event: 'NONE', - diffRefs: { __typename: 'DiffRefs', baseSha: '', startSha: '', headSha: '' }, - discussions: { __typename: 'DesignDiscussion', nodes: [] }, - versions: { - __typename: 'DesignVersionConnection', - edges: { - __typename: 'DesignVersionEdge', - node: { __typename: 'DesignVersion', id: -1, sha: -1 }, - }, - }, - }, - ], - errors: [], - skippedDesigns: [], - }, - }; - expect(designUploadOptimisticResponse([{ name: 'test' }])).toEqual(expectedResponse); - }); - - it('correctly generated for updateImageDiffNoteOptimisticResponse', () => { - const mockNote = { - id: 'test-note-id', - }; - - const mockPosition = { - x: 10, - y: 10, - width: 10, - height: 10, - }; - - const expectedResponse = { - __typename: 'Mutation', - updateImageDiffNote: { - __typename: 'UpdateImageDiffNotePayload', - note: { - ...mockNote, - position: mockPosition, - }, - errors: [], - }, - }; - expect(updateImageDiffNoteOptimisticResponse(mockNote, { position: mockPosition })).toEqual( - expectedResponse, - ); - }); -}); - -describe('isValidDesignFile', () => { - // test every filetype that Design Management supports - // https://docs.gitlab.com/ee/user/project/issues/design_management.html#limitations - it.each` - mimetype | isValid - ${'image/svg'} | ${true} - ${'image/png'} | ${true} - ${'image/jpg'} | ${true} - ${'image/jpeg'} | ${true} - ${'image/gif'} | ${true} - ${'image/bmp'} | ${true} - ${'image/tiff'} | ${true} - ${'image/ico'} | ${true} - ${'image/svg'} | ${true} - ${'video/mpeg'} | ${false} - ${'audio/midi'} | ${false} - ${'application/octet-stream'} | ${false} - `('returns $isValid for file type $mimetype', ({ mimetype, isValid }) => { - expect(isValidDesignFile({ type: mimetype })).toBe(isValid); - }); -}); - -describe('extractDesign', () => { - describe('with no designs', () => { - it('returns undefined', () => { - expect(extractDesign(mockResponseNoDesigns)).toBeUndefined(); - }); - }); - - describe('with designs', () => { - it('returns the first design available', () => { - expect(extractDesign(mockResponseWithDesigns)).toEqual(mockDesign); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/utils/error_messages_spec.js b/spec/frontend/design_management_legacy/utils/error_messages_spec.js deleted file mode 100644 index 489ac23da4e..00000000000 --- a/spec/frontend/design_management_legacy/utils/error_messages_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - designDeletionError, - designUploadSkippedWarning, -} from '~/design_management_legacy/utils/error_messages'; - -const mockFilenames = n => - Array(n) - .fill(0) - .map((_, i) => ({ filename: `${i + 1}.jpg` })); - -describe('Error message', () => { - describe('designDeletionError', () => { - const singularMsg = 'Could not delete a design. Please try again.'; - const pluralMsg = 'Could not delete designs. Please try again.'; - - describe('when [singular=true]', () => { - it.each([[undefined], [true]])('uses singular grammar', singularOption => { - expect(designDeletionError({ singular: singularOption })).toEqual(singularMsg); - }); - }); - - describe('when [singular=false]', () => { - it('uses plural grammar', () => { - expect(designDeletionError({ singular: false })).toEqual(pluralMsg); - }); - }); - }); - - describe.each([ - [[], [], null], - [mockFilenames(1), mockFilenames(1), 'Upload skipped. 1.jpg did not change.'], - [ - mockFilenames(2), - mockFilenames(2), - 'Upload skipped. The designs you tried uploading did not change.', - ], - [ - mockFilenames(2), - mockFilenames(1), - 'Upload skipped. Some of the designs you tried uploading did not change: 1.jpg.', - ], - [ - mockFilenames(6), - mockFilenames(5), - 'Upload skipped. Some of the designs you tried uploading did not change: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg.', - ], - [ - mockFilenames(7), - mockFilenames(6), - 'Upload skipped. Some of the designs you tried uploading did not change: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg, and 1 more.', - ], - [ - mockFilenames(8), - mockFilenames(7), - 'Upload skipped. Some of the designs you tried uploading did not change: 1.jpg, 2.jpg, 3.jpg, 4.jpg, 5.jpg, and 2 more.', - ], - ])('designUploadSkippedWarning', (uploadedFiles, skippedFiles, expected) => { - it('returns expected warning message', () => { - expect(designUploadSkippedWarning(uploadedFiles, skippedFiles)).toBe(expected); - }); - }); -}); diff --git a/spec/frontend/design_management_legacy/utils/tracking_spec.js b/spec/frontend/design_management_legacy/utils/tracking_spec.js deleted file mode 100644 index a59cf80c906..00000000000 --- a/spec/frontend/design_management_legacy/utils/tracking_spec.js +++ /dev/null @@ -1,59 +0,0 @@ -import { mockTracking } from 'helpers/tracking_helper'; -import { trackDesignDetailView } from '~/design_management_legacy/utils/tracking'; - -function getTrackingSpy(key) { - return mockTracking(key, undefined, jest.spyOn); -} - -describe('Tracking Events', () => { - describe('trackDesignDetailView', () => { - const eventKey = 'projects:issues:design'; - const eventName = 'view_design'; - - it('trackDesignDetailView fires a tracking event when called', () => { - const trackingSpy = getTrackingSpy(eventKey); - - trackDesignDetailView(); - - expect(trackingSpy).toHaveBeenCalledWith( - eventKey, - eventName, - expect.objectContaining({ - label: eventName, - context: { - schema: expect.any(String), - data: { - 'design-version-number': 1, - 'design-is-current-version': false, - 'internal-object-referrer': '', - 'design-collection-owner': '', - }, - }, - }), - ); - }); - - it('trackDesignDetailView allows to customize the value payload', () => { - const trackingSpy = getTrackingSpy(eventKey); - - trackDesignDetailView('from-a-test', 'test', 100, true); - - expect(trackingSpy).toHaveBeenCalledWith( - eventKey, - eventName, - expect.objectContaining({ - label: eventName, - context: { - schema: expect.any(String), - data: { - 'design-version-number': 100, - 'design-is-current-version': true, - 'internal-object-referrer': 'from-a-test', - 'design-collection-owner': 'test', - }, - }, - }), - ); - }); - }); -}); |