diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 21:09:05 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 21:09:05 +0300 |
commit | 883d5720994852248f18cb3053dc9f053f28d6f9 (patch) | |
tree | 409c976ddc659f34afaae3b2e97f1d0325f6455c /spec/frontend | |
parent | 5e97da08cba997aefba6f6d13850f95536a80477 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
16 files changed, 334 insertions, 206 deletions
diff --git a/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js b/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js new file mode 100644 index 00000000000..4f66348f9cd --- /dev/null +++ b/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js @@ -0,0 +1,125 @@ +import { GlDrawer, GlForm, GlFormGroup, GlFormRadioGroup } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + +import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; + +jest.mock('~/lib/utils/common_utils', () => ({ + contentTop: jest.fn(), +})); + +jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); + +describe('AbuseCategorySelector', () => { + let wrapper; + + const ACTION_PATH = '/abuse_reports/add_category'; + const USER_ID = '1'; + const REPORTED_FROM_URL = 'http://example.com'; + + const createComponent = (props) => { + wrapper = shallowMountExtended(AbuseCategorySelector, { + propsData: { + ...props, + }, + provide: { + formSubmitPath: ACTION_PATH, + userId: USER_ID, + reportedFromUrl: REPORTED_FROM_URL, + }, + }); + }; + + beforeEach(() => { + createComponent({ showDrawer: true }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findDrawer = () => wrapper.findComponent(GlDrawer); + const findTitle = () => wrapper.findByTestId('category-drawer-title'); + + const findForm = () => wrapper.findComponent(GlForm); + const findFormGroup = () => wrapper.findComponent(GlFormGroup); + const findRadioGroup = () => wrapper.findComponent(GlFormRadioGroup); + + const findCSRFToken = () => findForm().find('input[name="authenticity_token"]'); + const findUserId = () => wrapper.findByTestId('input-user-id'); + const findReferer = () => wrapper.findByTestId('input-referer'); + + const findSubmitFormButton = () => wrapper.findByTestId('submit-form-button'); + + describe('Drawer', () => { + it('is open when prop showDrawer = true', () => { + expect(findDrawer().exists()).toBe(true); + expect(findDrawer().props('open')).toBe(true); + }); + + it('renders title', () => { + expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.title); + }); + + it('emits close-drawer event', async () => { + await findDrawer().vm.$emit('close'); + + expect(wrapper.emitted('close-drawer')).toHaveLength(1); + }); + + describe('when props showDrawer = false', () => { + beforeEach(() => { + createComponent({ showDrawer: false }); + }); + + it('hides the drawer', () => { + expect(findDrawer().props('open')).toBe(false); + }); + }); + }); + + describe('Select category form', () => { + it('renders POST form with path', () => { + expect(findForm().attributes()).toMatchObject({ + method: 'post', + action: ACTION_PATH, + }); + }); + + it('renders csrf token', () => { + expect(findCSRFToken().attributes('value')).toBe('mock-csrf-token'); + }); + + it('renders label', () => { + expect(findFormGroup().exists()).toBe(true); + expect(findFormGroup().attributes('label')).toBe(wrapper.vm.$options.i18n.label); + }); + + it('renders radio group', () => { + expect(findRadioGroup().exists()).toBe(true); + expect(findRadioGroup().props('options')).toEqual(wrapper.vm.$options.categoryOptions); + expect(findRadioGroup().attributes('name')).toBe('abuse_report[category]'); + expect(findRadioGroup().attributes('required')).not.toBeUndefined(); + }); + + it('renders userId as a hidden fields', () => { + expect(findUserId().attributes()).toMatchObject({ + type: 'hidden', + name: 'user_id', + value: USER_ID, + }); + }); + + it('renders referer as a hidden fields', () => { + expect(findReferer().attributes()).toMatchObject({ + type: 'hidden', + name: 'ref_url', + value: REPORTED_FROM_URL, + }); + }); + + it('renders submit button', () => { + expect(findSubmitFormButton().exists()).toBe(true); + expect(findSubmitFormButton().text()).toBe(wrapper.vm.$options.i18n.next); + }); + }); +}); diff --git a/spec/frontend/behaviors/markdown/render_gfm_spec.js b/spec/frontend/behaviors/markdown/render_gfm_spec.js new file mode 100644 index 00000000000..0bbb92282e5 --- /dev/null +++ b/spec/frontend/behaviors/markdown/render_gfm_spec.js @@ -0,0 +1,9 @@ +import { renderGFM } from '~/behaviors/markdown/render_gfm'; + +describe('renderGFM', () => { + it('handles a missing element', () => { + expect(() => { + renderGFM(); + }).not.toThrow(); + }); +}); diff --git a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js index 17bf465baf3..0821c59c8a0 100644 --- a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js @@ -1,5 +1,5 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; import PipelineScheduleLastPipeline from '~/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline.vue'; import { mockPipelineScheduleNodes } from '../../../mock_data'; @@ -18,7 +18,7 @@ describe('Pipeline schedule last pipeline', () => { }); }; - const findCIBadge = () => wrapper.findComponent(CiBadge); + const findCIBadgeLink = () => wrapper.findComponent(CiBadgeLink); const findStatusText = () => wrapper.findByTestId('pipeline-schedule-status-text'); afterEach(() => { @@ -28,8 +28,10 @@ describe('Pipeline schedule last pipeline', () => { it('displays pipeline status', () => { createComponent(); - expect(findCIBadge().exists()).toBe(true); - expect(findCIBadge().props('status')).toBe(defaultProps.schedule.lastPipeline.detailedStatus); + expect(findCIBadgeLink().exists()).toBe(true); + expect(findCIBadgeLink().props('status')).toBe( + defaultProps.schedule.lastPipeline.detailedStatus, + ); expect(findStatusText().exists()).toBe(false); }); @@ -37,6 +39,6 @@ describe('Pipeline schedule last pipeline', () => { createComponent({ schedule: mockPipelineScheduleNodes[0] }); expect(findStatusText().text()).toBe('None'); - expect(findCIBadge().exists()).toBe(false); + expect(findCIBadgeLink().exists()).toBe(false); }); }); diff --git a/spec/frontend/gfm_auto_complete/mock_data.js b/spec/frontend/gfm_auto_complete/mock_data.js index 9c5a9d7ef3d..d58ccaf0f39 100644 --- a/spec/frontend/gfm_auto_complete/mock_data.js +++ b/spec/frontend/gfm_auto_complete/mock_data.js @@ -37,8 +37,8 @@ export const crmContactsMock = [ { id: 1, email: 'contact.1@email.com', - firstName: 'Contact', - lastName: 'One', + first_name: 'Contact', + last_name: 'One', search: 'contact.1@email.com', state: 'active', set: false, @@ -46,8 +46,8 @@ export const crmContactsMock = [ { id: 2, email: 'contact.2@email.com', - firstName: 'Contact', - lastName: 'Two', + first_name: 'Contact', + last_name: 'Two', search: 'contact.2@email.com', state: 'active', set: false, @@ -55,8 +55,8 @@ export const crmContactsMock = [ { id: 3, email: 'contact.3@email.com', - firstName: 'Contact', - lastName: 'Three', + first_name: 'Contact', + last_name: 'Three', search: 'contact.3@email.com', state: 'inactive', set: false, @@ -64,8 +64,8 @@ export const crmContactsMock = [ { id: 4, email: 'contact.4@email.com', - firstName: 'Contact', - lastName: 'Four', + first_name: 'Contact', + last_name: 'Four', search: 'contact.4@email.com', state: 'inactive', set: true, @@ -73,8 +73,8 @@ export const crmContactsMock = [ { id: 5, email: 'contact.5@email.com', - firstName: 'Contact', - lastName: 'Five', + first_name: 'Contact', + last_name: 'Five', search: 'contact.5@email.com', state: 'active', set: true, @@ -82,8 +82,8 @@ export const crmContactsMock = [ { id: 5, email: 'contact.6@email.com', - firstName: 'Contact', - lastName: 'Six', + first_name: 'Contact', + last_name: 'Six', search: 'contact.6@email.com', state: 'active', set: undefined, // On purpose diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js index eeef92d4183..cc2dc084e47 100644 --- a/spec/frontend/gfm_auto_complete_spec.js +++ b/spec/frontend/gfm_auto_complete_spec.js @@ -4,6 +4,7 @@ import $ from 'jquery'; import labelsFixture from 'test_fixtures/autocomplete_sources/labels.json'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import GfmAutoComplete, { + escape, membersBeforeSave, highlighter, CONTACT_STATE_ACTIVE, @@ -21,6 +22,20 @@ import { crmContactsMock, } from 'ee_else_ce_jest/gfm_auto_complete/mock_data'; +describe('escape', () => { + it.each` + xssPayload | escapedPayload + ${'<script>alert(1)</script>'} | ${'<script>alert(1)</script>'} + ${'%3Cscript%3E alert(1) %3C%2Fscript%3E'} | ${'<script> alert(1) </script>'} + ${'%253Cscript%253E alert(1) %253C%252Fscript%253E'} | ${'<script> alert(1) </script>'} + `( + 'escapes the input string correctly accounting for multiple encoding', + ({ xssPayload, escapedPayload }) => { + expect(escape(xssPayload)).toBe(escapedPayload); + }, + ); +}); + describe('GfmAutoComplete', () => { const fetchDataMock = { fetchData: jest.fn() }; let gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call(fetchDataMock); @@ -590,7 +605,7 @@ describe('GfmAutoComplete', () => { id: 5, title: '${search}<script>oh no $', // eslint-disable-line no-template-curly-in-string }), - ).toBe('<li><small>5</small> ${search}<script>oh no $</li>'); + ).toBe('<li><small>5</small> &dollar;{search}<script>oh no &dollar;</li>'); }); }); @@ -636,7 +651,7 @@ describe('GfmAutoComplete', () => { availabilityStatus: '', }), ).toBe( - '<li>IMG my-group <small>${search}<script>oh no $</small> <i class="icon"/></li>', + '<li>IMG my-group <small>&dollar;{search}<script>oh no &dollar;</small> <i class="icon"/></li>', ); }); @@ -813,7 +828,7 @@ describe('GfmAutoComplete', () => { const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string expect(GfmAutoComplete.Labels.templateFunction(color, title)).toBe( - '<li><span class="dropdown-label-box" style="background: #123456"></span> ${search}<script>oh no $</li>', + '<li><span class="dropdown-label-box" style="background: #123456"></span> &dollar;{search}<script>oh no &dollar;</li>', ); }); }); @@ -868,7 +883,7 @@ describe('GfmAutoComplete', () => { const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string expect(GfmAutoComplete.Milestones.templateFunction(title, expired)).toBe( - '<li>${search}<script>oh no $</li>', + '<li>&dollar;{search}<script>oh no &dollar;</li>', ); }); }); @@ -925,7 +940,9 @@ describe('GfmAutoComplete', () => { const expectContacts = ({ input, output }) => { triggerDropdown(input); - expect(getDropdownItems()).toEqual(output.map((contact) => contact.email)); + expect(getDropdownItems()).toEqual( + output.map((contact) => `${contact.first_name} ${contact.last_name} ${contact.email}`), + ); }; describe('with no contacts assigned', () => { diff --git a/spec/frontend/jobs/components/table/jobs_table_spec.js b/spec/frontend/jobs/components/table/jobs_table_spec.js index 803df3df37f..3c4f2d624fe 100644 --- a/spec/frontend/jobs/components/table/jobs_table_spec.js +++ b/spec/frontend/jobs/components/table/jobs_table_spec.js @@ -2,14 +2,14 @@ import { GlTable } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import JobsTable from '~/jobs/components/table/jobs_table.vue'; -import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; import { mockJobsNodes } from '../../mock_data'; describe('Jobs Table', () => { let wrapper; const findTable = () => wrapper.findComponent(GlTable); - const findStatusBadge = () => wrapper.findComponent(CiBadge); + const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink); const findTableRows = () => wrapper.findAllByTestId('jobs-table-row'); const findJobStage = () => wrapper.findByTestId('job-stage-name'); const findJobName = () => wrapper.findByTestId('job-name'); @@ -43,7 +43,7 @@ describe('Jobs Table', () => { }); it('displays job status', () => { - expect(findStatusBadge().exists()).toBe(true); + expect(findCiBadgeLink().exists()).toBe(true); }); it('displays the job stage and name', () => { diff --git a/spec/frontend/members/components/table/role_dropdown_spec.js b/spec/frontend/members/components/table/role_dropdown_spec.js index b254cce4d72..3815064b3f6 100644 --- a/spec/frontend/members/components/table/role_dropdown_spec.js +++ b/spec/frontend/members/components/table/role_dropdown_spec.js @@ -4,11 +4,14 @@ import { within } from '@testing-library/dom'; import { mount, createWrapper } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; +import waitForPromises from 'helpers/wait_for_promises'; import RoleDropdown from '~/members/components/table/role_dropdown.vue'; import { MEMBER_TYPES } from '~/members/constants'; +import { guestOverageConfirmAction } from 'ee_else_ce/members/guest_overage_confirm_action'; import { member } from '../../mock_data'; Vue.use(Vuex); +jest.mock('ee_else_ce/members/guest_overage_confirm_action'); describe('RoleDropdown', () => { let wrapper; @@ -63,12 +66,21 @@ describe('RoleDropdown', () => { const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]'); const findDropdown = () => wrapper.findComponent(GlDropdown); + let originalGon; + + beforeEach(() => { + originalGon = window.gon; + gon.features = { showOverageOnRolePromotion: true }; + }); + afterEach(() => { + window.gon = originalGon; wrapper.destroy(); }); describe('when dropdown is open', () => { beforeEach(() => { + guestOverageConfirmAction.mockReturnValue(true); createComponent(); return findDropdownToggle().trigger('click'); @@ -117,8 +129,12 @@ describe('RoleDropdown', () => { await getDropdownItemByText('Developer').trigger('click'); expect(findDropdown().props('disabled')).toBe(true); + }); - await nextTick(); + it('enables dropdown after `updateMemberRole` resolves', async () => { + await getDropdownItemByText('Developer').trigger('click'); + + await waitForPromises(); expect(findDropdown().props('disabled')).toBe(false); }); @@ -148,4 +164,44 @@ describe('RoleDropdown', () => { expect(findDropdown().props('right')).toBe(false); }); + + describe('guestOverageConfirmAction', () => { + const mockConfirmAction = ({ confirmed }) => { + guestOverageConfirmAction.mockResolvedValueOnce(confirmed); + }; + + beforeEach(() => { + createComponent(); + + findDropdownToggle().trigger('click'); + }); + + afterEach(() => { + guestOverageConfirmAction.mockReset(); + }); + + describe('when guestOverageConfirmAction returns true', () => { + beforeEach(() => { + mockConfirmAction({ confirmed: true }); + + getDropdownItemByText('Reporter').trigger('click'); + }); + + it('calls updateMemberRole', () => { + expect(actions.updateMemberRole).toHaveBeenCalled(); + }); + }); + + describe('when guestOverageConfirmAction returns false', () => { + beforeEach(() => { + mockConfirmAction({ confirmed: false }); + + getDropdownItemByText('Reporter').trigger('click'); + }); + + it('does not call updateMemberRole', () => { + expect(actions.updateMemberRole).not.toHaveBeenCalled(); + }); + }); + }); }); diff --git a/spec/frontend/members/guest_overage_confirm_action_spec.js b/spec/frontend/members/guest_overage_confirm_action_spec.js new file mode 100644 index 00000000000..d7ab54fa13b --- /dev/null +++ b/spec/frontend/members/guest_overage_confirm_action_spec.js @@ -0,0 +1,7 @@ +import { guestOverageConfirmAction } from '~/members/guest_overage_confirm_action'; + +describe('guestOverageConfirmAction', () => { + it('returns true', () => { + expect(guestOverageConfirmAction()).toBe(true); + }); +}); diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index 740037a5ac8..9359bd9b95f 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -17,7 +17,7 @@ import { TRACKING_CATEGORIES, } from '~/pipelines/constants'; -import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; jest.mock('~/pipelines/event_hub'); @@ -50,7 +50,7 @@ describe('Pipelines Table', () => { }; const findGlTableLite = () => wrapper.findComponent(GlTableLite); - const findStatusBadge = () => wrapper.findComponent(CiBadge); + const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink); const findPipelineInfo = () => wrapper.findComponent(PipelineUrl); const findTriggerer = () => wrapper.findComponent(PipelineTriggerer); const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); @@ -97,7 +97,7 @@ describe('Pipelines Table', () => { describe('status cell', () => { it('should render a status badge', () => { - expect(findStatusBadge().exists()).toBe(true); + expect(findCiBadgeLink().exists()).toBe(true); }); }); @@ -171,7 +171,7 @@ describe('Pipelines Table', () => { }); it('tracks status badge click', () => { - findStatusBadge().vm.$emit('ciStatusBadgeClick'); + findCiBadgeLink().vm.$emit('ciStatusBadgeClick'); expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', { label: TRACKING_CATEGORIES.table, diff --git a/spec/frontend/repository/commits_service_spec.js b/spec/frontend/repository/commits_service_spec.js index de7c56f239a..b7343bf3a7e 100644 --- a/spec/frontend/repository/commits_service_spec.js +++ b/spec/frontend/repository/commits_service_spec.js @@ -4,6 +4,7 @@ import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/co import httpStatus from '~/lib/utils/http_status'; import { createAlert } from '~/flash'; import { I18N_COMMIT_DATA_FETCH_ERROR } from '~/repository/constants'; +import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from './mock_data'; jest.mock('~/flash'); @@ -39,10 +40,11 @@ describe('commits service', () => { expect(axios.get).toHaveBeenCalledWith(testUrl, { params: { format: 'json', offset } }); }); - it('encodes the path correctly', async () => { - await requestCommits(1, 'some-project', 'with $peci@l ch@rs/'); + it('encodes the path and ref', async () => { + const encodedUrl = `/some-project/-/refs/${encodedRefWithSpecialCharMock}/logs_tree/with%20%24peci%40l%20ch%40rs%2F`; + + await requestCommits(1, 'some-project', 'with $peci@l ch@rs/', refWithSpecialCharMock); - const encodedUrl = '/some-project/-/refs/main/logs_tree/with%20%24peci%40l%20ch%40rs%2F'; expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything()); }); diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index cda47a5b0a5..c1b5f89c37f 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -87,6 +87,8 @@ export const applicationInfoMock = { gitpodEnabled: true }; export const propsMock = { path: 'some_file.js', projectPath: 'some/path' }; export const refMock = 'default-ref'; +export const refWithSpecialCharMock = 'selected-#-ref'; +export const encodedRefWithSpecialCharMock = encodeURIComponent(refWithSpecialCharMock); export const blobControlsDataMock = { id: '1234', diff --git a/spec/frontend/repository/utils/ref_switcher_utils_spec.js b/spec/frontend/repository/utils/ref_switcher_utils_spec.js index 3335059554f..4d0250fffbf 100644 --- a/spec/frontend/repository/utils/ref_switcher_utils_spec.js +++ b/spec/frontend/repository/utils/ref_switcher_utils_spec.js @@ -1,5 +1,6 @@ import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils'; import setWindowLocation from 'helpers/set_window_location_helper'; +import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from '../mock_data'; const projectRootPath = 'root/Project1'; const currentRef = 'main'; @@ -19,4 +20,10 @@ describe('generateRefDestinationPath', () => { setWindowLocation(currentPath); expect(generateRefDestinationPath(projectRootPath, selectedRef)).toBe(result); }); + + it('encodes the selected ref', () => { + const result = `${projectRootPath}/-/tree/${encodedRefWithSpecialCharMock}`; + + expect(generateRefDestinationPath(projectRootPath, refWithSpecialCharMock)).toBe(result); + }); }); diff --git a/spec/frontend/users/profile/components/report_abuse_button_spec.js b/spec/frontend/users/profile/components/report_abuse_button_spec.js new file mode 100644 index 00000000000..bd39a089473 --- /dev/null +++ b/spec/frontend/users/profile/components/report_abuse_button_spec.js @@ -0,0 +1,72 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + +import ReportAbuseButton from '~/users/profile/components/report_abuse_button.vue'; +import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; + +describe('ReportAbuseButton', () => { + let wrapper; + + const ACTION_PATH = '/abuse_reports/add_category'; + const USER_ID = '1'; + const REPORTED_FROM_URL = 'http://example.com'; + + const createComponent = (props) => { + wrapper = shallowMountExtended(ReportAbuseButton, { + propsData: { + ...props, + }, + provide: { + formSubmitPath: ACTION_PATH, + userId: USER_ID, + reportedFromUrl: REPORTED_FROM_URL, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findReportAbuseButton = () => wrapper.findComponent(GlButton); + const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector); + + it('renders report abuse button', () => { + expect(findReportAbuseButton().exists()).toBe(true); + + expect(findReportAbuseButton().props()).toMatchObject({ + category: 'primary', + icon: 'error', + }); + + expect(findReportAbuseButton().attributes('aria-label')).toBe( + wrapper.vm.$options.i18n.reportAbuse, + ); + }); + + it('renders abuse category selector with the drawer initially closed', () => { + expect(findAbuseCategorySelector().exists()).toBe(true); + + expect(findAbuseCategorySelector().props('showDrawer')).toBe(false); + }); + + describe('when button is clicked', () => { + beforeEach(async () => { + await findReportAbuseButton().vm.$emit('click'); + }); + + it('opens the abuse category selector', () => { + expect(findAbuseCategorySelector().props('showDrawer')).toBe(true); + }); + + it('closes the abuse category selector', async () => { + await findAbuseCategorySelector().vm.$emit('close-drawer'); + + expect(findAbuseCategorySelector().props('showDrawer')).toBe(false); + }); + }); +}); diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap deleted file mode 100644 index 4077564486c..00000000000 --- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap +++ /dev/null @@ -1,163 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = ` -<div - class="mr-widget-body media gl-display-flex gl-align-items-center" -> - <div - class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3" - > - <div - class="gl-display-flex gl-m-auto" - > - <div - class="gl-mr-3 gl-p-2 gl-m-0! gl-text-blue-500 gl-w-6 gl-p-2" - > - <div - class="gl-rounded-full gl-relative gl-display-flex mr-widget-extension-icon" - > - <div - class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto" - > - <div - class="gl-display-flex gl-m-auto gl-translate-y-n50" - > - <svg - aria-label="Scheduled " - class="gl-display-block gl-icon s12" - data-qa-selector="status_scheduled_icon" - data-testid="status-scheduled-icon" - role="img" - > - <use - href="#status-scheduled" - /> - </svg> - </div> - </div> - </div> - </div> - </div> - </div> - - <div - class="gl-display-flex gl-w-full" - > - <div - class="media-body gl-display-flex gl-align-items-center" - > - - <h4 - class="gl-mr-3" - data-testid="statusText" - > - Set by to be merged automatically when the pipeline succeeds - </h4> - - <div - class="gl-display-flex gl-font-size-0 gl-ml-auto gl-gap-3" - > - <div - class="gl-display-flex gl-align-items-flex-start" - > - <div - class="dropdown b-dropdown gl-dropdown gl-display-block gl-md-display-none! btn-group" - lazy="" - no-caret="" - title="Options" - > - <!----> - <button - aria-expanded="false" - aria-haspopup="true" - class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret" - type="button" - > - <!----> - - <svg - aria-hidden="true" - class="dropdown-icon gl-icon s16" - data-testid="ellipsis_v-icon" - role="img" - > - <use - href="#ellipsis_v" - /> - </svg> - - <span - class="gl-dropdown-button-text gl-sr-only" - > - - </span> - - <svg - aria-hidden="true" - class="gl-button-icon dropdown-chevron gl-icon s16" - data-testid="chevron-down-icon" - role="img" - > - <use - href="#chevron-down" - /> - </svg> - </button> - <ul - class="dropdown-menu dropdown-menu-right" - role="menu" - tabindex="-1" - > - <!----> - </ul> - </div> - - <button - class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge" - data-qa-selector="cancel_auto_merge_button" - data-testid="cancelAutomaticMergeButton" - type="button" - > - <!----> - - <!----> - - <span - class="gl-button-text" - > - - Cancel auto-merge - - </span> - </button> - </div> - </div> - </div> - - <div - class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1" - > - <button - class="btn gl-vertical-align-top btn-default btn-sm gl-button btn-default-tertiary btn-icon" - title="Collapse merge details" - type="button" - > - <!----> - - <svg - aria-hidden="true" - class="gl-button-icon gl-icon s16" - data-testid="chevron-lg-up-icon" - role="img" - > - <use - href="#chevron-lg-up" - /> - </svg> - - <!----> - </button> - </div> - </div> -</div> -`; diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js index 5b9f30dfb86..fef5fee5f19 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js @@ -128,14 +128,6 @@ describe('MRWidgetAutoMergeEnabled', () => { }); describe('template', () => { - it('should have correct elements', () => { - factory({ - ...defaultMrProps(), - }); - - expect(wrapper.element).toMatchSnapshot(); - }); - it('should disable cancel auto merge button when the action is in progress', async () => { factory({ ...defaultMrProps(), diff --git a/spec/frontend/vue_shared/components/ci_badge_link_spec.js b/spec/frontend/vue_shared/components/ci_badge_link_spec.js index 07cbfe1e79b..4f24ec2d015 100644 --- a/spec/frontend/vue_shared/components/ci_badge_link_spec.js +++ b/spec/frontend/vue_shared/components/ci_badge_link_spec.js @@ -1,6 +1,6 @@ import { GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; jest.mock('~/lib/utils/url_utility', () => ({ @@ -79,7 +79,7 @@ describe('CI Badge Link Component', () => { const findIcon = () => wrapper.findComponent(CiIcon); const createComponent = (propsData) => { - wrapper = shallowMount(CiBadge, { propsData }); + wrapper = shallowMount(CiBadgeLink, { propsData }); }; afterEach(() => { |