diff options
Diffstat (limited to 'spec/frontend/sidebar')
13 files changed, 546 insertions, 605 deletions
diff --git a/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap b/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap deleted file mode 100644 index 2367667544d..00000000000 --- a/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap +++ /dev/null @@ -1,199 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Confidential Issue Sidebar Block renders for confidential = false and isEditable = false 1`] = ` -<div - class="block issuable-sidebar-item confidentiality" - iid="" -> - <div - class="sidebar-collapsed-icon" - title="Not confidential" - > - <gl-icon-stub - name="eye" - size="16" - /> - </div> - - <div - class="title hide-collapsed" - > - - Confidentiality - - <!----> - </div> - - <div - class="value sidebar-item-value hide-collapsed" - > - <!----> - - <div - class="no-value sidebar-item-value" - data-testid="not-confidential" - > - <gl-icon-stub - class="sidebar-item-icon inline" - name="eye" - size="16" - /> - - Not confidential - - </div> - </div> -</div> -`; - -exports[`Confidential Issue Sidebar Block renders for confidential = false and isEditable = true 1`] = ` -<div - class="block issuable-sidebar-item confidentiality" - iid="" -> - <div - class="sidebar-collapsed-icon" - title="Not confidential" - > - <gl-icon-stub - name="eye" - size="16" - /> - </div> - - <div - class="title hide-collapsed" - > - - Confidentiality - - <a - class="float-right confidential-edit" - data-track-event="click_edit_button" - data-track-label="right_sidebar" - data-track-property="confidentiality" - href="#" - > - Edit - </a> - </div> - - <div - class="value sidebar-item-value hide-collapsed" - > - <!----> - - <div - class="no-value sidebar-item-value" - data-testid="not-confidential" - > - <gl-icon-stub - class="sidebar-item-icon inline" - name="eye" - size="16" - /> - - Not confidential - - </div> - </div> -</div> -`; - -exports[`Confidential Issue Sidebar Block renders for confidential = true and isEditable = false 1`] = ` -<div - class="block issuable-sidebar-item confidentiality" - iid="" -> - <div - class="sidebar-collapsed-icon" - title="Confidential" - > - <gl-icon-stub - name="eye-slash" - size="16" - /> - </div> - - <div - class="title hide-collapsed" - > - - Confidentiality - - <!----> - </div> - - <div - class="value sidebar-item-value hide-collapsed" - > - <!----> - - <div - class="value sidebar-item-value hide-collapsed" - > - <gl-icon-stub - class="sidebar-item-icon inline is-active" - name="eye-slash" - size="16" - /> - - This issue is confidential - - </div> - </div> -</div> -`; - -exports[`Confidential Issue Sidebar Block renders for confidential = true and isEditable = true 1`] = ` -<div - class="block issuable-sidebar-item confidentiality" - iid="" -> - <div - class="sidebar-collapsed-icon" - title="Confidential" - > - <gl-icon-stub - name="eye-slash" - size="16" - /> - </div> - - <div - class="title hide-collapsed" - > - - Confidentiality - - <a - class="float-right confidential-edit" - data-track-event="click_edit_button" - data-track-label="right_sidebar" - data-track-property="confidentiality" - href="#" - > - Edit - </a> - </div> - - <div - class="value sidebar-item-value hide-collapsed" - > - <!----> - - <div - class="value sidebar-item-value hide-collapsed" - > - <gl-icon-stub - class="sidebar-item-icon inline is-active" - name="eye-slash" - size="16" - /> - - This issue is confidential - - </div> - </div> -</div> -`; diff --git a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_content_spec.js b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_content_spec.js new file mode 100644 index 00000000000..8844e1626cd --- /dev/null +++ b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_content_spec.js @@ -0,0 +1,71 @@ +import { GlIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import SidebarConfidentialityContent from '~/sidebar/components/confidential/sidebar_confidentiality_content.vue'; + +describe('Sidebar Confidentiality Content', () => { + let wrapper; + + const findIcon = () => wrapper.findComponent(GlIcon); + const findText = () => wrapper.find('[data-testid="confidential-text"]'); + const findCollapsedIcon = () => wrapper.find('[data-testid="sidebar-collapsed-icon"]'); + + const createComponent = ({ confidential = false, issuableType = 'issue' } = {}) => { + wrapper = shallowMount(SidebarConfidentialityContent, { + propsData: { + confidential, + issuableType, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('emits `expandSidebar` event on collapsed icon click', () => { + createComponent(); + findCollapsedIcon().trigger('click'); + + expect(wrapper.emitted('expandSidebar')).toHaveLength(1); + }); + + describe('when issue is non-confidential', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders a non-confidential icon', () => { + expect(findIcon().props('name')).toBe('eye'); + }); + + it('does not add `is-active` class to the icon', () => { + expect(findIcon().classes()).not.toContain('is-active'); + }); + + it('displays a non-confidential text', () => { + expect(findText().text()).toBe('Not confidential'); + }); + }); + + describe('when issue is confidential', () => { + it('renders a confidential icon', () => { + createComponent({ confidential: true }); + expect(findIcon().props('name')).toBe('eye-slash'); + }); + + it('adds `is-active` class to the icon', () => { + createComponent({ confidential: true }); + expect(findIcon().classes()).toContain('is-active'); + }); + + it('displays a correct confidential text for issue', () => { + createComponent({ confidential: true }); + expect(findText().text()).toBe('This issue is confidential'); + }); + + it('displays a correct confidential text for epic', () => { + createComponent({ confidential: true, issuableType: 'epic' }); + expect(findText().text()).toBe('This epic is confidential'); + }); + }); +}); diff --git a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js new file mode 100644 index 00000000000..d5e6310ed38 --- /dev/null +++ b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js @@ -0,0 +1,173 @@ +import { GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import SidebarConfidentialityForm from '~/sidebar/components/confidential/sidebar_confidentiality_form.vue'; +import { confidentialityQueries } from '~/sidebar/constants'; + +jest.mock('~/flash'); + +describe('Sidebar Confidentiality Form', () => { + let wrapper; + + const findWarningMessage = () => wrapper.find(`[data-testid="warning-message"]`); + const findConfidentialToggle = () => wrapper.find(`[data-testid="confidential-toggle"]`); + const findCancelButton = () => wrapper.find(`[data-testid="confidential-cancel"]`); + + const createComponent = ({ + props = {}, + mutate = jest.fn().mockResolvedValue('Success'), + } = {}) => { + wrapper = shallowMount(SidebarConfidentialityForm, { + provide: { + fullPath: 'group/project', + iid: '1', + }, + propsData: { + confidential: false, + issuableType: 'issue', + ...props, + }, + mocks: { + $apollo: { + mutate, + }, + }, + stubs: { + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('emits a `closeForm` event when Cancel button is clicked', () => { + createComponent(); + findCancelButton().vm.$emit('click'); + + expect(wrapper.emitted().closeForm).toHaveLength(1); + }); + + it('renders a loading state after clicking on turn on/off button', async () => { + createComponent(); + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalled(); + await nextTick(); + expect(findConfidentialToggle().props('loading')).toBe(true); + }); + + it('creates a flash if mutation is rejected', async () => { + createComponent({ mutate: jest.fn().mockRejectedValue('Error!') }); + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'Something went wrong while setting issue confidentiality.', + }); + }); + + it('creates a flash if mutation contains errors', async () => { + createComponent({ + mutate: jest.fn().mockResolvedValue({ + data: { issuableSetConfidential: { errors: ['Houston, we have a problem!'] } }, + }), + }); + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'Houston, we have a problem!', + }); + }); + + describe('when issue is not confidential', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders a message about making an issue confidential', () => { + expect(findWarningMessage().text()).toBe( + 'You are going to turn on confidentiality. Only team members with at least Reporter access will be able to see and leave comments on the issue.', + ); + }); + + it('has a `Turn on` button text', () => { + expect(findConfidentialToggle().text()).toBe('Turn on'); + }); + + it('calls a mutation to set confidential to true on button click', () => { + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: confidentialityQueries[wrapper.vm.issuableType].mutation, + variables: { + input: { + confidential: true, + iid: '1', + projectPath: 'group/project', + }, + }, + }); + }); + }); + + describe('when issue is confidential', () => { + beforeEach(() => { + createComponent({ props: { confidential: true } }); + }); + + it('renders a message about making an issue non-confidential', () => { + expect(findWarningMessage().text()).toBe( + 'You are going to turn off the confidentiality. This means everyone will be able to see and leave a comment on this issue.', + ); + }); + + it('has a `Turn off` button text', () => { + expect(findConfidentialToggle().text()).toBe('Turn off'); + }); + + it('calls a mutation to set confidential to false on button click', () => { + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: confidentialityQueries[wrapper.vm.issuableType].mutation, + variables: { + input: { + confidential: false, + iid: '1', + projectPath: 'group/project', + }, + }, + }); + }); + }); + + describe('when issuable type is `epic`', () => { + beforeEach(() => { + createComponent({ props: { confidential: true, issuableType: 'epic' } }); + }); + + it('renders a message about making an epic non-confidential', () => { + expect(findWarningMessage().text()).toBe( + 'You are going to turn off the confidentiality. This means everyone will be able to see and leave a comment on this epic.', + ); + }); + + it('calls a mutation to set epic confidentiality with correct parameters', () => { + findConfidentialToggle().vm.$emit('click', new MouseEvent('click')); + + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: confidentialityQueries[wrapper.vm.issuableType].mutation, + variables: { + input: { + confidential: false, + iid: '1', + groupPath: 'group/project', + }, + }, + }); + }); + }); +}); diff --git a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js new file mode 100644 index 00000000000..20a5be9b518 --- /dev/null +++ b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js @@ -0,0 +1,159 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import SidebarConfidentialityContent from '~/sidebar/components/confidential/sidebar_confidentiality_content.vue'; +import SidebarConfidentialityForm from '~/sidebar/components/confidential/sidebar_confidentiality_form.vue'; +import SidebarConfidentialityWidget, { + confidentialWidget, +} from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; +import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; +import issueConfidentialQuery from '~/sidebar/queries/issue_confidential.query.graphql'; +import { issueConfidentialityResponse } from '../../mock_data'; + +jest.mock('~/flash'); + +const localVue = createLocalVue(); +localVue.use(VueApollo); + +describe('Sidebar Confidentiality Widget', () => { + let wrapper; + let fakeApollo; + + const findEditableItem = () => wrapper.findComponent(SidebarEditableItem); + const findConfidentialityForm = () => wrapper.findComponent(SidebarConfidentialityForm); + const findConfidentialityContent = () => wrapper.findComponent(SidebarConfidentialityContent); + + const createComponent = ({ + confidentialQueryHandler = jest.fn().mockResolvedValue(issueConfidentialityResponse()), + } = {}) => { + fakeApollo = createMockApollo([[issueConfidentialQuery, confidentialQueryHandler]]); + + wrapper = shallowMount(SidebarConfidentialityWidget, { + localVue, + apolloProvider: fakeApollo, + provide: { + fullPath: 'group/project', + iid: '1', + canUpdate: true, + }, + propsData: { + issuableType: 'issue', + }, + stubs: { + SidebarEditableItem, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + fakeApollo = null; + }); + + it('passes a `loading` prop as true to editable item when query is loading', () => { + createComponent(); + + expect(findEditableItem().props('loading')).toBe(true); + }); + + it('exposes a method via external observable', () => { + createComponent(); + + expect(confidentialWidget.setConfidentiality).toEqual(wrapper.vm.setConfidentiality); + }); + + describe('when issue is not confidential', () => { + beforeEach(async () => { + createComponent(); + await waitForPromises(); + }); + + it('passes a `loading` prop as false to editable item', () => { + expect(findEditableItem().props('loading')).toBe(false); + }); + + it('passes false to `confidential` prop of child components', () => { + expect(findConfidentialityForm().props('confidential')).toBe(false); + expect(findConfidentialityContent().props('confidential')).toBe(false); + }); + + it('changes confidentiality to true after setConfidentiality is called', async () => { + confidentialWidget.setConfidentiality(); + await nextTick(); + expect(findConfidentialityForm().props('confidential')).toBe(true); + expect(findConfidentialityContent().props('confidential')).toBe(true); + }); + + it('emits `confidentialityUpdated` event with a `false` payload', () => { + expect(wrapper.emitted('confidentialityUpdated')).toEqual([[false]]); + }); + }); + + describe('when issue is confidential', () => { + beforeEach(async () => { + createComponent({ + confidentialQueryHandler: jest.fn().mockResolvedValue(issueConfidentialityResponse(true)), + }); + await waitForPromises(); + }); + + it('passes a `loading` prop as false to editable item', () => { + expect(findEditableItem().props('loading')).toBe(false); + }); + + it('passes false to `confidential` prop of child components', () => { + expect(findConfidentialityForm().props('confidential')).toBe(true); + expect(findConfidentialityContent().props('confidential')).toBe(true); + }); + + it('changes confidentiality to false after setConfidentiality is called', async () => { + confidentialWidget.setConfidentiality(); + await nextTick(); + expect(findConfidentialityForm().props('confidential')).toBe(false); + expect(findConfidentialityContent().props('confidential')).toBe(false); + }); + + it('emits `confidentialityUpdated` event with a `true` payload', () => { + expect(wrapper.emitted('confidentialityUpdated')).toEqual([[true]]); + }); + }); + + it('displays a flash message when query is rejected', async () => { + createComponent({ + confidentialQueryHandler: jest.fn().mockRejectedValue('Houston, we have a problem'), + }); + await waitForPromises(); + + expect(createFlash).toHaveBeenCalled(); + }); + + it('closes the form and dispatches an event when `closeForm` is emitted', async () => { + createComponent(); + const el = wrapper.vm.$el; + jest.spyOn(el, 'dispatchEvent'); + + await waitForPromises(); + wrapper.vm.$refs.editable.expand(); + await nextTick(); + + expect(findConfidentialityForm().isVisible()).toBe(true); + + findConfidentialityForm().vm.$emit('closeForm'); + await nextTick(); + expect(findConfidentialityForm().isVisible()).toBe(false); + + expect(el.dispatchEvent).toHaveBeenCalled(); + expect(wrapper.emitted('closeForm')).toHaveLength(1); + }); + + it('emits `expandSidebar` event when it is emitted from child component', async () => { + createComponent(); + await waitForPromises(); + findConfidentialityContent().vm.$emit('expandSidebar'); + + expect(wrapper.emitted('expandSidebar')).toHaveLength(1); + }); +}); diff --git a/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js b/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js new file mode 100644 index 00000000000..1dbb7702a15 --- /dev/null +++ b/spec/frontend/sidebar/components/reference/sidebar_reference_widget_spec.js @@ -0,0 +1,93 @@ +import { GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { IssuableType } from '~/issue_show/constants'; +import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; +import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; +import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { issueReferenceResponse } from '../../mock_data'; + +describe('Sidebar Reference Widget', () => { + let wrapper; + let fakeApollo; + const referenceText = 'reference'; + + const createComponent = ({ + issuableType, + referenceQuery = issueReferenceQuery, + referenceQueryHandler = jest.fn().mockResolvedValue(issueReferenceResponse(referenceText)), + } = {}) => { + Vue.use(VueApollo); + + fakeApollo = createMockApollo([[referenceQuery, referenceQueryHandler]]); + + wrapper = shallowMount(SidebarReferenceWidget, { + apolloProvider: fakeApollo, + provide: { + fullPath: 'group/project', + iid: '1', + }, + propsData: { + issuableType, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe.each([ + [IssuableType.Issue, issueReferenceQuery], + [IssuableType.MergeRequest, mergeRequestReferenceQuery], + ])('when issuableType is %s', (issuableType, referenceQuery) => { + it('displays the reference text', async () => { + createComponent({ + issuableType, + referenceQuery, + }); + + await waitForPromises(); + + expect(wrapper.text()).toContain(referenceText); + }); + + it('displays loading icon while fetching and hides clipboard icon', async () => { + createComponent({ + issuableType, + referenceQuery, + }); + + expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.find(ClipboardButton).exists()).toBe(false); + }); + + it('calls createFlash with correct parameters', async () => { + const mockError = new Error('mayday'); + + createComponent({ + issuableType, + referenceQuery, + referenceQueryHandler: jest.fn().mockRejectedValue(mockError), + }); + + await waitForPromises(); + + const [ + [ + { + message, + error: { networkError }, + }, + ], + ] = wrapper.emitted('fetch-error'); + expect(message).toBe('An error occurred while fetching reference'); + expect(networkError).toEqual(mockError); + }); + }); +}); diff --git a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js index 7c67149b517..9f6878db785 100644 --- a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js +++ b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js @@ -7,6 +7,8 @@ import userDataMock from '../../user_data_mock'; describe('UncollapsedReviewerList component', () => { let wrapper; + const reviewerApprovalIcons = () => wrapper.findAll('[data-testid="re-approved"]'); + function createComponent(props = {}) { const propsData = { users: [], @@ -58,19 +60,29 @@ describe('UncollapsedReviewerList component', () => { const user = userDataMock(); createComponent({ - users: [user, { ...user, id: 2, username: 'hello-world' }], + users: [user, { ...user, id: 2, username: 'hello-world', approved: true }], }); }); - it('only has one user', () => { + it('has both users', () => { expect(wrapper.findAll(ReviewerAvatarLink).length).toBe(2); }); - it('shows one user with avatar, username and author name', () => { + it('shows both users with avatar, username and author name', () => { expect(wrapper.text()).toContain(`@root`); expect(wrapper.text()).toContain(`@hello-world`); }); + it('renders approval icon', () => { + expect(reviewerApprovalIcons().length).toBe(1); + }); + + it('shows that hello-world approved', () => { + const icon = reviewerApprovalIcons().at(0); + + expect(icon.attributes('title')).toEqual('Approved by @hello-world'); + }); + it('renders re-request loading icon', async () => { await wrapper.setData({ loadingStates: { 2: 'loading' } }); diff --git a/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap b/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap deleted file mode 100644 index d33f6c7f389..00000000000 --- a/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Edit Form Dropdown when confidential renders on or off text based on confidentiality 1`] = ` -<div - class="dropdown show" - toggleform="function () {}" - updateconfidentialattribute="function () {}" -> - <div - class="dropdown-menu sidebar-item-warning-message" - > - <div> - <p> - <gl-sprintf-stub - message="You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}." - /> - </p> - - <edit-form-buttons-stub - confidential="true" - fullpath="" - /> - </div> - </div> -</div> -`; - -exports[`Edit Form Dropdown when not confidential renders "You are going to turn on the confidentiality." in the 1`] = ` -<div - class="dropdown show" - toggleform="function () {}" - updateconfidentialattribute="function () {}" -> - <div - class="dropdown-menu sidebar-item-warning-message" - > - <div> - <p> - <gl-sprintf-stub - message="You are going to turn on the confidentiality. This means that only team members with %{strongStart}at least Reporter access%{strongEnd} are able to see and leave comments on the %{issuableType}." - /> - </p> - - <edit-form-buttons-stub - fullpath="" - /> - </div> - </div> -</div> -`; diff --git a/spec/frontend/sidebar/confidential/edit_form_buttons_spec.js b/spec/frontend/sidebar/confidential/edit_form_buttons_spec.js deleted file mode 100644 index 427e3a89c29..00000000000 --- a/spec/frontend/sidebar/confidential/edit_form_buttons_spec.js +++ /dev/null @@ -1,146 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import waitForPromises from 'helpers/wait_for_promises'; -import { deprecatedCreateFlash as flash } from '~/flash'; -import createStore from '~/notes/stores'; -import EditFormButtons from '~/sidebar/components/confidential/edit_form_buttons.vue'; -import eventHub from '~/sidebar/event_hub'; - -jest.mock('~/sidebar/event_hub', () => ({ $emit: jest.fn() })); -jest.mock('~/flash'); - -describe('Edit Form Buttons', () => { - let wrapper; - let store; - const findConfidentialToggle = () => wrapper.find('[data-testid="confidential-toggle"]'); - - const createComponent = ({ props = {}, data = {}, resolved = true }) => { - store = createStore(); - if (resolved) { - jest.spyOn(store, 'dispatch').mockResolvedValue(); - } else { - jest.spyOn(store, 'dispatch').mockRejectedValue(); - } - - wrapper = shallowMount(EditFormButtons, { - propsData: { - fullPath: '', - ...props, - }, - data() { - return { - isLoading: true, - ...data, - }; - }, - store, - }); - }; - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - describe('when isLoading', () => { - beforeEach(() => { - createComponent({ - props: { - confidential: false, - }, - }); - }); - - it('renders "Applying" in the toggle button', () => { - expect(findConfidentialToggle().text()).toBe('Applying'); - }); - - it('disables the toggle button', () => { - expect(findConfidentialToggle().props('disabled')).toBe(true); - }); - - it('sets loading on the toggle button', () => { - expect(findConfidentialToggle().props('loading')).toBe(true); - }); - }); - - describe('when not confidential', () => { - it('renders Turn On in the toggle button', () => { - createComponent({ - data: { - isLoading: false, - }, - props: { - confidential: false, - }, - }); - - expect(findConfidentialToggle().text()).toBe('Turn On'); - }); - }); - - describe('when confidential', () => { - beforeEach(() => { - createComponent({ - data: { - isLoading: false, - }, - props: { - confidential: true, - }, - }); - }); - - it('renders on or off text based on confidentiality', () => { - expect(findConfidentialToggle().text()).toBe('Turn Off'); - }); - }); - - describe('when succeeds', () => { - beforeEach(() => { - createComponent({ data: { isLoading: false }, props: { confidential: true } }); - findConfidentialToggle().vm.$emit('click', new Event('click')); - }); - - it('dispatches the correct action', () => { - expect(store.dispatch).toHaveBeenCalledWith('updateConfidentialityOnIssuable', { - confidential: false, - fullPath: '', - }); - }); - - it('resets loading on the toggle button', () => { - return waitForPromises().then(() => { - expect(findConfidentialToggle().props('loading')).toBe(false); - }); - }); - - it('emits close form', () => { - return waitForPromises().then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('closeConfidentialityForm'); - }); - }); - - it('emits updateOnConfidentiality event', () => { - return waitForPromises().then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('updateIssuableConfidentiality', false); - }); - }); - }); - - describe('when fails', () => { - beforeEach(() => { - createComponent({ - data: { isLoading: false }, - props: { confidential: true }, - resolved: false, - }); - findConfidentialToggle().vm.$emit('click', new Event('click')); - }); - - it('calls flash with the correct message', () => { - expect(flash).toHaveBeenCalledWith( - 'Something went wrong trying to change the confidentiality of this issue', - ); - }); - }); -}); diff --git a/spec/frontend/sidebar/confidential/edit_form_spec.js b/spec/frontend/sidebar/confidential/edit_form_spec.js deleted file mode 100644 index 6b571df10ae..00000000000 --- a/spec/frontend/sidebar/confidential/edit_form_spec.js +++ /dev/null @@ -1,48 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import EditForm from '~/sidebar/components/confidential/edit_form.vue'; - -describe('Edit Form Dropdown', () => { - let wrapper; - const toggleForm = () => {}; - const updateConfidentialAttribute = () => {}; - - const createComponent = (props) => { - wrapper = shallowMount(EditForm, { - propsData: { - ...props, - isLoading: false, - fullPath: '', - issuableType: 'issue', - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - describe('when not confidential', () => { - it('renders "You are going to turn on the confidentiality." in the ', () => { - createComponent({ - confidential: false, - toggleForm, - updateConfidentialAttribute, - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('when confidential', () => { - it('renders on or off text based on confidentiality', () => { - createComponent({ - confidential: true, - toggleForm, - updateConfidentialAttribute, - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - }); -}); diff --git a/spec/frontend/sidebar/confidential_issue_sidebar_spec.js b/spec/frontend/sidebar/confidential_issue_sidebar_spec.js deleted file mode 100644 index 93a6401b1fc..00000000000 --- a/spec/frontend/sidebar/confidential_issue_sidebar_spec.js +++ /dev/null @@ -1,159 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; -import { mockTracking, triggerEvent } from 'helpers/tracking_helper'; -import createStore from '~/notes/stores'; -import * as types from '~/notes/stores/mutation_types'; -import ConfidentialIssueSidebar from '~/sidebar/components/confidential/confidential_issue_sidebar.vue'; -import EditForm from '~/sidebar/components/confidential/edit_form.vue'; - -jest.mock('~/flash'); -jest.mock('~/sidebar/services/sidebar_service'); - -describe('Confidential Issue Sidebar Block', () => { - useMockLocationHelper(); - - let wrapper; - const mutate = jest - .fn() - .mockResolvedValue({ data: { issueSetConfidential: { issue: { confidential: true } } } }); - - const createComponent = ({ propsData, data = {} }) => { - const store = createStore(); - wrapper = shallowMount(ConfidentialIssueSidebar, { - store, - data() { - return data; - }, - propsData: { - iid: '', - fullPath: '', - ...propsData, - }, - mocks: { - $apollo: { - mutate, - }, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - it.each` - confidential | isEditable - ${false} | ${false} - ${false} | ${true} - ${true} | ${false} - ${true} | ${true} - `( - 'renders for confidential = $confidential and isEditable = $isEditable', - ({ confidential, isEditable }) => { - createComponent({ - propsData: { - isEditable, - }, - }); - wrapper.vm.$store.state.noteableData.confidential = confidential; - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.element).toMatchSnapshot(); - }); - }, - ); - - describe('if editable', () => { - beforeEach(() => { - createComponent({ - propsData: { - isEditable: true, - }, - }); - wrapper.vm.$store.state.noteableData.confidential = true; - }); - - it('displays the edit form when editable', () => { - wrapper.setData({ edit: false }); - - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.find({ ref: 'editLink' }).trigger('click'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.find(EditForm).exists()).toBe(true); - }); - }); - - it('displays the edit form when opened from collapsed state', () => { - wrapper.setData({ edit: false }); - - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.find({ ref: 'collapseIcon' }).trigger('click'); - return wrapper.vm.$nextTick(); - }) - .then(() => { - expect(wrapper.find(EditForm).exists()).toBe(true); - }); - }); - - it('tracks the event when "Edit" is clicked', () => { - const spy = mockTracking('_category_', wrapper.element, jest.spyOn); - - const editLink = wrapper.find({ ref: 'editLink' }); - triggerEvent(editLink.element); - - expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', { - label: 'right_sidebar', - property: 'confidentiality', - }); - }); - }); - describe('computed confidential', () => { - beforeEach(() => { - createComponent({ - propsData: { - isEditable: true, - }, - }); - }); - - it('returns false when noteableData is not present', () => { - wrapper.vm.$store.commit(types.SET_NOTEABLE_DATA, null); - - expect(wrapper.vm.confidential).toBe(false); - }); - - it('returns true when noteableData has confidential attr as true', () => { - wrapper.vm.$store.commit(types.SET_NOTEABLE_DATA, {}); - wrapper.vm.$store.commit(types.SET_ISSUE_CONFIDENTIAL, true); - - expect(wrapper.vm.confidential).toBe(true); - }); - - it('returns false when noteableData has confidential attr as false', () => { - wrapper.vm.$store.commit(types.SET_NOTEABLE_DATA, {}); - wrapper.vm.$store.commit(types.SET_ISSUE_CONFIDENTIAL, false); - - expect(wrapper.vm.confidential).toBe(false); - }); - - it('returns true when confidential attr is true', () => { - wrapper.vm.$store.commit(types.SET_NOTEABLE_DATA, {}); - wrapper.vm.$store.commit(types.SET_ISSUE_CONFIDENTIAL, true); - - expect(wrapper.vm.confidential).toBe(true); - }); - - it('returns false when confidential attr is false', () => { - wrapper.vm.$store.commit(types.SET_NOTEABLE_DATA, {}); - wrapper.vm.$store.commit(types.SET_ISSUE_CONFIDENTIAL, false); - - expect(wrapper.vm.confidential).toBe(false); - }); - }); -}); diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js index 3dde40260eb..e751f1239c8 100644 --- a/spec/frontend/sidebar/mock_data.js +++ b/spec/frontend/sidebar/mock_data.js @@ -220,4 +220,29 @@ const mockData = { }, }; +export const issueConfidentialityResponse = (confidential = false) => ({ + data: { + workspace: { + __typename: 'Project', + issuable: { + __typename: 'Issue', + id: 'gid://gitlab/Issue/4', + confidential, + }, + }, + }, +}); + +export const issueReferenceResponse = (reference) => ({ + data: { + workspace: { + __typename: 'Project', + issuable: { + __typename: 'Issue', + id: 'gid://gitlab/Issue/4', + reference, + }, + }, + }, +}); export default mockData; diff --git a/spec/frontend/sidebar/subscriptions_spec.js b/spec/frontend/sidebar/subscriptions_spec.js index e7ae59e26cf..6ab8e1e0ebc 100644 --- a/spec/frontend/sidebar/subscriptions_spec.js +++ b/spec/frontend/sidebar/subscriptions_spec.js @@ -84,6 +84,15 @@ describe('Subscriptions', () => { spy.mockRestore(); }); + it('has visually hidden label', () => { + wrapper = mountComponent(); + + expect(findToggleButton().props()).toMatchObject({ + label: 'Notifications', + labelPosition: 'hidden', + }); + }); + describe('given project emails are disabled', () => { const subscribeDisabledDescription = 'Notifications have been disabled'; diff --git a/spec/frontend/sidebar/user_data_mock.js b/spec/frontend/sidebar/user_data_mock.js index 41d0331f34a..7c11551b0be 100644 --- a/spec/frontend/sidebar/user_data_mock.js +++ b/spec/frontend/sidebar/user_data_mock.js @@ -10,4 +10,5 @@ export default () => ({ can_merge: true, can_update_merge_request: true, reviewed: true, + approved: false, }); |