diff options
Diffstat (limited to 'spec/frontend/admin/abuse_report')
-rw-r--r-- | spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js | 80 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/activity_events_list_spec.js | 30 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/activity_history_item_spec.js (renamed from spec/frontend/admin/abuse_report/components/history_items_spec.js) | 20 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/labels_select_spec.js | 297 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/report_actions_spec.js | 27 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/report_details_spec.js | 74 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/report_header_spec.js | 55 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/reported_content_spec.js | 11 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/components/user_details_spec.js | 62 | ||||
-rw-r--r-- | spec/frontend/admin/abuse_report/mock_data.js | 88 |
10 files changed, 619 insertions, 125 deletions
diff --git a/spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js b/spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js index e519684bbc5..4340699a7ed 100644 --- a/spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js +++ b/spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js @@ -1,28 +1,46 @@ -import { shallowMount } from '@vue/test-utils'; import { GlAlert } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import AbuseReportApp from '~/admin/abuse_report/components/abuse_report_app.vue'; import ReportHeader from '~/admin/abuse_report/components/report_header.vue'; import UserDetails from '~/admin/abuse_report/components/user_details.vue'; +import ReportDetails from '~/admin/abuse_report/components/report_details.vue'; import ReportedContent from '~/admin/abuse_report/components/reported_content.vue'; -import HistoryItems from '~/admin/abuse_report/components/history_items.vue'; +import ActivityEventsList from '~/admin/abuse_report/components/activity_events_list.vue'; +import ActivityHistoryItem from '~/admin/abuse_report/components/activity_history_item.vue'; import { SUCCESS_ALERT } from '~/admin/abuse_report/constants'; import { mockAbuseReport } from '../mock_data'; describe('AbuseReportApp', () => { let wrapper; + const { similarOpenReports } = mockAbuseReport.user; + const findAlert = () => wrapper.findComponent(GlAlert); const findReportHeader = () => wrapper.findComponent(ReportHeader); const findUserDetails = () => wrapper.findComponent(UserDetails); - const findReportedContent = () => wrapper.findComponent(ReportedContent); - const findHistoryItems = () => wrapper.findComponent(HistoryItems); - const createComponent = (props = {}) => { - wrapper = shallowMount(AbuseReportApp, { + const findReportedContent = () => wrapper.findByTestId('reported-content'); + const findReportedContentForSimilarReports = () => + wrapper.findAllByTestId('reported-content-similar-open-reports'); + const firstReportedContentForSimilarReports = () => + findReportedContentForSimilarReports().at(0).findComponent(ReportedContent); + + const findActivityList = () => wrapper.findComponent(ActivityEventsList); + const findActivityItem = () => wrapper.findByTestId('activity'); + const findActivityForSimilarReports = () => + wrapper.findAllByTestId('activity-similar-open-reports'); + const firstActivityForSimilarReports = () => + findActivityForSimilarReports().at(0).findComponent(ActivityHistoryItem); + + const findReportDetails = () => wrapper.findComponent(ReportDetails); + + const createComponent = (props = {}, provide = {}) => { + wrapper = shallowMountExtended(AbuseReportApp, { propsData: { abuseReport: mockAbuseReport, ...props, }, + provide, }); }; @@ -64,7 +82,7 @@ describe('AbuseReportApp', () => { }); }); - describe('ReportHeader', () => { + describe('Report header', () => { it('renders ReportHeader', () => { expect(findReportHeader().props('user')).toBe(mockAbuseReport.user); expect(findReportHeader().props('report')).toBe(mockAbuseReport.report); @@ -83,7 +101,7 @@ describe('AbuseReportApp', () => { }); }); - describe('UserDetails', () => { + describe('User Details', () => { it('renders UserDetails', () => { expect(findUserDetails().props('user')).toBe(mockAbuseReport.user); }); @@ -101,13 +119,47 @@ describe('AbuseReportApp', () => { }); }); - it('renders ReportedContent', () => { - expect(findReportedContent().props('report')).toBe(mockAbuseReport.report); - expect(findReportedContent().props('reporter')).toBe(mockAbuseReport.reporter); + describe('Reported Content', () => { + it('renders ReportedContent', () => { + expect(findReportedContent().props('report')).toBe(mockAbuseReport.report); + }); + + it('renders similar abuse reports', () => { + expect(findReportedContentForSimilarReports()).toHaveLength(similarOpenReports.length); + expect(firstReportedContentForSimilarReports().props('report')).toBe(similarOpenReports[0]); + }); }); - it('renders HistoryItems', () => { - expect(findHistoryItems().props('report')).toBe(mockAbuseReport.report); - expect(findHistoryItems().props('reporter')).toBe(mockAbuseReport.reporter); + describe('ReportDetails', () => { + describe('when abuseReportLabels feature flag is enabled', () => { + it('renders ReportDetails', () => { + createComponent({}, { glFeatures: { abuseReportLabels: true } }); + + expect(findReportDetails().props('reportId')).toBe(mockAbuseReport.report.globalId); + }); + }); + + describe('when abuseReportLabels feature flag is disabled', () => { + it('does not render ReportDetails', () => { + createComponent({}, { glFeatures: { abuseReportLabels: false } }); + + expect(findReportDetails().exists()).toBe(false); + }); + }); + }); + + describe('Activity', () => { + it('renders the activity events list', () => { + expect(findActivityList().exists()).toBe(true); + }); + + it('renders activity item for abuse report', () => { + expect(findActivityItem().props('report')).toBe(mockAbuseReport.report); + }); + + it('renders activity items for similar abuse reports', () => { + expect(findActivityForSimilarReports()).toHaveLength(similarOpenReports.length); + expect(firstActivityForSimilarReports().props('report')).toBe(similarOpenReports[0]); + }); }); }); diff --git a/spec/frontend/admin/abuse_report/components/activity_events_list_spec.js b/spec/frontend/admin/abuse_report/components/activity_events_list_spec.js new file mode 100644 index 00000000000..cd1120d2db4 --- /dev/null +++ b/spec/frontend/admin/abuse_report/components/activity_events_list_spec.js @@ -0,0 +1,30 @@ +import { shallowMount } from '@vue/test-utils'; +import ActivityEventsList from '~/admin/abuse_report/components/activity_events_list.vue'; + +describe('ActivityEventsList', () => { + let wrapper; + + const mockSlotContent = 'Test slot content'; + + const findActivityEventsList = () => wrapper.findComponent(ActivityEventsList); + + const createComponent = () => { + wrapper = shallowMount(ActivityEventsList, { + slots: { + 'history-items': mockSlotContent, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + it('renders activity title', () => { + expect(findActivityEventsList().text()).toContain('Activity'); + }); + + it('renders history-items slot', () => { + expect(findActivityEventsList().text()).toContain(mockSlotContent); + }); +}); diff --git a/spec/frontend/admin/abuse_report/components/history_items_spec.js b/spec/frontend/admin/abuse_report/components/activity_history_item_spec.js index 86e994fdc57..3f430b0143e 100644 --- a/spec/frontend/admin/abuse_report/components/history_items_spec.js +++ b/spec/frontend/admin/abuse_report/components/activity_history_item_spec.js @@ -1,25 +1,23 @@ import { GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { sprintf } from '~/locale'; -import HistoryItems from '~/admin/abuse_report/components/history_items.vue'; +import AcitivityHistoryItem from '~/admin/abuse_report/components/activity_history_item.vue'; import HistoryItem from '~/vue_shared/components/registry/history_item.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import { HISTORY_ITEMS_I18N } from '~/admin/abuse_report/constants'; import { mockAbuseReport } from '../mock_data'; -describe('HistoryItems', () => { +describe('AcitivityHistoryItem', () => { let wrapper; - const { report, reporter } = mockAbuseReport; + const { report } = mockAbuseReport; const findHistoryItem = () => wrapper.findComponent(HistoryItem); const findTimeAgo = () => wrapper.findComponent(TimeAgoTooltip); const createComponent = (props = {}) => { - wrapper = shallowMount(HistoryItems, { + wrapper = shallowMount(AcitivityHistoryItem, { propsData: { report, - reporter, ...props, }, stubs: { @@ -38,8 +36,8 @@ describe('HistoryItems', () => { describe('rendering the title', () => { it('renders the reporters name and the category', () => { - const title = sprintf(HISTORY_ITEMS_I18N.reportedByForCategory, { - name: reporter.name, + const title = sprintf('Reported by %{name} for %{category}.', { + name: report.reporter.name, category: report.category, }); expect(findHistoryItem().text()).toContain(title); @@ -47,12 +45,12 @@ describe('HistoryItems', () => { describe('when the reporter is not defined', () => { beforeEach(() => { - createComponent({ reporter: undefined }); + createComponent({ report: { ...report, reporter: undefined } }); }); it('renders the `No user found` as the reporters name and the category', () => { - const title = sprintf(HISTORY_ITEMS_I18N.reportedByForCategory, { - name: HISTORY_ITEMS_I18N.deletedReporter, + const title = sprintf('Reported by %{name} for %{category}.', { + name: 'No user found', category: report.category, }); expect(findHistoryItem().text()).toContain(title); diff --git a/spec/frontend/admin/abuse_report/components/labels_select_spec.js b/spec/frontend/admin/abuse_report/components/labels_select_spec.js new file mode 100644 index 00000000000..a22dcc18e10 --- /dev/null +++ b/spec/frontend/admin/abuse_report/components/labels_select_spec.js @@ -0,0 +1,297 @@ +import MockAdapter from 'axios-mock-adapter'; +import { GlButton, GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK, HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status'; +import LabelsSelect from '~/admin/abuse_report/components/labels_select.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { stubComponent, RENDER_ALL_SLOTS_TEMPLATE } from 'helpers/stub_component'; +import labelsQuery from '~/admin/abuse_report/components/graphql/abuse_report_labels.query.graphql'; +import DropdownWidget from '~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue'; +import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue'; +import DropdownHeader from '~/sidebar/components/labels/labels_select_widget/dropdown_header.vue'; +import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue'; +import DropdownFooter from '~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue'; +import { createAlert } from '~/alert'; +import { mockLabelsQueryResponse, mockLabel1, mockLabel2 } from '../mock_data'; + +jest.mock('~/alert'); + +Vue.use(VueApollo); + +describe('Labels select component', () => { + let mock; + let wrapper; + let fakeApollo; + + const selectedText = () => wrapper.findByTestId('selected-labels').text(); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findEditButton = () => wrapper.findComponent(GlButton); + const findDropdown = () => wrapper.findComponent(DropdownWidget); + const findDropdownHeader = () => wrapper.findComponent(DropdownHeader); + const findDropdownValue = () => wrapper.findComponent(DropdownValue); + const findCreateView = () => wrapper.findComponent(DropdownContentsCreateView); + const findDropdownFooter = () => wrapper.findComponent(DropdownFooter); + + const labelsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockLabelsQueryResponse); + const labelsQueryHandlerFailure = jest.fn().mockRejectedValue(new Error()); + + const updatePath = '/admin/abuse_reports/1'; + const listPath = '/admin/abuse_reports'; + + async function openLabelsDropdown() { + findEditButton().vm.$emit('click'); + await waitForPromises(); + } + + const selectLabel = (label) => { + findDropdown().vm.$emit('set-option', label); + nextTick(); + }; + + const createComponent = ({ props = {}, labelsQueryHandler = labelsQueryHandlerSuccess } = {}) => { + fakeApollo = createMockApollo([[labelsQuery, labelsQueryHandler]]); + wrapper = shallowMountExtended(LabelsSelect, { + apolloProvider: fakeApollo, + propsData: { + report: { labels: [] }, + canEdit: true, + ...props, + }, + provide: { + updatePath, + listPath, + }, + stubs: { + GlDropdown, + GlDropdownItem, + DropdownWidget: stubComponent(DropdownWidget, { + template: RENDER_ALL_SLOTS_TEMPLATE, + methods: { showDropdown: jest.fn() }, + }), + }, + }); + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + fakeApollo = null; + mock.restore(); + }); + + describe('initial load', () => { + beforeEach(() => { + createComponent(); + }); + + it('displays loading icon', () => { + expect(findLoadingIcon().exists()).toEqual(true); + }); + + it('disables edit button', () => { + expect(findEditButton().props('disabled')).toEqual(true); + }); + + describe('after initial load', () => { + beforeEach(() => { + wrapper.setProps({ report: { labels: [mockLabel1] } }); + }); + + it('does not display loading icon', () => { + expect(findLoadingIcon().exists()).toEqual(false); + }); + + it('enables edit button', () => { + expect(findEditButton().props('disabled')).toEqual(false); + }); + + it('renders fetched DropdownValue with the correct props', () => { + const component = findDropdownValue(); + expect(component.isVisible()).toBe(true); + expect(component.props('selectedLabels')).toEqual([mockLabel1]); + expect(component.props('labelsFilterBasePath')).toBe(listPath); + }); + }); + }); + + describe('when there are no selected labels', () => { + it('displays "None"', () => { + createComponent(); + + expect(selectedText()).toContain('None'); + }); + }); + + describe('when there are selected labels', () => { + beforeEach(() => { + createComponent({ props: { report: { labels: [mockLabel1, mockLabel2] } } }); + + mock.onPut(updatePath).reply(HTTP_STATUS_OK, {}); + jest.spyOn(axios, 'put'); + }); + + it('renders selected labels in DropdownValue', () => { + expect(findDropdownValue().isVisible()).toBe(true); + expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel1, mockLabel2]); + }); + + it('selected labels can be removed', async () => { + findDropdownValue().vm.$emit('onLabelRemove', mockLabel1.id); + await nextTick(); + + expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel2]); + expect(axios.put).toHaveBeenCalledWith(updatePath, { + label_ids: [mockLabel2.id], + }); + }); + }); + + describe('when not editing', () => { + beforeEach(() => { + createComponent(); + }); + + it('does not trigger abuse report labels query', () => { + expect(labelsQueryHandlerSuccess).not.toHaveBeenCalled(); + }); + + it('does not render the dropdown', () => { + expect(findDropdown().isVisible()).toBe(false); + }); + }); + + describe('when editing', () => { + beforeEach(async () => { + createComponent(); + await openLabelsDropdown(); + }); + + it('triggers abuse report labels query', () => { + expect(labelsQueryHandlerSuccess).toHaveBeenCalledTimes(1); + }); + + it('renders dropdown with fetched labels', () => { + expect(findDropdown().isVisible()).toBe(true); + expect(findDropdown().props('options')).toEqual([mockLabel1, mockLabel2]); + }); + + it('selects/deselects a label', async () => { + await selectLabel(mockLabel1); + + expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel1]); + + await selectLabel(mockLabel1); + + expect(selectedText()).toContain('None'); + }); + + it('triggers abuse report labels query when search term is set', async () => { + findDropdown().vm.$emit('set-search', 'Dos'); + await waitForPromises(); + + expect(labelsQueryHandlerSuccess).toHaveBeenCalledTimes(2); + expect(labelsQueryHandlerSuccess).toHaveBeenCalledWith({ searchTerm: 'Dos' }); + }); + + it('does not render DropdownContentsCreateView', () => { + expect(findCreateView().exists()).toBe(false); + }); + + it('renders DropdownFooter', () => { + expect(findDropdownFooter().props('footerCreateLabelTitle')).toEqual('Create label'); + expect(findDropdownFooter().props('footerManageLabelTitle')).toEqual(''); + }); + + describe('when DropdownHeader emits `toggleDropdownContentsCreateView` event', () => { + beforeEach(() => { + findDropdownHeader().vm.$emit('toggleDropdownContentsCreateView'); + }); + + it('renders DropdownContentsCreateView and removes DropdownFooter', () => { + expect(findCreateView().props('workspaceType')).toEqual('abuseReport'); + expect(findDropdownFooter().exists()).toBe(false); + }); + + describe('when DropdownContentsCreateView emits `hideCreateView` event', () => { + it('removes itself', async () => { + findCreateView().vm.$emit('hideCreateView'); + await nextTick(); + + expect(findCreateView().exists()).toBe(false); + }); + }); + + describe('when DropdownContentsCreateView emits `labelCreated` event', () => { + it('selects created label', async () => { + findCreateView().vm.$emit('labelCreated', mockLabel1); + await nextTick(); + + expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel1]); + }); + }); + }); + + describe('when DropdownFooter emits `toggleDropdownContentsCreateView` event', () => { + it('renders DropdownContentsCreateView', async () => { + findDropdownFooter().vm.$emit('toggleDropdownContentsCreateView'); + await nextTick(); + + expect(findCreateView().props('workspaceType')).toEqual('abuseReport'); + }); + }); + }); + + describe('after edit', () => { + const setup = async (response) => { + mock.onPut(updatePath).reply(response, {}); + jest.spyOn(axios, 'put'); + + createComponent(); + await openLabelsDropdown(); + await selectLabel(mockLabel1); + + findDropdown().vm.$emit('hide'); + }; + + describe('successful save', () => { + it('saves', async () => { + await setup(HTTP_STATUS_OK); + + expect(axios.put).toHaveBeenCalledWith(updatePath, { + label_ids: [mockLabel1.id], + }); + }); + }); + + describe('unsuccessful save', () => { + it('creates an alert', async () => { + await setup(HTTP_STATUS_INTERNAL_SERVER_ERROR); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: 'An error occurred while updating labels.', + captureError: true, + error: expect.any(Error), + }); + }); + }); + }); + + describe('failed abuse report labels query', () => { + it('creates an alert', async () => { + createComponent({ labelsQueryHandler: labelsQueryHandlerFailure }); + await openLabelsDropdown(); + + expect(createAlert).toHaveBeenCalledWith({ + message: 'An error occurred while searching for labels, please try again.', + }); + }); + }); +}); diff --git a/spec/frontend/admin/abuse_report/components/report_actions_spec.js b/spec/frontend/admin/abuse_report/components/report_actions_spec.js index 6dd6d0e55c5..0e20630db14 100644 --- a/spec/frontend/admin/abuse_report/components/report_actions_spec.js +++ b/spec/frontend/admin/abuse_report/components/report_actions_spec.js @@ -191,31 +191,4 @@ describe('ReportActions', () => { ); }); }); - - describe('when moderateUserPath is not present', () => { - it('sends the request to updatePath', async () => { - jest.spyOn(axios, 'put'); - axiosMock.onPut(report.updatePath).replyOnce(HTTP_STATUS_OK, {}); - - const reportWithoutModerateUserPath = { ...report }; - delete reportWithoutModerateUserPath.moderateUserPath; - - createComponent({ report: reportWithoutModerateUserPath }); - - clickActionsButton(); - - await nextTick(); - - selectAction(params.user_action); - selectReason(params.reason); - - await nextTick(); - - submitForm(); - - await waitForPromises(); - - expect(axios.put).toHaveBeenCalledWith(report.updatePath, expect.any(Object)); - }); - }); }); diff --git a/spec/frontend/admin/abuse_report/components/report_details_spec.js b/spec/frontend/admin/abuse_report/components/report_details_spec.js new file mode 100644 index 00000000000..a5c43dcb82b --- /dev/null +++ b/spec/frontend/admin/abuse_report/components/report_details_spec.js @@ -0,0 +1,74 @@ +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import LabelsSelect from '~/admin/abuse_report/components/labels_select.vue'; +import ReportDetails from '~/admin/abuse_report/components/report_details.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import abuseReportQuery from '~/admin/abuse_report/components/graphql/abuse_report.query.graphql'; +import { createAlert } from '~/alert'; +import { mockAbuseReport, mockLabel1, mockReportQueryResponse } from '../mock_data'; + +jest.mock('~/alert'); + +Vue.use(VueApollo); + +describe('Report Details', () => { + let wrapper; + let fakeApollo; + + const findLabelsSelect = () => wrapper.findComponent(LabelsSelect); + + const abuseReportQueryHandlerSuccess = jest.fn().mockResolvedValue(mockReportQueryResponse); + const abuseReportQueryHandlerFailure = jest.fn().mockRejectedValue(new Error()); + + const createComponent = ({ abuseReportQueryHandler = abuseReportQueryHandlerSuccess } = {}) => { + fakeApollo = createMockApollo([[abuseReportQuery, abuseReportQueryHandler]]); + wrapper = shallowMount(ReportDetails, { + apolloProvider: fakeApollo, + propsData: { + reportId: mockAbuseReport.report.globalId, + }, + }); + }; + + afterEach(() => { + fakeApollo = null; + }); + + describe('successful abuse report query', () => { + beforeEach(() => { + createComponent(); + }); + + it('triggers abuse report query', async () => { + await waitForPromises(); + + expect(abuseReportQueryHandlerSuccess).toHaveBeenCalledWith({ + id: mockAbuseReport.report.globalId, + }); + }); + + it('renders LabelsSelect with the fetched report', async () => { + expect(findLabelsSelect().props('report').labels).toEqual([]); + + await waitForPromises(); + + expect(findLabelsSelect().props('report').labels).toEqual([mockLabel1]); + }); + }); + + describe('failed abuse report query', () => { + beforeEach(async () => { + createComponent({ abuseReportQueryHandler: abuseReportQueryHandlerFailure }); + + await waitForPromises(); + }); + + it('creates an alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: 'An error occurred while fetching labels, please try again.', + }); + }); + }); +}); diff --git a/spec/frontend/admin/abuse_report/components/report_header_spec.js b/spec/frontend/admin/abuse_report/components/report_header_spec.js index f22f3af091f..6ec380f0387 100644 --- a/spec/frontend/admin/abuse_report/components/report_header_spec.js +++ b/spec/frontend/admin/abuse_report/components/report_header_spec.js @@ -54,37 +54,30 @@ describe('ReportHeader', () => { }); describe.each` - status | text | variant | className | badgeIcon - ${STATUS_OPEN} | ${REPORT_HEADER_I18N[STATUS_OPEN]} | ${'success'} | ${'issuable-status-badge-open'} | ${'issues'} - ${STATUS_CLOSED} | ${REPORT_HEADER_I18N[STATUS_CLOSED]} | ${'info'} | ${'issuable-status-badge-closed'} | ${'issue-closed'} - `( - 'rendering the report $status status badge', - ({ status, text, variant, className, badgeIcon }) => { - beforeEach(() => { - createComponent({ report: { ...report, status } }); - }); - - it(`indicates the ${status} status`, () => { - expect(findBadge().text()).toBe(text); - }); - - it(`with the ${variant} variant`, () => { - expect(findBadge().props('variant')).toBe(variant); - }); - - it(`with the text '${text}' as 'aria-label'`, () => { - expect(findBadge().attributes('aria-label')).toBe(text); - }); - - it(`contains the ${className} class`, () => { - expect(findBadge().element.classList).toContain(className); - }); - - it(`has an icon with the ${badgeIcon} name`, () => { - expect(findIcon().props('name')).toBe(badgeIcon); - }); - }, - ); + status | text | variant | badgeIcon + ${STATUS_OPEN} | ${REPORT_HEADER_I18N[STATUS_OPEN]} | ${'success'} | ${'issues'} + ${STATUS_CLOSED} | ${REPORT_HEADER_I18N[STATUS_CLOSED]} | ${'info'} | ${'issue-closed'} + `('rendering the report $status status badge', ({ status, text, variant, badgeIcon }) => { + beforeEach(() => { + createComponent({ report: { ...report, status } }); + }); + + it(`indicates the ${status} status`, () => { + expect(findBadge().text()).toBe(text); + }); + + it(`with the ${variant} variant`, () => { + expect(findBadge().props('variant')).toBe(variant); + }); + + it(`with the text '${text}' as 'aria-label'`, () => { + expect(findBadge().attributes('aria-label')).toBe(text); + }); + + it(`has an icon with the ${badgeIcon} name`, () => { + expect(findIcon().props('name')).toBe(badgeIcon); + }); + }); it('renders the actions', () => { const actionsComponent = findActions(); diff --git a/spec/frontend/admin/abuse_report/components/reported_content_spec.js b/spec/frontend/admin/abuse_report/components/reported_content_spec.js index 9fc49f08f8c..2f16f5a7af2 100644 --- a/spec/frontend/admin/abuse_report/components/reported_content_spec.js +++ b/spec/frontend/admin/abuse_report/components/reported_content_spec.js @@ -14,7 +14,7 @@ const modalId = 'abuse-report-screenshot-modal'; describe('ReportedContent', () => { let wrapper; - const { report, reporter } = { ...mockAbuseReport }; + const { report } = { ...mockAbuseReport }; const findScreenshotButton = () => wrapper.findByTestId('screenshot-button'); const findReportUrlButton = () => wrapper.findByTestId('report-url-button'); @@ -32,7 +32,6 @@ describe('ReportedContent', () => { wrapper = shallowMountExtended(ReportedContent, { propsData: { report, - reporter, ...props, }, stubs: { @@ -167,18 +166,18 @@ describe('ReportedContent', () => { describe('rendering the card footer', () => { it('renders the reporters avatar', () => { - expect(findAvatar().props('src')).toBe(reporter.avatarUrl); + expect(findAvatar().props('src')).toBe(report.reporter.avatarUrl); }); it('renders the users name', () => { - expect(findCardFooter().text()).toContain(reporter.name); + expect(findCardFooter().text()).toContain(report.reporter.name); }); it('renders a link to the users profile page', () => { const link = findProfileLink(); - expect(link.attributes('href')).toBe(reporter.path); - expect(link.text()).toBe(`@${reporter.username}`); + expect(link.attributes('href')).toBe(report.reporter.path); + expect(link.text()).toBe(`@${report.reporter.username}`); }); it('renders the time-ago tooltip', () => { diff --git a/spec/frontend/admin/abuse_report/components/user_details_spec.js b/spec/frontend/admin/abuse_report/components/user_details_spec.js index ca499fbaa6e..f3d8d5bb610 100644 --- a/spec/frontend/admin/abuse_report/components/user_details_spec.js +++ b/spec/frontend/admin/abuse_report/components/user_details_spec.js @@ -18,7 +18,7 @@ describe('UserDetails', () => { const findLinkFor = (attribute) => findLinkIn(findUserDetail(attribute)); const findTimeIn = (component) => component.findComponent(TimeAgoTooltip).props('time'); const findTimeFor = (attribute) => findTimeIn(findUserDetail(attribute)); - const findOtherReport = (index) => wrapper.findByTestId(`other-report-${index}`); + const findPastReport = (index) => wrapper.findByTestId(`past-report-${index}`); const createComponent = (props = {}) => { wrapper = shallowMountExtended(UserDetails, { @@ -38,8 +38,8 @@ describe('UserDetails', () => { describe('createdAt', () => { it('renders the users createdAt with the correct label', () => { - expect(findUserDetailLabel('createdAt')).toBe(USER_DETAILS_I18N.createdAt); - expect(findTimeFor('createdAt')).toBe(user.createdAt); + expect(findUserDetailLabel('created-at')).toBe(USER_DETAILS_I18N.createdAt); + expect(findTimeFor('created-at')).toBe(user.createdAt); }); }); @@ -67,32 +67,34 @@ describe('UserDetails', () => { describe('creditCard', () => { it('renders the correct label', () => { - expect(findUserDetailLabel('creditCard')).toBe(USER_DETAILS_I18N.creditCard); + expect(findUserDetailLabel('credit-card-verification')).toBe(USER_DETAILS_I18N.creditCard); }); it('renders the users name', () => { - expect(findUserDetail('creditCard').text()).toContain( + expect(findUserDetail('credit-card-verification').text()).toContain( sprintf(USER_DETAILS_I18N.registeredWith, { ...user.creditCard }), ); - expect(findUserDetail('creditCard').text()).toContain(user.creditCard.name); + expect(findUserDetail('credit-card-verification').text()).toContain(user.creditCard.name); }); describe('similar credit cards', () => { it('renders the number of similar records', () => { - expect(findUserDetail('creditCard').text()).toContain( + expect(findUserDetail('credit-card-verification').text()).toContain( sprintf('Card matches %{similarRecordsCount} accounts', { ...user.creditCard }), ); }); it('renders a link to the matching cards', () => { - expect(findLinkFor('creditCard').attributes('href')).toBe(user.creditCard.cardMatchesLink); + expect(findLinkFor('credit-card-verification').attributes('href')).toBe( + user.creditCard.cardMatchesLink, + ); - expect(findLinkFor('creditCard').text()).toBe( + expect(findLinkFor('credit-card-verification').text()).toBe( sprintf('%{similarRecordsCount} accounts', { ...user.creditCard }), ); - expect(findLinkFor('creditCard').text()).toContain( + expect(findLinkFor('credit-card-verification').text()).toContain( user.creditCard.similarRecordsCount.toString(), ); }); @@ -105,13 +107,13 @@ describe('UserDetails', () => { }); it('does not render the number of similar records', () => { - expect(findUserDetail('creditCard').text()).not.toContain( + expect(findUserDetail('credit-card-verification').text()).not.toContain( sprintf('Card matches %{similarRecordsCount} accounts', { ...user.creditCard }), ); }); it('does not render a link to the matching cards', () => { - expect(findLinkFor('creditCard').exists()).toBe(false); + expect(findLinkFor('credit-card-verification').exists()).toBe(false); }); }); }); @@ -124,55 +126,55 @@ describe('UserDetails', () => { }); it('does not render the users creditCard', () => { - expect(findUserDetail('creditCard').exists()).toBe(false); + expect(findUserDetail('credit-card-verification').exists()).toBe(false); }); }); }); describe('otherReports', () => { it('renders the correct label', () => { - expect(findUserDetailLabel('otherReports')).toBe(USER_DETAILS_I18N.otherReports); + expect(findUserDetailLabel('past-closed-reports')).toBe(USER_DETAILS_I18N.pastReports); }); - describe.each(user.otherReports)('renders a line for report %#', (otherReport) => { - const index = user.otherReports.indexOf(otherReport); + describe.each(user.pastClosedReports)('renders a line for report %#', (pastReport) => { + const index = user.pastClosedReports.indexOf(pastReport); it('renders the category', () => { - expect(findOtherReport(index).text()).toContain( - sprintf('Reported for %{category}', { ...otherReport }), + expect(findPastReport(index).text()).toContain( + sprintf('Reported for %{category}', { ...pastReport }), ); }); it('renders a link to the report', () => { - expect(findLinkIn(findOtherReport(index)).attributes('href')).toBe(otherReport.reportPath); + expect(findLinkIn(findPastReport(index)).attributes('href')).toBe(pastReport.reportPath); }); it('renders the time it was created', () => { - expect(findTimeIn(findOtherReport(index))).toBe(otherReport.createdAt); + expect(findTimeIn(findPastReport(index))).toBe(pastReport.createdAt); }); }); describe('when the users otherReports is empty', () => { beforeEach(() => { createComponent({ - user: { ...user, otherReports: [] }, + user: { ...user, pastClosedReports: [] }, }); }); it('does not render the users otherReports', () => { - expect(findUserDetail('otherReports').exists()).toBe(false); + expect(findUserDetail('past-closed-reports').exists()).toBe(false); }); }); }); describe('normalLocation', () => { it('renders the correct label', () => { - expect(findUserDetailLabel('normalLocation')).toBe(USER_DETAILS_I18N.normalLocation); + expect(findUserDetailLabel('normal-location')).toBe(USER_DETAILS_I18N.normalLocation); }); describe('when the users mostUsedIp is blank', () => { it('renders the users lastSignInIp', () => { - expect(findUserDetailValue('normalLocation')).toBe(user.lastSignInIp); + expect(findUserDetailValue('normal-location')).toBe(user.lastSignInIp); }); }); @@ -186,23 +188,25 @@ describe('UserDetails', () => { }); it('renders the users mostUsedIp', () => { - expect(findUserDetailValue('normalLocation')).toBe(mostUsedIp); + expect(findUserDetailValue('normal-location')).toBe(mostUsedIp); }); }); }); describe('lastSignInIp', () => { it('renders the users lastSignInIp with the correct label', () => { - expect(findUserDetailLabel('lastSignInIp')).toBe(USER_DETAILS_I18N.lastSignInIp); - expect(findUserDetailValue('lastSignInIp')).toBe(user.lastSignInIp); + expect(findUserDetailLabel('last-sign-in-ip')).toBe(USER_DETAILS_I18N.lastSignInIp); + expect(findUserDetailValue('last-sign-in-ip')).toBe(user.lastSignInIp); }); }); it.each(['snippets', 'groups', 'notes'])( 'renders the users %s with the correct label', (attribute) => { - expect(findUserDetailLabel(attribute)).toBe(USER_DETAILS_I18N[attribute]); - expect(findUserDetailValue(attribute)).toBe( + const testId = `user-${attribute}-count`; + + expect(findUserDetailLabel(testId)).toBe(USER_DETAILS_I18N[attribute]); + expect(findUserDetailValue(testId)).toBe( USER_DETAILS_I18N[`${attribute}Count`](user[`${attribute}Count`]), ); }, diff --git a/spec/frontend/admin/abuse_report/mock_data.js b/spec/frontend/admin/abuse_report/mock_data.js index 8ff0c7d507a..ee61eabfa66 100644 --- a/spec/frontend/admin/abuse_report/mock_data.js +++ b/spec/frontend/admin/abuse_report/mock_data.js @@ -15,7 +15,7 @@ export const mockAbuseReport = { similarRecordsCount: 2, cardMatchesLink: '/admin/users/spamuser417/card_match', }, - otherReports: [ + pastClosedReports: [ { category: 'offensive', createdAt: '2023-02-28T10:09:54.982Z', @@ -32,14 +32,27 @@ export const mockAbuseReport = { snippetsCount: 0, groupsCount: 0, notesCount: 6, - }, - reporter: { - username: 'reporter', - name: 'R Porter', - avatarUrl: 'https://www.gravatar.com/avatar/a2579caffc69ea5d7606f9dd9d8504ba?s=80&d=identicon', - path: '/reporter', + similarOpenReports: [ + { + status: 'open', + message: 'This is obvious spam', + reportedAt: '2023-03-29T09:39:50.502Z', + category: 'spam', + type: 'issue', + content: '', + screenshot: null, + reporter: { + username: 'reporter 2', + name: 'Another Reporter', + avatarUrl: 'https://www.gravatar.com/avatar/anotherreporter', + path: '/reporter-2', + }, + updatePath: '/admin/abuse_reports/28', + }, + ], }, report: { + globalId: 'gid://gitlab/AbuseReport/1', status: 'open', message: 'This is obvious spam', reportedAt: '2023-03-29T09:39:50.502Z', @@ -52,5 +65,66 @@ export const mockAbuseReport = { '/uploads/-/system/abuse_report/screenshot/27/Screenshot_2023-03-30_at_16.56.37.png', updatePath: '/admin/abuse_reports/27', moderateUserPath: '/admin/abuse_reports/27/moderate_user', + reporter: { + username: 'reporter', + name: 'R Porter', + avatarUrl: + 'https://www.gravatar.com/avatar/a2579caffc69ea5d7606f9dd9d8504ba?s=80&d=identicon', + path: '/reporter', + }, + }, +}; + +export const mockLabel1 = { + id: 'gid://gitlab/Admin::AbuseReportLabel/1', + title: 'Uno', + color: '#F0AD4E', + textColor: '#FFFFFF', + description: null, +}; + +export const mockLabel2 = { + id: 'gid://gitlab/Admin::AbuseReportLabel/2', + title: 'Dos', + color: '#F0AD4E', + textColor: '#FFFFFF', + description: null, +}; + +export const mockLabelsQueryResponse = { + data: { + labels: { + nodes: [mockLabel1, mockLabel2], + __typename: 'LabelConnection', + }, + }, +}; + +export const mockReportQueryResponse = { + data: { + abuseReport: { + labels: { + nodes: [mockLabel1], + __typename: 'LabelConnection', + }, + __typename: 'AbuseReport', + }, + }, +}; + +export const mockCreateLabelResponse = { + data: { + labelCreate: { + label: { + id: 'gid://gitlab/Admin::AbuseReportLabel/1', + color: '#ed9121', + description: null, + title: 'abuse report label', + textColor: '#FFFFFF', + __typename: 'Label', + }, + errors: [], + __typename: 'AbuseReportLabelCreatePayload', + }, }, }; |