diff options
Diffstat (limited to 'spec/frontend/notes')
11 files changed, 270 insertions, 187 deletions
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js index 2f58f75ab70..bab90723578 100644 --- a/spec/frontend/notes/components/comment_form_spec.js +++ b/spec/frontend/notes/components/comment_form_spec.js @@ -1,7 +1,9 @@ +import { GlDropdown, GlAlert } from '@gitlab/ui'; import { mount, shallowMount } from '@vue/test-utils'; import Autosize from 'autosize'; import MockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; +import Vue, { nextTick } from 'vue'; +import Vuex from 'vuex'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import { deprecatedCreateFlash as flash } from '~/flash'; @@ -9,7 +11,8 @@ import axios from '~/lib/utils/axios_utils'; import CommentForm from '~/notes/components/comment_form.vue'; import * as constants from '~/notes/constants'; import eventHub from '~/notes/event_hub'; -import createStore from '~/notes/stores'; +import { COMMENT_FORM } from '~/notes/i18n'; +import notesModule from '~/notes/stores/modules'; import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data'; jest.mock('autosize'); @@ -17,15 +20,45 @@ jest.mock('~/commons/nav/user_merge_requests'); jest.mock('~/flash'); jest.mock('~/gl_form'); +Vue.use(Vuex); + describe('issue_comment_form component', () => { let store; let wrapper; let axiosMock; const findCloseReopenButton = () => wrapper.findByTestId('close-reopen-button'); - const findCommentButton = () => wrapper.findByTestId('comment-button'); const findTextArea = () => wrapper.findByTestId('comment-field'); const findConfidentialNoteCheckbox = () => wrapper.findByTestId('confidential-note-checkbox'); + const findCommentGlDropdown = () => wrapper.find(GlDropdown); + const findCommentButton = () => findCommentGlDropdown().find('button'); + const findErrorAlerts = () => wrapper.findAllComponents(GlAlert).wrappers; + + async function clickCommentButton({ waitForComponent = true, waitForNetwork = true } = {}) { + findCommentButton().trigger('click'); + + if (waitForComponent || waitForNetwork) { + // Wait for the click to bubble out and trigger the handler + await nextTick(); + + if (waitForNetwork) { + // Wait for the network request promise to resolve + await nextTick(); + } + } + } + + function createStore({ actions = {} } = {}) { + const baseModule = notesModule(); + + return new Vuex.Store({ + ...baseModule, + actions: { + ...baseModule.actions, + ...actions, + }, + }); + } const createNotableDataMock = (data = {}) => { return { @@ -101,6 +134,83 @@ describe('issue_comment_form component', () => { expect(wrapper.vm.resizeTextarea).toHaveBeenCalled(); }); + it('does not report errors in the UI when the save succeeds', async () => { + mountComponent({ mountFunction: mount, initialData: { note: '/label ~sdfghj' } }); + + jest.spyOn(wrapper.vm, 'saveNote').mockResolvedValue(); + + await clickCommentButton(); + + // findErrorAlerts().exists returns false if *any* wrapper is empty, + // not necessarily that there aren't any at all. + // We want to check here that there are none found, so we use the + // raw wrapper array length instead. + expect(findErrorAlerts().length).toBe(0); + }); + + it.each` + httpStatus | errors + ${400} | ${[COMMENT_FORM.GENERIC_UNSUBMITTABLE_NETWORK]} + ${422} | ${['error 1']} + ${422} | ${['error 1', 'error 2']} + ${422} | ${['error 1', 'error 2', 'error 3']} + `( + 'displays the correct errors ($errors) for a $httpStatus network response', + async ({ errors, httpStatus }) => { + store = createStore({ + actions: { + saveNote: jest.fn().mockRejectedValue({ + response: { status: httpStatus, data: { errors: { commands_only: errors } } }, + }), + }, + }); + + mountComponent({ mountFunction: mount, initialData: { note: '/label ~sdfghj' } }); + + await clickCommentButton(); + + const errorAlerts = findErrorAlerts(); + + expect(errorAlerts.length).toBe(errors.length); + errors.forEach((msg, index) => { + const alert = errorAlerts[index]; + + expect(alert.text()).toBe(msg); + }); + }, + ); + + it('should remove the correct error from the list when it is dismissed', async () => { + const commandErrors = ['1', '2', '3']; + store = createStore({ + actions: { + saveNote: jest.fn().mockRejectedValue({ + response: { status: 422, data: { errors: { commands_only: [...commandErrors] } } }, + }), + }, + }); + + mountComponent({ mountFunction: mount, initialData: { note: '/label ~sdfghj' } }); + + await clickCommentButton(); + + let errorAlerts = findErrorAlerts(); + + expect(errorAlerts.length).toBe(commandErrors.length); + + // dismiss the second error + extendedWrapper(errorAlerts[1]).findByTestId('close-icon').trigger('click'); + // Wait for the dismissal to bubble out of the Alert component and be handled in this component + await nextTick(); + // Refresh the list of alerts + errorAlerts = findErrorAlerts(); + + expect(errorAlerts.length).toBe(commandErrors.length - 1); + // We want to know that the *correct* error was dismissed, not just that any one is gone + expect(errorAlerts[0].text()).toBe(commandErrors[0]); + expect(errorAlerts[1].text()).toBe(commandErrors[2]); + }); + it('should toggle issue state when no note', () => { mountComponent({ mountFunction: mount }); @@ -243,7 +353,7 @@ describe('issue_comment_form component', () => { it('should render comment button as disabled', () => { mountComponent(); - expect(findCommentButton().props('disabled')).toBe(true); + expect(findCommentGlDropdown().props('disabled')).toBe(true); }); it('should enable comment button if it has note', async () => { @@ -251,7 +361,7 @@ describe('issue_comment_form component', () => { await wrapper.setData({ note: 'Foo' }); - expect(findCommentButton().props('disabled')).toBe(false); + expect(findCommentGlDropdown().props('disabled')).toBe(false); }); it('should update buttons texts when it has note', () => { @@ -437,7 +547,7 @@ describe('issue_comment_form component', () => { await wrapper.vm.$nextTick(); // submit comment - wrapper.findByTestId('comment-button').trigger('click'); + findCommentButton().trigger('click'); const [providedData] = wrapper.vm.saveNote.mock.calls[0]; expect(providedData.data.note.confidential).toBe(shouldCheckboxBeChecked); @@ -472,16 +582,4 @@ describe('issue_comment_form component', () => { expect(findTextArea().exists()).toBe(false); }); }); - - describe('close/reopen button variants', () => { - it.each([ - [constants.OPENED, 'warning'], - [constants.REOPENED, 'warning'], - [constants.CLOSED, 'default'], - ])('when %s, the variant of the btn is %s', (state, expected) => { - mountComponent({ noteableData: { ...noteableDataMock, state } }); - - expect(findCloseReopenButton().props('variant')).toBe(expected); - }); - }); }); diff --git a/spec/frontend/notes/components/diff_discussion_header_spec.js b/spec/frontend/notes/components/diff_discussion_header_spec.js index fdc89522901..fa34a5e8d39 100644 --- a/spec/frontend/notes/components/diff_discussion_header_spec.js +++ b/spec/frontend/notes/components/diff_discussion_header_spec.js @@ -6,14 +6,10 @@ import createStore from '~/notes/stores'; import mockDiffFile from '../../diffs/mock_data/diff_discussions'; import { discussionMock } from '../mock_data'; -const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json'; - describe('diff_discussion_header component', () => { let store; let wrapper; - preloadFixtures(discussionWithTwoUnresolvedNotes); - beforeEach(() => { window.mrTabs = {}; store = createStore(); diff --git a/spec/frontend/notes/components/discussion_actions_spec.js b/spec/frontend/notes/components/discussion_actions_spec.js index 03e5842bb0f..c6a7d7ead98 100644 --- a/spec/frontend/notes/components/discussion_actions_spec.js +++ b/spec/frontend/notes/components/discussion_actions_spec.js @@ -96,7 +96,7 @@ describe('DiscussionActions', () => { it('emits showReplyForm event when clicking on reply placeholder', () => { jest.spyOn(wrapper.vm, '$emit'); - wrapper.find(ReplyPlaceholder).find('button').trigger('click'); + wrapper.find(ReplyPlaceholder).find('textarea').trigger('focus'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('showReplyForm'); }); diff --git a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js index b7b7ec08867..2a4cd0df0c7 100644 --- a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js +++ b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js @@ -1,17 +1,17 @@ import { shallowMount } from '@vue/test-utils'; import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; -const buttonText = 'Test Button Text'; +const placeholderText = 'Test Button Text'; describe('ReplyPlaceholder', () => { let wrapper; - const findButton = () => wrapper.find({ ref: 'button' }); + const findTextarea = () => wrapper.find({ ref: 'textarea' }); beforeEach(() => { wrapper = shallowMount(ReplyPlaceholder, { propsData: { - buttonText, + placeholderText, }, }); }); @@ -20,17 +20,17 @@ describe('ReplyPlaceholder', () => { wrapper.destroy(); }); - it('emits onClick event on button click', () => { - findButton().trigger('click'); + it('emits focus event on button click', () => { + findTextarea().trigger('focus'); return wrapper.vm.$nextTick().then(() => { expect(wrapper.emitted()).toEqual({ - onClick: [[]], + focus: [[]], }); }); }); it('should render reply button', () => { - expect(findButton().text()).toEqual(buttonText); + expect(findTextarea().attributes('placeholder')).toEqual(placeholderText); }); }); diff --git a/spec/frontend/notes/components/note_actions_spec.js b/spec/frontend/notes/components/note_actions_spec.js index 17717ebd09a..cc41088e21e 100644 --- a/spec/frontend/notes/components/note_actions_spec.js +++ b/spec/frontend/notes/components/note_actions_spec.js @@ -6,6 +6,7 @@ import axios from '~/lib/utils/axios_utils'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import noteActions from '~/notes/components/note_actions.vue'; import createStore from '~/notes/stores'; +import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue'; import { userDataMock } from '../mock_data'; describe('noteActions', () => { @@ -15,6 +16,9 @@ describe('noteActions', () => { let actions; let axiosMock; + const findUserAccessRoleBadge = (idx) => wrapper.findAll(UserAccessRoleBadge).at(idx); + const findUserAccessRoleBadgeText = (idx) => findUserAccessRoleBadge(idx).text().trim(); + const mountNoteActions = (propsData, computed) => { const localVue = createLocalVue(); return mount(localVue.extend(noteActions), { @@ -44,6 +48,7 @@ describe('noteActions', () => { projectName: 'project', reportAbusePath: `${TEST_HOST}/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26`, showReply: false, + awardPath: `${TEST_HOST}/award_emoji`, }; actions = { @@ -66,11 +71,11 @@ describe('noteActions', () => { }); it('should render noteable author badge', () => { - expect(wrapper.findAll('.note-role').at(0).text().trim()).toEqual('Author'); + expect(findUserAccessRoleBadgeText(0)).toBe('Author'); }); it('should render access level badge', () => { - expect(wrapper.findAll('.note-role').at(1).text().trim()).toEqual(props.accessLevel); + expect(findUserAccessRoleBadgeText(1)).toBe(props.accessLevel); }); it('should render contributor badge', () => { @@ -80,7 +85,7 @@ describe('noteActions', () => { }); return wrapper.vm.$nextTick().then(() => { - expect(wrapper.findAll('.note-role').at(1).text().trim()).toBe('Contributor'); + expect(findUserAccessRoleBadgeText(1)).toBe('Contributor'); }); }); diff --git a/spec/frontend/notes/components/note_form_spec.js b/spec/frontend/notes/components/note_form_spec.js index 7615f3b70f1..92137d3190f 100644 --- a/spec/frontend/notes/components/note_form_spec.js +++ b/spec/frontend/notes/components/note_form_spec.js @@ -83,7 +83,7 @@ describe('issue_note_form component', () => { }); const message = - 'This comment has changed since you started editing, please review the updated comment to ensure information is not lost.'; + 'This comment changed after you started editing it. Review the updated comment to ensure information is not lost.'; await nextTick(); diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js index 87538279c3d..dd65351ef88 100644 --- a/spec/frontend/notes/components/noteable_discussion_spec.js +++ b/spec/frontend/notes/components/noteable_discussion_spec.js @@ -24,8 +24,6 @@ describe('noteable_discussion component', () => { let wrapper; let originalGon; - preloadFixtures(discussionWithTwoUnresolvedNotes); - beforeEach(() => { window.mrTabs = {}; store = createStore(); @@ -65,7 +63,7 @@ describe('noteable_discussion component', () => { expect(wrapper.vm.isReplying).toEqual(false); const replyPlaceholder = wrapper.find(ReplyPlaceholder); - replyPlaceholder.vm.$emit('onClick'); + replyPlaceholder.vm.$emit('focus'); await nextTick(); expect(wrapper.vm.isReplying).toEqual(true); diff --git a/spec/frontend/notes/components/noteable_note_spec.js b/spec/frontend/notes/components/noteable_note_spec.js index fe78e086403..112983f3ac2 100644 --- a/spec/frontend/notes/components/noteable_note_spec.js +++ b/spec/frontend/notes/components/noteable_note_spec.js @@ -1,5 +1,6 @@ import { mount, createLocalVue } from '@vue/test-utils'; import { escape } from 'lodash'; +import waitForPromises from 'helpers/wait_for_promises'; import NoteActions from '~/notes/components/note_actions.vue'; import NoteBody from '~/notes/components/note_body.vue'; import NoteHeader from '~/notes/components/note_header.vue'; @@ -13,7 +14,7 @@ describe('issue_note', () => { let wrapper; const findMultilineComment = () => wrapper.find('[data-testid="multiline-comment"]'); - beforeEach(() => { + const createWrapper = (props = {}) => { store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); @@ -23,6 +24,7 @@ describe('issue_note', () => { store, propsData: { note, + ...props, }, localVue, stubs: [ @@ -33,14 +35,18 @@ describe('issue_note', () => { 'multiline-comment-form', ], }); - }); + }; afterEach(() => { wrapper.destroy(); }); describe('mutiline comments', () => { - it('should render if has multiline comment', () => { + beforeEach(() => { + createWrapper(); + }); + + it('should render if has multiline comment', async () => { const position = { line_range: { start: { @@ -69,9 +75,8 @@ describe('issue_note', () => { line, }); - return wrapper.vm.$nextTick().then(() => { - expect(findMultilineComment().text()).toEqual('Comment on lines 1 to 2'); - }); + await wrapper.vm.$nextTick(); + expect(findMultilineComment().text()).toBe('Comment on lines 1 to 2'); }); it('should only render if it has everything it needs', () => { @@ -147,108 +152,151 @@ describe('issue_note', () => { }); }); - it('should render user information', () => { - const { author } = note; - const avatar = wrapper.find(UserAvatarLink); - const avatarProps = avatar.props(); + describe('rendering', () => { + beforeEach(() => { + createWrapper(); + }); - expect(avatarProps.linkHref).toBe(author.path); - expect(avatarProps.imgSrc).toBe(author.avatar_url); - expect(avatarProps.imgAlt).toBe(author.name); - expect(avatarProps.imgSize).toBe(40); - }); + it('should render user information', () => { + const { author } = note; + const avatar = wrapper.findComponent(UserAvatarLink); + const avatarProps = avatar.props(); - it('should render note header content', () => { - const noteHeader = wrapper.find(NoteHeader); - const noteHeaderProps = noteHeader.props(); + expect(avatarProps.linkHref).toBe(author.path); + expect(avatarProps.imgSrc).toBe(author.avatar_url); + expect(avatarProps.imgAlt).toBe(author.name); + expect(avatarProps.imgSize).toBe(40); + }); - expect(noteHeaderProps.author).toEqual(note.author); - expect(noteHeaderProps.createdAt).toEqual(note.created_at); - expect(noteHeaderProps.noteId).toEqual(note.id); - }); + it('should render note header content', () => { + const noteHeader = wrapper.findComponent(NoteHeader); + const noteHeaderProps = noteHeader.props(); - it('should render note actions', () => { - const { author } = note; - const noteActions = wrapper.find(NoteActions); - const noteActionsProps = noteActions.props(); - - expect(noteActionsProps.authorId).toBe(author.id); - expect(noteActionsProps.noteId).toBe(note.id); - expect(noteActionsProps.noteUrl).toBe(note.noteable_note_url); - expect(noteActionsProps.accessLevel).toBe(note.human_access); - expect(noteActionsProps.canEdit).toBe(note.current_user.can_edit); - expect(noteActionsProps.canAwardEmoji).toBe(note.current_user.can_award_emoji); - expect(noteActionsProps.canDelete).toBe(note.current_user.can_edit); - expect(noteActionsProps.canReportAsAbuse).toBe(true); - expect(noteActionsProps.canResolve).toBe(false); - expect(noteActionsProps.reportAbusePath).toBe(note.report_abuse_path); - expect(noteActionsProps.resolvable).toBe(false); - expect(noteActionsProps.isResolved).toBe(false); - expect(noteActionsProps.isResolving).toBe(false); - expect(noteActionsProps.resolvedBy).toEqual({}); - }); + expect(noteHeaderProps.author).toBe(note.author); + expect(noteHeaderProps.createdAt).toBe(note.created_at); + expect(noteHeaderProps.noteId).toBe(note.id); + }); - it('should render issue body', () => { - const noteBody = wrapper.find(NoteBody); - const noteBodyProps = noteBody.props(); + it('should render note actions', () => { + const { author } = note; + const noteActions = wrapper.findComponent(NoteActions); + const noteActionsProps = noteActions.props(); - expect(noteBodyProps.note).toEqual(note); - expect(noteBodyProps.line).toBe(null); - expect(noteBodyProps.canEdit).toBe(note.current_user.can_edit); - expect(noteBodyProps.isEditing).toBe(false); - expect(noteBodyProps.helpPagePath).toBe(''); - }); + expect(noteActionsProps.authorId).toBe(author.id); + expect(noteActionsProps.noteId).toBe(note.id); + expect(noteActionsProps.noteUrl).toBe(note.noteable_note_url); + expect(noteActionsProps.accessLevel).toBe(note.human_access); + expect(noteActionsProps.canEdit).toBe(note.current_user.can_edit); + expect(noteActionsProps.canAwardEmoji).toBe(note.current_user.can_award_emoji); + expect(noteActionsProps.canDelete).toBe(note.current_user.can_edit); + expect(noteActionsProps.canReportAsAbuse).toBe(true); + expect(noteActionsProps.canResolve).toBe(false); + expect(noteActionsProps.reportAbusePath).toBe(note.report_abuse_path); + expect(noteActionsProps.resolvable).toBe(false); + expect(noteActionsProps.isResolved).toBe(false); + expect(noteActionsProps.isResolving).toBe(false); + expect(noteActionsProps.resolvedBy).toEqual({}); + }); - it('prevents note preview xss', (done) => { - const imgSrc = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; - const noteBody = `<img src="${imgSrc}" onload="alert(1)" />`; - const alertSpy = jest.spyOn(window, 'alert'); - store.hotUpdate({ - actions: { - updateNote() {}, - setSelectedCommentPositionHover() {}, - }, + it('should render issue body', () => { + const noteBody = wrapper.findComponent(NoteBody); + const noteBodyProps = noteBody.props(); + + expect(noteBodyProps.note).toBe(note); + expect(noteBodyProps.line).toBe(null); + expect(noteBodyProps.canEdit).toBe(note.current_user.can_edit); + expect(noteBodyProps.isEditing).toBe(false); + expect(noteBodyProps.helpPagePath).toBe(''); }); - const noteBodyComponent = wrapper.find(NoteBody); - noteBodyComponent.vm.$emit('handleFormUpdate', noteBody, null, () => {}); + it('prevents note preview xss', async () => { + const noteBody = + '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert(1)" />'; + const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {}); + const noteBodyComponent = wrapper.findComponent(NoteBody); + + store.hotUpdate({ + actions: { + updateNote() {}, + setSelectedCommentPositionHover() {}, + }, + }); + + noteBodyComponent.vm.$emit('handleFormUpdate', noteBody, null, () => {}); - setImmediate(() => { + await waitForPromises(); expect(alertSpy).not.toHaveBeenCalled(); - expect(wrapper.vm.note.note_html).toEqual(escape(noteBody)); - done(); + expect(wrapper.vm.note.note_html).toBe(escape(noteBody)); }); }); describe('cancel edit', () => { - it('restores content of updated note', (done) => { + beforeEach(() => { + createWrapper(); + }); + + it('restores content of updated note', async () => { const updatedText = 'updated note text'; store.hotUpdate({ actions: { updateNote() {}, }, }); - const noteBody = wrapper.find(NoteBody); + const noteBody = wrapper.findComponent(NoteBody); noteBody.vm.resetAutoSave = () => {}; noteBody.vm.$emit('handleFormUpdate', updatedText, null, () => {}); - wrapper.vm - .$nextTick() - .then(() => { - const noteBodyProps = noteBody.props(); - - expect(noteBodyProps.note.note_html).toBe(updatedText); - noteBody.vm.$emit('cancelForm'); - }) - .then(() => wrapper.vm.$nextTick()) - .then(() => { - const noteBodyProps = noteBody.props(); - - expect(noteBodyProps.note.note_html).toBe(note.note_html); - }) - .then(done) - .catch(done.fail); + await wrapper.vm.$nextTick(); + let noteBodyProps = noteBody.props(); + + expect(noteBodyProps.note.note_html).toBe(updatedText); + + noteBody.vm.$emit('cancelForm'); + await wrapper.vm.$nextTick(); + + noteBodyProps = noteBody.props(); + + expect(noteBodyProps.note.note_html).toBe(note.note_html); + }); + }); + + describe('formUpdateHandler', () => { + const updateNote = jest.fn(); + const params = ['', null, jest.fn(), '']; + + const updateActions = () => { + store.hotUpdate({ + actions: { + updateNote, + setSelectedCommentPositionHover() {}, + }, + }); + }; + + afterEach(() => updateNote.mockReset()); + + it('responds to handleFormUpdate', () => { + createWrapper(); + updateActions(); + wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params); + expect(wrapper.emitted('handleUpdateNote')).toBeTruthy(); + }); + + it('does not stringify empty position', () => { + createWrapper(); + updateActions(); + wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params); + expect(updateNote.mock.calls[0][1].note.note.position).toBeUndefined(); + }); + + it('stringifies populated position', () => { + const position = { test: true }; + const expectation = JSON.stringify(position); + createWrapper({ note: { ...note, position } }); + updateActions(); + wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', ...params); + expect(updateNote.mock.calls[0][1].note.note.position).toBe(expectation); }); }); }); diff --git a/spec/frontend/notes/components/notes_app_spec.js b/spec/frontend/notes/components/notes_app_spec.js index efee72dea96..163501d5ce8 100644 --- a/spec/frontend/notes/components/notes_app_spec.js +++ b/spec/frontend/notes/components/notes_app_spec.js @@ -33,6 +33,8 @@ describe('note_app', () => { let wrapper; let store; + const findCommentButton = () => wrapper.find('[data-testid="comment-button"]'); + const getComponentOrder = () => { return wrapper .findAll('#notes-list,.js-comment-form') @@ -144,7 +146,7 @@ describe('note_app', () => { }); it('should render form comment button as disabled', () => { - expect(wrapper.find('.js-note-new-discussion').attributes('disabled')).toEqual('disabled'); + expect(findCommentButton().props('disabled')).toEqual(true); }); it('updates discussions badge', () => { diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js index 1852108b39f..f972ff0d2e4 100644 --- a/spec/frontend/notes/stores/actions_spec.js +++ b/spec/frontend/notes/stores/actions_spec.js @@ -3,6 +3,7 @@ import testAction from 'helpers/vuex_action_helper'; import { TEST_HOST } from 'spec/test_constants'; import Api from '~/api'; import { deprecatedCreateFlash as Flash } from '~/flash'; +import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; import axios from '~/lib/utils/axios_utils'; import * as notesConstants from '~/notes/constants'; import createStore from '~/notes/stores'; @@ -10,7 +11,6 @@ import * as actions from '~/notes/stores/actions'; import * as mutationTypes from '~/notes/stores/mutation_types'; import mutations from '~/notes/stores/mutations'; import * as utils from '~/notes/stores/utils'; -import updateIssueConfidentialMutation from '~/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql'; import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql'; import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql'; import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub'; @@ -203,7 +203,7 @@ describe('Actions Notes Store', () => { describe('emitStateChangedEvent', () => { it('emits an event on the document', () => { - document.addEventListener('issuable_vue_app:change', (event) => { + document.addEventListener(EVENT_ISSUABLE_VUE_APP_CHANGE, (event) => { expect(event.detail.data).toEqual({ id: '1', state: 'closed' }); expect(event.detail.isClosed).toEqual(false); }); @@ -1276,68 +1276,6 @@ describe('Actions Notes Store', () => { }); }); - describe('updateConfidentialityOnIssuable', () => { - state = { noteableData: { confidential: false } }; - const iid = '1'; - const projectPath = 'full/path'; - const getters = { getNoteableData: { iid } }; - const actionArgs = { fullPath: projectPath, confidential: true }; - const confidential = true; - - beforeEach(() => { - jest - .spyOn(utils.gqClient, 'mutate') - .mockResolvedValue({ data: { issueSetConfidential: { issue: { confidential } } } }); - }); - - it('calls gqClient mutation one time', () => { - actions.updateConfidentialityOnIssuable({ commit: () => {}, state, getters }, actionArgs); - - expect(utils.gqClient.mutate).toHaveBeenCalledTimes(1); - }); - - it('calls gqClient mutation with the correct values', () => { - actions.updateConfidentialityOnIssuable({ commit: () => {}, state, getters }, actionArgs); - - expect(utils.gqClient.mutate).toHaveBeenCalledWith({ - mutation: updateIssueConfidentialMutation, - variables: { input: { iid, projectPath, confidential } }, - }); - }); - - describe('on success of mutation', () => { - it('calls commit with the correct values', () => { - const commitSpy = jest.fn(); - - return actions - .updateConfidentialityOnIssuable({ commit: commitSpy, state, getters }, actionArgs) - .then(() => { - expect(Flash).not.toHaveBeenCalled(); - expect(commitSpy).toHaveBeenCalledWith( - mutationTypes.SET_ISSUE_CONFIDENTIAL, - confidential, - ); - }); - }); - }); - - describe('on user recoverable error', () => { - it('sends the error to Flash', () => { - const error = 'error'; - - jest - .spyOn(utils.gqClient, 'mutate') - .mockResolvedValue({ data: { issueSetConfidential: { errors: [error] } } }); - - return actions - .updateConfidentialityOnIssuable({ commit: () => {}, state, getters }, actionArgs) - .then(() => { - expect(Flash).toHaveBeenCalledWith(error, 'alert'); - }); - }); - }); - }); - describe.each` issuableType ${'issue'} | ${'merge_request'} diff --git a/spec/frontend/notes/stores/getters_spec.js b/spec/frontend/notes/stores/getters_spec.js index 4ebfc679310..4d2f86a1ecf 100644 --- a/spec/frontend/notes/stores/getters_spec.js +++ b/spec/frontend/notes/stores/getters_spec.js @@ -26,8 +26,6 @@ const createDiscussionNeighborParams = (discussionId, diffOrder, step) => ({ describe('Getters Notes Store', () => { let state; - preloadFixtures(discussionWithTwoUnresolvedNotes); - beforeEach(() => { state = { discussions: [individualNote], |