From a016ebc4f7dfe4651d59bc39e7a8a77cf2dad30c Mon Sep 17 00:00:00 2001 From: Hinam Mehra <hmehra@gitlab.com> Date: Mon, 4 Sep 2023 07:38:57 +0000 Subject: [PATCH] Display activity for similar open abuse reports when aggregated - When an abuse report is aggregated, in it's detail view, display the activity for the aggregated reports as well Changelog: added --- .../components/abuse_report_app.vue | 24 ++++++-- .../components/activity_events_list.vue | 19 ++++++ .../components/activity_history_item.vue | 42 +++++++++++++ .../abuse_report/components/history_items.vue | 49 --------------- .../components/abuse_report_app_spec.js | 59 +++++++++++++------ .../components/activity_events_list_spec.js | 30 ++++++++++ ..._spec.js => activity_history_item_spec.js} | 13 ++-- 7 files changed, 158 insertions(+), 78 deletions(-) create mode 100644 app/assets/javascripts/admin/abuse_report/components/activity_events_list.vue create mode 100644 app/assets/javascripts/admin/abuse_report/components/activity_history_item.vue delete mode 100644 app/assets/javascripts/admin/abuse_report/components/history_items.vue create mode 100644 spec/frontend/admin/abuse_report/components/activity_events_list_spec.js rename spec/frontend/admin/abuse_report/components/{history_items_spec.js => activity_history_item_spec.js} (79%) diff --git a/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue b/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue index 3aa1289261d03..3c46de7c2be22 100644 --- a/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue +++ b/app/assets/javascripts/admin/abuse_report/components/abuse_report_app.vue @@ -5,7 +5,8 @@ import ReportHeader from './report_header.vue'; import UserDetails from './user_details.vue'; import ReportDetails from './report_details.vue'; import ReportedContent from './reported_content.vue'; -import HistoryItems from './history_items.vue'; +import ActivityEventsList from './activity_events_list.vue'; +import ActivityHistoryItem from './activity_history_item.vue'; const alertDefaults = { visible: false, @@ -21,7 +22,8 @@ export default { UserDetails, ReportDetails, ReportedContent, - HistoryItems, + ActivityEventsList, + ActivityHistoryItem, }, mixins: [glFeatureFlagsMixin()], props: { @@ -75,10 +77,24 @@ export default { <reported-content :report="abuseReport.report" data-testid="reported-content" /> - <div v-for="report in similarOpenReports" :key="report.id" data-testid="similar-open-reports"> + <div + v-for="report in similarOpenReports" + :key="report.id" + data-testid="reported-content-similar-open-reports" + > <reported-content :report="report" /> </div> - <history-items :report="abuseReport.report" /> + <activity-events-list> + <template #history-items> + <activity-history-item :report="abuseReport.report" data-testid="activity" /> + <activity-history-item + v-for="report in similarOpenReports" + :key="report.id" + :report="report" + data-testid="activity-similar-open-reports" + /> + </template> + </activity-events-list> </section> </template> diff --git a/app/assets/javascripts/admin/abuse_report/components/activity_events_list.vue b/app/assets/javascripts/admin/abuse_report/components/activity_events_list.vue new file mode 100644 index 0000000000000..8c4c1da28b845 --- /dev/null +++ b/app/assets/javascripts/admin/abuse_report/components/activity_events_list.vue @@ -0,0 +1,19 @@ +<script> +import { HISTORY_ITEMS_I18N } from '../constants'; + +export default { + name: 'ActivityEventsList', + i18n: HISTORY_ITEMS_I18N, +}; +</script> + +<template> + <!-- The styles `issuable-discussion`, `timeline`, `main-notes-list` and `notes` used below + are declared in app/assets/stylesheets/pages/notes.scss --> + <section class="gl-pt-6 issuable-discussion"> + <h2 class="gl-font-lg gl-mt-0 gl-mb-2">{{ $options.i18n.activity }}</h2> + <ul class="timeline main-notes-list notes"> + <slot name="history-items"></slot> + </ul> + </section> +</template> diff --git a/app/assets/javascripts/admin/abuse_report/components/activity_history_item.vue b/app/assets/javascripts/admin/abuse_report/components/activity_history_item.vue new file mode 100644 index 0000000000000..5962203c382ae --- /dev/null +++ b/app/assets/javascripts/admin/abuse_report/components/activity_history_item.vue @@ -0,0 +1,42 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import HistoryItem from '~/vue_shared/components/registry/history_item.vue'; +import { HISTORY_ITEMS_I18N } from '../constants'; + +export default { + name: 'ActivityHistoryItem', + components: { + GlSprintf, + TimeAgoTooltip, + HistoryItem, + }, + props: { + report: { + type: Object, + required: true, + }, + }, + computed: { + reporter() { + return this.report.reporter; + }, + reporterName() { + return this.reporter?.name || this.$options.i18n.deletedReporter; + }, + }, + i18n: HISTORY_ITEMS_I18N, +}; +</script> + +<template> + <history-item icon="warning"> + <div class="gl-display-flex gl-xs-flex-direction-column"> + <gl-sprintf :message="$options.i18n.reportedByForCategory"> + <template #name>{{ reporterName }}</template> + <template #category>{{ report.category }}</template> + </gl-sprintf> + <time-ago-tooltip :time="report.reportedAt" class="gl-text-secondary gl-sm-ml-3" /> + </div> + </history-item> +</template> diff --git a/app/assets/javascripts/admin/abuse_report/components/history_items.vue b/app/assets/javascripts/admin/abuse_report/components/history_items.vue deleted file mode 100644 index 619a8bcfe9281..0000000000000 --- a/app/assets/javascripts/admin/abuse_report/components/history_items.vue +++ /dev/null @@ -1,49 +0,0 @@ -<script> -import { GlSprintf } from '@gitlab/ui'; -import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import HistoryItem from '~/vue_shared/components/registry/history_item.vue'; -import { HISTORY_ITEMS_I18N } from '../constants'; - -export default { - name: 'HistoryItems', - components: { - GlSprintf, - TimeAgoTooltip, - HistoryItem, - }, - props: { - report: { - type: Object, - required: true, - }, - }, - computed: { - reporter() { - return this.report.reporter; - }, - reporterName() { - return this.reporter?.name || this.$options.i18n.deletedReporter; - }, - }, - i18n: HISTORY_ITEMS_I18N, -}; -</script> - -<template> - <!-- The styles `issuable-discussion`, `timeline`, `main-notes-list` and `notes` used below - are declared in app/assets/stylesheets/pages/notes.scss --> - <section class="gl-pt-6 issuable-discussion"> - <h2 class="gl-font-lg gl-mt-0 gl-mb-2">{{ $options.i18n.activity }}</h2> - <ul class="timeline main-notes-list notes"> - <history-item icon="warning"> - <div class="gl-display-flex gl-xs-flex-direction-column"> - <gl-sprintf :message="$options.i18n.reportedByForCategory"> - <template #name>{{ reporterName }}</template> - <template #category>{{ report.category }}</template> - </gl-sprintf> - <time-ago-tooltip :time="report.reportedAt" class="gl-text-secondary gl-sm-ml-3" /> - </div> - </history-item> - </ul> - </section> -</template> 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 d704a91b93d57..4340699a7ed32 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 @@ -5,21 +5,33 @@ 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.findByTestId('reported-content'); - const findSimilarOpenReports = () => wrapper.findAllByTestId('similar-open-reports'); - const findSimilarReportedContent = () => - findSimilarOpenReports().at(0).findComponent(ReportedContent); - const findHistoryItems = () => wrapper.findComponent(HistoryItems); + 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 = {}) => { @@ -70,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); @@ -89,7 +101,7 @@ describe('AbuseReportApp', () => { }); }); - describe('UserDetails', () => { + describe('User Details', () => { it('renders UserDetails', () => { expect(findUserDetails().props('user')).toBe(mockAbuseReport.user); }); @@ -107,6 +119,17 @@ describe('AbuseReportApp', () => { }); }); + 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]); + }); + }); + describe('ReportDetails', () => { describe('when abuseReportLabels feature flag is enabled', () => { it('renders ReportDetails', () => { @@ -125,18 +148,18 @@ describe('AbuseReportApp', () => { }); }); - it('renders ReportedContent', () => { - expect(findReportedContent().props('report')).toBe(mockAbuseReport.report); - }); - - it('renders similar abuse reports', () => { - const { similarOpenReports } = mockAbuseReport.user; + describe('Activity', () => { + it('renders the activity events list', () => { + expect(findActivityList().exists()).toBe(true); + }); - expect(findSimilarOpenReports()).toHaveLength(similarOpenReports.length); - expect(findSimilarReportedContent().props('report')).toBe(similarOpenReports[0]); - }); + it('renders activity item for abuse report', () => { + expect(findActivityItem().props('report')).toBe(mockAbuseReport.report); + }); - it('renders HistoryItems', () => { - expect(findHistoryItems().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 0000000000000..cd1120d2db40a --- /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 similarity index 79% rename from spec/frontend/admin/abuse_report/components/history_items_spec.js rename to spec/frontend/admin/abuse_report/components/activity_history_item_spec.js index e88888100959e..3f430b0143e10 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,13 +1,12 @@ 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 } = mockAbuseReport; @@ -16,7 +15,7 @@ describe('HistoryItems', () => { const findTimeAgo = () => wrapper.findComponent(TimeAgoTooltip); const createComponent = (props = {}) => { - wrapper = shallowMount(HistoryItems, { + wrapper = shallowMount(AcitivityHistoryItem, { propsData: { report, ...props, @@ -37,7 +36,7 @@ describe('HistoryItems', () => { describe('rendering the title', () => { it('renders the reporters name and the category', () => { - const title = sprintf(HISTORY_ITEMS_I18N.reportedByForCategory, { + const title = sprintf('Reported by %{name} for %{category}.', { name: report.reporter.name, category: report.category, }); @@ -50,8 +49,8 @@ describe('HistoryItems', () => { }); 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); -- GitLab