Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-24 21:09:14 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-24 21:09:14 +0300
commitca386bfc0cf083e0ccb477995378061fc2a15b66 (patch)
treef53d4a2f288ba64e9f440080817f14d62965398c /spec/frontend/admin
parent61ebd5753018a1f4b6032122f6ea625dc4e4fc8e (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/admin')
-rw-r--r--spec/frontend/admin/abuse_report/components/abuse_report_app_spec.js39
-rw-r--r--spec/frontend/admin/abuse_report/components/report_actions_spec.js158
-rw-r--r--spec/frontend/admin/abuse_report/components/report_header_spec.js49
-rw-r--r--spec/frontend/admin/abuse_report/mock_data.js10
-rw-r--r--spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js202
5 files changed, 240 insertions, 218 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 cabbb5e1591..e519684bbc5 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,14 +1,17 @@
import { shallowMount } from '@vue/test-utils';
+import { GlAlert } from '@gitlab/ui';
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 ReportedContent from '~/admin/abuse_report/components/reported_content.vue';
import HistoryItems from '~/admin/abuse_report/components/history_items.vue';
+import { SUCCESS_ALERT } from '~/admin/abuse_report/constants';
import { mockAbuseReport } from '../mock_data';
describe('AbuseReportApp', () => {
let wrapper;
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findReportHeader = () => wrapper.findComponent(ReportHeader);
const findUserDetails = () => wrapper.findComponent(UserDetails);
const findReportedContent = () => wrapper.findComponent(ReportedContent);
@@ -27,10 +30,44 @@ describe('AbuseReportApp', () => {
createComponent();
});
+ it('does not show the alert by default', () => {
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ describe('when emitting the showAlert event from the report header', () => {
+ const message = 'alert message';
+
+ beforeEach(() => {
+ findReportHeader().vm.$emit('showAlert', SUCCESS_ALERT, message);
+ });
+
+ it('shows the alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ });
+
+ it('displays the message', () => {
+ expect(findAlert().text()).toBe(message);
+ });
+
+ it('sets the variant property', () => {
+ expect(findAlert().props('variant')).toBe(SUCCESS_ALERT);
+ });
+
+ describe('when dismissing the alert', () => {
+ beforeEach(() => {
+ findAlert().vm.$emit('dismiss');
+ });
+
+ it('hides the alert', () => {
+ expect(findAlert().exists()).toBe(false);
+ });
+ });
+ });
+
describe('ReportHeader', () => {
it('renders ReportHeader', () => {
expect(findReportHeader().props('user')).toBe(mockAbuseReport.user);
- expect(findReportHeader().props('actions')).toBe(mockAbuseReport.actions);
+ expect(findReportHeader().props('report')).toBe(mockAbuseReport.report);
});
describe('when no user is present', () => {
diff --git a/spec/frontend/admin/abuse_report/components/report_actions_spec.js b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
new file mode 100644
index 00000000000..a1a78902b58
--- /dev/null
+++ b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
@@ -0,0 +1,158 @@
+import MockAdapter from 'axios-mock-adapter';
+import { GlDrawer } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import axios from '~/lib/utils/axios_utils';
+import {
+ HTTP_STATUS_OK,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+ HTTP_STATUS_INTERNAL_SERVER_ERROR,
+} from '~/lib/utils/http_status';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import ReportActions from '~/admin/abuse_report/components/report_actions.vue';
+import {
+ ACTIONS_I18N,
+ SUCCESS_ALERT,
+ FAILED_ALERT,
+ ERROR_MESSAGE,
+} from '~/admin/abuse_report/constants';
+import { mockAbuseReport } from '../mock_data';
+
+describe('ReportActions', () => {
+ let wrapper;
+ let axiosMock;
+
+ const params = {
+ user_action: 'ban_user',
+ close: true,
+ comment: 'my comment',
+ reason: 'spam',
+ };
+
+ const { report } = mockAbuseReport;
+
+ const clickActionsButton = () => wrapper.findByTestId('actions-button').vm.$emit('click');
+ const isDrawerOpen = () => wrapper.findComponent(GlDrawer).props('open');
+ const findErrorFor = (id) => wrapper.findByTestId(id).find('.d-block.invalid-feedback');
+ const setCloseReport = (close) => wrapper.findByTestId('close').find('input').setChecked(close);
+ const setSelectOption = (id, value) =>
+ wrapper.findByTestId(`${id}-select`).find(`option[value=${value}]`).setSelected();
+ const selectAction = (action) => setSelectOption('action', action);
+ const selectReason = (reason) => setSelectOption('reason', reason);
+ const setComment = (comment) => wrapper.findByTestId('comment').find('input').setValue(comment);
+ const submitForm = () => wrapper.findByTestId('submit-button').vm.$emit('click');
+
+ const createComponent = (props = {}) => {
+ wrapper = mountExtended(ReportActions, {
+ propsData: {
+ report,
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ axiosMock = new MockAdapter(axios);
+ createComponent();
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
+ });
+
+ it('initially hides the drawer', () => {
+ expect(isDrawerOpen()).toBe(false);
+ });
+
+ describe('when clicking the actions button', () => {
+ beforeEach(() => {
+ clickActionsButton();
+ });
+
+ it('shows the drawer', () => {
+ expect(isDrawerOpen()).toBe(true);
+ });
+
+ describe.each`
+ input | errorFor | messageShown
+ ${null} | ${'action'} | ${true}
+ ${null} | ${'reason'} | ${true}
+ ${'close'} | ${'action'} | ${false}
+ ${'action'} | ${'action'} | ${false}
+ ${'reason'} | ${'reason'} | ${false}
+ `('when submitting an invalid form', ({ input, errorFor, messageShown }) => {
+ describe(`when ${
+ input ? `providing a value for the ${input} field` : 'not providing any values'
+ }`, () => {
+ beforeEach(() => {
+ submitForm();
+
+ if (input === 'close') {
+ setCloseReport(params.close);
+ } else if (input === 'action') {
+ selectAction(params.user_action);
+ } else if (input === 'reason') {
+ selectReason(params.reason);
+ }
+ });
+
+ it(`${messageShown ? 'shows' : 'hides'} ${errorFor} error message`, () => {
+ if (messageShown) {
+ expect(findErrorFor(errorFor).text()).toBe(ACTIONS_I18N.requiredFieldFeedback);
+ } else {
+ expect(findErrorFor(errorFor).exists()).toBe(false);
+ }
+ });
+ });
+ });
+
+ describe('when submitting a valid form', () => {
+ describe.each`
+ response | success | responseStatus | responseData | alertType | alertMessage
+ ${'successful'} | ${true} | ${HTTP_STATUS_OK} | ${{ message: 'success!' }} | ${SUCCESS_ALERT} | ${'success!'}
+ ${'custom failure'} | ${false} | ${HTTP_STATUS_UNPROCESSABLE_ENTITY} | ${{ message: 'fail!' }} | ${FAILED_ALERT} | ${'fail!'}
+ ${'generic failure'} | ${false} | ${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${{}} | ${FAILED_ALERT} | ${ERROR_MESSAGE}
+ `(
+ 'when the server responds with a $response response',
+ ({ success, responseStatus, responseData, alertType, alertMessage }) => {
+ beforeEach(async () => {
+ jest.spyOn(axios, 'put');
+
+ axiosMock.onPut(report.updatePath).replyOnce(responseStatus, responseData);
+
+ selectAction(params.user_action);
+ setCloseReport(params.close);
+ selectReason(params.reason);
+ setComment(params.comment);
+
+ await nextTick();
+
+ submitForm();
+
+ await waitForPromises();
+ });
+
+ it('does a put call with the right data', () => {
+ expect(axios.put).toHaveBeenCalledWith(report.updatePath, params);
+ });
+
+ it('closes the drawer', () => {
+ expect(isDrawerOpen()).toBe(false);
+ });
+
+ it('emits the showAlert event', () => {
+ expect(wrapper.emitted('showAlert')).toStrictEqual([[alertType, alertMessage]]);
+ });
+
+ it(`${success ? 'does' : 'does not'} emit the closeReport event`, () => {
+ if (success) {
+ expect(wrapper.emitted('closeReport')).toBeDefined();
+ } else {
+ expect(wrapper.emitted('closeReport')).toBeUndefined();
+ }
+ });
+ },
+ );
+ });
+ });
+});
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 d584cab05b3..f22f3af091f 100644
--- a/spec/frontend/admin/abuse_report/components/report_header_spec.js
+++ b/spec/frontend/admin/abuse_report/components/report_header_spec.js
@@ -1,25 +1,27 @@
-import { GlAvatar, GlLink, GlButton } from '@gitlab/ui';
+import { GlBadge, GlIcon, GlAvatar, GlLink, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import ReportHeader from '~/admin/abuse_report/components/report_header.vue';
-import AbuseReportActions from '~/admin/abuse_reports/components/abuse_report_actions.vue';
-import { REPORT_HEADER_I18N } from '~/admin/abuse_report/constants';
+import ReportActions from '~/admin/abuse_report/components/report_actions.vue';
+import { REPORT_HEADER_I18N, STATUS_OPEN, STATUS_CLOSED } from '~/admin/abuse_report/constants';
import { mockAbuseReport } from '../mock_data';
describe('ReportHeader', () => {
let wrapper;
- const { user, actions } = mockAbuseReport;
+ const { user, report } = mockAbuseReport;
+ const findBadge = () => wrapper.findComponent(GlBadge);
+ const findIcon = () => wrapper.findComponent(GlIcon);
const findAvatar = () => wrapper.findComponent(GlAvatar);
const findLink = () => wrapper.findComponent(GlLink);
const findButton = () => wrapper.findComponent(GlButton);
- const findActions = () => wrapper.findComponent(AbuseReportActions);
+ const findActions = () => wrapper.findComponent(ReportActions);
const createComponent = (props = {}) => {
wrapper = shallowMount(ReportHeader, {
propsData: {
user,
- actions,
+ report,
...props,
},
});
@@ -51,9 +53,42 @@ describe('ReportHeader', () => {
expect(button.text()).toBe(REPORT_HEADER_I18N.adminProfile);
});
+ 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);
+ });
+ },
+ );
+
it('renders the actions', () => {
const actionsComponent = findActions();
- expect(actionsComponent.props('report')).toMatchObject(actions);
+ expect(actionsComponent.props('report')).toMatchObject(report);
});
});
diff --git a/spec/frontend/admin/abuse_report/mock_data.js b/spec/frontend/admin/abuse_report/mock_data.js
index ee0f0967735..8c0ae223c87 100644
--- a/spec/frontend/admin/abuse_report/mock_data.js
+++ b/spec/frontend/admin/abuse_report/mock_data.js
@@ -40,6 +40,7 @@ export const mockAbuseReport = {
path: '/reporter',
},
report: {
+ status: 'open',
message: 'This is obvious spam',
reportedAt: '2023-03-29T09:39:50.502Z',
category: 'spam',
@@ -49,13 +50,6 @@ export const mockAbuseReport = {
url: 'http://localhost:3000/spamuser417/project/-/merge_requests/1#note_1375',
screenshot:
'/uploads/-/system/abuse_report/screenshot/27/Screenshot_2023-03-30_at_16.56.37.png',
- },
- actions: {
- reportedUser: { name: 'Sp4m User', createdAt: '2023-03-29T09:30:23.885Z' },
- userBlocked: false,
- blockUserPath: '/admin/users/spamuser417/block',
- removeReportPath: '/admin/abuse_reports/27',
- removeUserAndReportPath: '/admin/abuse_reports/27?remove_user=true',
- redirectPath: '/admin/abuse_reports',
+ updatePath: '/admin/abuse_reports/27',
},
};
diff --git a/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js b/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js
deleted file mode 100644
index bc648e52fad..00000000000
--- a/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js
+++ /dev/null
@@ -1,202 +0,0 @@
-import { nextTick } from 'vue';
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import { GlDisclosureDropdown, GlDisclosureDropdownItem, GlModal } from '@gitlab/ui';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import AbuseReportActions from '~/admin/abuse_reports/components/abuse_report_actions.vue';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import { redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
-import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { sprintf } from '~/locale';
-import { ACTIONS_I18N } from '~/admin/abuse_reports/constants';
-import { mockAbuseReports } from '../mock_data';
-
-jest.mock('~/alert');
-jest.mock('~/lib/utils/url_utility');
-
-describe('AbuseReportActions', () => {
- let wrapper;
-
- const findRemoveUserAndReportButton = () => wrapper.findByText('Remove user & report');
- const findBlockUserButton = () => wrapper.findByTestId('block-user-button');
- const findRemoveReportButton = () => wrapper.findByText('Remove report');
- const findConfirmationModal = () => wrapper.findComponent(GlModal);
-
- const report = mockAbuseReports[0];
-
- const createComponent = (props = {}) => {
- wrapper = mountExtended(AbuseReportActions, {
- propsData: {
- report,
- ...props,
- },
- stubs: {
- GlDisclosureDropdown,
- GlDisclosureDropdownItem,
- },
- });
- };
-
- describe('default', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('displays "Block user", "Remove user & report", and "Remove report" buttons', () => {
- expect(findRemoveUserAndReportButton().text()).toBe(ACTIONS_I18N.removeUserAndReport);
-
- const blockButton = findBlockUserButton();
- expect(blockButton.text()).toBe(ACTIONS_I18N.blockUser);
- expect(blockButton.attributes('disabled')).toBeUndefined();
-
- expect(findRemoveReportButton().text()).toBe(ACTIONS_I18N.removeReport);
- });
-
- it('does not show the confirmation modal initially', () => {
- expect(findConfirmationModal().props('visible')).toBe(false);
- });
- });
-
- describe('block button when user is already blocked', () => {
- it('is disabled and has the correct text', () => {
- createComponent({ report: { ...report, userBlocked: true } });
-
- const button = findBlockUserButton();
- expect(button.text()).toBe(ACTIONS_I18N.alreadyBlocked);
- expect(button.attributes('disabled')).toBeDefined();
- });
- });
-
- describe('actions', () => {
- let axiosMock;
-
- beforeEach(() => {
- axiosMock = new MockAdapter(axios);
-
- createComponent();
- });
-
- afterEach(() => {
- axiosMock.restore();
- createAlert.mockClear();
- });
-
- describe('on remove user and report', () => {
- it('shows confirmation modal and reloads the page on success', async () => {
- findRemoveUserAndReportButton().trigger('click');
- await nextTick();
-
- expect(findConfirmationModal().props()).toMatchObject({
- visible: true,
- title: sprintf(ACTIONS_I18N.removeUserAndReportConfirm, {
- user: report.reportedUser.name,
- }),
- });
-
- axiosMock.onDelete(report.removeUserAndReportPath).reply(HTTP_STATUS_OK);
-
- findConfirmationModal().vm.$emit('primary');
- await axios.waitForAll();
-
- expect(refreshCurrentPage).toHaveBeenCalled();
- });
-
- describe('when a redirect path is present', () => {
- beforeEach(() => {
- createComponent({ report: { ...report, redirectPath: '/redirect_path' } });
- });
-
- it('redirects to the given path', async () => {
- findRemoveUserAndReportButton().trigger('click');
- await nextTick();
-
- axiosMock.onDelete(report.removeUserAndReportPath).reply(HTTP_STATUS_OK);
-
- findConfirmationModal().vm.$emit('primary');
- await axios.waitForAll();
-
- expect(redirectTo).toHaveBeenCalledWith('/redirect_path'); // eslint-disable-line import/no-deprecated
- });
- });
- });
-
- describe('on block user', () => {
- beforeEach(async () => {
- findBlockUserButton().trigger('click');
- await nextTick();
- });
-
- it('shows confirmation modal', () => {
- expect(findConfirmationModal().props()).toMatchObject({
- visible: true,
- title: ACTIONS_I18N.blockUserConfirm,
- });
- });
-
- describe.each([
- {
- responseData: { notice: 'Notice' },
- createAlertArgs: { message: 'Notice', variant: VARIANT_SUCCESS },
- blockButtonText: ACTIONS_I18N.alreadyBlocked,
- blockButtonDisabled: 'disabled',
- },
- {
- responseData: { error: 'Error' },
- createAlertArgs: { message: 'Error' },
- blockButtonText: ACTIONS_I18N.blockUser,
- blockButtonDisabled: undefined,
- },
- ])(
- 'when response JSON is $responseData',
- ({ responseData, createAlertArgs, blockButtonText, blockButtonDisabled }) => {
- beforeEach(async () => {
- axiosMock.onPut(report.blockUserPath).reply(HTTP_STATUS_OK, responseData);
-
- findConfirmationModal().vm.$emit('primary');
- await axios.waitForAll();
- });
-
- it('updates the block button correctly', () => {
- const button = findBlockUserButton();
- expect(button.text()).toBe(blockButtonText);
- expect(button.attributes('disabled')).toBe(blockButtonDisabled);
- });
-
- it('displays the returned message', () => {
- expect(createAlert).toHaveBeenCalledWith(createAlertArgs);
- });
- },
- );
- });
-
- describe('on remove report', () => {
- it('reloads the page on success', async () => {
- axiosMock.onDelete(report.removeReportPath).reply(HTTP_STATUS_OK);
-
- findRemoveReportButton().trigger('click');
-
- expect(findConfirmationModal().props('visible')).toBe(false);
-
- await axios.waitForAll();
-
- expect(refreshCurrentPage).toHaveBeenCalled();
- });
-
- describe('when a redirect path is present', () => {
- beforeEach(() => {
- createComponent({ report: { ...report, redirectPath: '/redirect_path' } });
- });
-
- it('redirects to the given path', async () => {
- axiosMock.onDelete(report.removeReportPath).reply(HTTP_STATUS_OK);
-
- findRemoveReportButton().trigger('click');
-
- await axios.waitForAll();
-
- expect(redirectTo).toHaveBeenCalledWith('/redirect_path'); // eslint-disable-line import/no-deprecated
- });
- });
- });
- });
-});