Commit 24575749 authored by Robert Hunt's avatar Robert Hunt

Replace compliance dashboard with new compliance violations report

Change the compliance dashboard to use the new compliance violations.
The violations provide a more granular report that also now allows us
to see every violation for every merged merge request in the group.
Not just the last merged merge request.

Changelog: changed
EE: true
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83959
parent 76b7809e
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
links: { links: {
groupSettingsDocsPath: helpPagePath('user/project/merge_requests/approvals/index.md'), groupSettingsDocsPath: helpPagePath('user/project/merge_requests/approvals/index.md'),
separationOfDutiesDocsPath: helpPagePath('user/compliance/compliance_report/index', { separationOfDutiesDocsPath: helpPagePath('user/compliance/compliance_report/index', {
anchor: 'approval-status-and-separation-of-duties', anchor: 'separation-of-duties',
}), }),
}, },
i18n: { i18n: {
......
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import ComplianceDashboard from './components/dashboard.vue';
import ComplianceReport from './components/report.vue'; import ComplianceReport from './components/report.vue';
import { buildDefaultFilterParams } from './utils'; import { buildDefaultFilterParams } from './utils';
export default () => { export default () => {
const el = document.getElementById('js-compliance-report'); const el = document.getElementById('js-compliance-report');
const { const { mergeCommitsCsvExportPath, groupPath } = el.dataset;
mergeRequests,
emptyStateSvgPath,
isLastPage,
mergeCommitsCsvExportPath,
groupPath,
} = el.dataset;
if (gon.features.complianceViolationsReport) { Vue.use(VueApollo);
Vue.use(VueApollo);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(),
}); });
const defaultFilterParams = buildDefaultFilterParams(window.location.search);
return new Vue({ const defaultFilterParams = buildDefaultFilterParams(window.location.search);
el,
apolloProvider,
render: (createElement) =>
createElement(ComplianceReport, {
props: {
mergeCommitsCsvExportPath,
groupPath,
defaultFilterParams,
},
}),
});
}
return new Vue({ return new Vue({
el, el,
apolloProvider,
render: (createElement) => render: (createElement) =>
createElement(ComplianceDashboard, { createElement(ComplianceReport, {
props: { props: {
mergeRequests: JSON.parse(mergeRequests),
isLastPage: parseBoolean(isLastPage),
emptyStateSvgPath,
mergeCommitsCsvExportPath, mergeCommitsCsvExportPath,
groupPath,
defaultFilterParams,
}, },
}), }),
}); });
......
...@@ -8,7 +8,7 @@ import { mapDashboardToDrawerData } from '../utils'; ...@@ -8,7 +8,7 @@ import { mapDashboardToDrawerData } from '../utils';
import MergeRequestDrawer from './drawer.vue'; import MergeRequestDrawer from './drawer.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import MergeRequestsGrid from './merge_requests/grid.vue'; import MergeRequestsGrid from './merge_requests/grid.vue';
import MergeCommitsExportButton from './merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from './shared/merge_commits_export_button.vue';
export default { export default {
name: 'ComplianceDashboard', name: 'ComplianceDashboard',
......
<script> <script>
import { GlLink, GlTooltipDirective } from '@gitlab/ui'; import { GlLink, GlTooltipDirective } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
...@@ -40,13 +40,14 @@ export default { ...@@ -40,13 +40,14 @@ export default {
warning: s__('ApprovalStatusTooltip|At least one rule does not adhere to separation of duties'), warning: s__('ApprovalStatusTooltip|At least one rule does not adhere to separation of duties'),
failed: s__('ApprovalStatusTooltip|Fails to adhere to separation of duties'), failed: s__('ApprovalStatusTooltip|Fails to adhere to separation of duties'),
}, },
docLink: helpPagePath('user/compliance/compliance_report/index', {
anchor: 'separation-of-duties',
}),
}; };
</script> </script>
<template> <template>
<gl-link <gl-link :href="$options.docLink">
href="https://docs.gitlab.com/ee/user/compliance/compliance_report/#approval-status-and-separation-of-duties"
>
<ci-icon v-gl-tooltip.left="tooltip" class="gl-display-flex" :status="{ icon, group }" /> <ci-icon v-gl-tooltip.left="tooltip" class="gl-display-flex" :status="{ icon, group }" />
</gl-link> </gl-link>
</template> </template>
...@@ -12,7 +12,7 @@ import getComplianceViolationsQuery from '../graphql/compliance_violations.query ...@@ -12,7 +12,7 @@ import getComplianceViolationsQuery from '../graphql/compliance_violations.query
import { mapViolations } from '../graphql/mappers'; import { mapViolations } from '../graphql/mappers';
import { DEFAULT_SORT, GRAPHQL_PAGE_SIZE, DEFAULT_PAGINATION_CURSORS } from '../constants'; import { DEFAULT_SORT, GRAPHQL_PAGE_SIZE, DEFAULT_PAGINATION_CURSORS } from '../constants';
import { parseViolationsQueryFilter } from '../utils'; import { parseViolationsQueryFilter } from '../utils';
import MergeCommitsExportButton from './merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from './shared/merge_commits_export_button.vue';
import MergeRequestDrawer from './drawer.vue'; import MergeRequestDrawer from './drawer.vue';
import ViolationReason from './violations/reason.vue'; import ViolationReason from './violations/reason.vue';
import ViolationFilter from './violations/filter.vue'; import ViolationFilter from './violations/filter.vue';
...@@ -209,9 +209,7 @@ export default { ...@@ -209,9 +209,7 @@ export default {
next: __('Next'), next: __('Next'),
viewDetailsBtn: __('View details'), viewDetailsBtn: __('View details'),
}, },
documentationPath: helpPagePath('user/compliance/compliance_report/index.md', { documentationPath: helpPagePath('user/compliance/compliance_report/index.md'),
anchor: 'approval-status-and-separation-of-duties',
}),
DRAWER_Z_INDEX, DRAWER_Z_INDEX,
}; };
</script> </script>
......
...@@ -6,9 +6,6 @@ class Groups::Security::ComplianceDashboardsController < Groups::ApplicationCont ...@@ -6,9 +6,6 @@ class Groups::Security::ComplianceDashboardsController < Groups::ApplicationCont
layout 'group' layout 'group'
before_action :authorize_compliance_dashboard! before_action :authorize_compliance_dashboard!
before_action do
push_frontend_feature_flag(:compliance_violations_report, @group, type: :development, default_enabled: :yaml)
end
track_redis_hll_event :show, name: 'g_compliance_dashboard' track_redis_hll_event :show, name: 'g_compliance_dashboard'
......
...@@ -4,7 +4,7 @@ module Enums ...@@ -4,7 +4,7 @@ module Enums
module MergeRequests module MergeRequests
module ComplianceViolation module ComplianceViolation
# Reasons are defined by GitLab in our public documentation. # Reasons are defined by GitLab in our public documentation.
# https://docs.gitlab.com/ee/user/compliance/compliance_dashboard/#approval-status-and-separation-of-duties # https://docs.gitlab.com/ee/user/compliance/compliance_dashboard/#separation-of-duties
def self.reasons def self.reasons
{ {
::Gitlab::ComplianceManagement::Violations::ApprovedByMergeRequestAuthor::REASON => 0, ::Gitlab::ComplianceManagement::Violations::ApprovedByMergeRequestAuthor::REASON => 0,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' } %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand') = expanded_by_default? ? _('Collapse') : _('Expand')
%p %p
- duties_link_url = help_page_path('user/compliance/compliance_report/index', anchor: 'approval-status-and-separation-of-duties') - duties_link_url = help_page_path('user/compliance/compliance_report/index', anchor: 'separation-of-duties')
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: duties_link_url } - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: duties_link_url }
= s_('MergeRequestApprovals|Enforce %{link_start}separation of duties%{link_end} for all projects.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } = s_('MergeRequestApprovals|Enforce %{link_start}separation of duties%{link_end} for all projects.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= link_to _("Learn more."), help_page_path("user/project/merge_requests/approvals/index.md"), target: '_blank', rel: 'noopener noreferrer' = link_to _("Learn more."), help_page_path("user/project/merge_requests/approvals/index.md"), target: '_blank', rel: 'noopener noreferrer'
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge request approvals') %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge request approvals')
%button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _("Collapse") : _("Expand") %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _("Collapse") : _("Expand")
%p %p
- duties_link_url = help_page_path('user/compliance/compliance_report/index', anchor: 'approval-status-and-separation-of-duties') - duties_link_url = help_page_path('user/compliance/compliance_report/index', anchor: 'separation-of-duties')
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: duties_link_url } - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: duties_link_url }
= s_('MergeRequestApprovals|Define approval rules and settings to ensure %{link_start}separation of duties%{link_end} for new merge requests.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } = s_('MergeRequestApprovals|Define approval rules and settings to ensure %{link_start}separation of duties%{link_end} for new merge requests.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= link_to _("Learn more."), help_page_path("user/project/merge_requests/approvals/index.md"), target: '_blank', rel: 'noopener noreferrer' = link_to _("Learn more."), help_page_path("user/project/merge_requests/approvals/index.md"), target: '_blank', rel: 'noopener noreferrer'
......
---
name: compliance_violations_report
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75015
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346266
milestone: '14.6'
type: development
group: group::compliance
default_enabled: false
...@@ -8,7 +8,7 @@ module Gitlab ...@@ -8,7 +8,7 @@ module Gitlab
SEVERITY_LEVEL = :high SEVERITY_LEVEL = :high
# The minimum number of approvers is defined by GitLab in our public documentation. # The minimum number of approvers is defined by GitLab in our public documentation.
# https://docs.gitlab.com/ee/user/compliance/compliance_dashboard/#approval-status-and-separation-of-duties # https://docs.gitlab.com/ee/user/compliance/compliance_dashboard/#separation-of-duties
MINIMUM_NUMBER_OF_APPROVERS = 2 MINIMUM_NUMBER_OF_APPROVERS = 2
def initialize(merge_request) def initialize(merge_request)
......
...@@ -36,109 +36,70 @@ RSpec.describe 'Compliance Dashboard', :js do ...@@ -36,109 +36,70 @@ RSpec.describe 'Compliance Dashboard', :js do
stub_licensed_features(group_level_compliance_dashboard: true) stub_licensed_features(group_level_compliance_dashboard: true)
group.add_owner(user) group.add_owner(user)
sign_in(user) sign_in(user)
visit group_security_compliance_dashboard_path(group)
end end
context 'when compliance_violations_report feature is disabled' do it 'shows the violations report table', :aggregate_failures do
before do page.within('table') do
stub_feature_flags(compliance_violations_report: false) expect(page).to have_content 'Severity'
visit group_security_compliance_dashboard_path(group) expect(page).to have_content 'Violation'
expect(page).to have_content 'Merge request'
expect(page).to have_content 'Date merged'
end end
end
context 'when there are no merge requests' do context 'when there are no compliance violations' do
it 'shows an empty state' do it 'shows an empty state' do
expect(page).to have_selector('.empty-state') expect(page).to have_content('No violations found')
end
end
context 'when there are merge requests' do
let_it_be(:merge_request) { create(:merge_request, source_project: project, state: :merged, merge_commit_sha: 'b71a6483b96dc303b66fdcaa212d9db6b10591ce') }
let_it_be(:merge_request_2) { create(:merge_request, source_project: project_2, state: :merged, merge_commit_sha: '24327319d067f4101cd3edd36d023ab5e49a8579') }
before_all do
create(:event, :merged, project: project, target: merge_request, author: user, created_at: 10.minutes.ago)
create(:event, :merged, project: project_2, target: merge_request_2, author: user, created_at: 15.minutes.ago)
end
it 'shows merge requests with details' do
expect(page).to have_link(merge_request.title)
expect(page).to have_content('merged 10 minutes ago')
expect(page).to have_content('no approvers')
end
context 'chain of custody report' do
it_behaves_like 'exports a merge commit-specific CSV'
end
end end
end end
context 'when compliance_violations_report feature is enabled' do context 'when there are merge requests' do
before do let_it_be(:merge_request) { create(:merge_request, source_project: project, state: :merged, merge_commit_sha: 'b71a6483b96dc303b66fdcaa212d9db6b10591ce') }
stub_feature_flags(compliance_violations_report: true) let_it_be(:merge_request_2) { create(:merge_request, source_project: project_2, state: :merged, merge_commit_sha: '24327319d067f4101cd3edd36d023ab5e49a8579') }
visit group_security_compliance_dashboard_path(group)
end
it 'shows the violations report table', :aggregate_failures do context 'chain of custody report' do
page.within('table') do it_behaves_like 'exports a merge commit-specific CSV'
expect(page).to have_content 'Severity'
expect(page).to have_content 'Violation'
expect(page).to have_content 'Merge request'
expect(page).to have_content 'Date merged'
end
end end
context 'when there are no compliance violations' do context 'and there is a compliance violation' do
it 'shows an empty state' do let_it_be(:violation) { create(:compliance_violation, :approved_by_committer, severity_level: :high, merge_request: merge_request, violating_user: user) }
expect(page).to have_content('No violations found') let_it_be(:violation_2) { create(:compliance_violation, :approved_by_merge_request_author, severity_level: :medium, merge_request: merge_request_2, violating_user: user) }
end
end
context 'when there are merge requests' do
let_it_be(:merge_request) { create(:merge_request, source_project: project, state: :merged, merge_commit_sha: 'b71a6483b96dc303b66fdcaa212d9db6b10591ce') }
let_it_be(:merge_request_2) { create(:merge_request, source_project: project_2, state: :merged, merge_commit_sha: '24327319d067f4101cd3edd36d023ab5e49a8579') }
context 'chain of custody report' do before do
it_behaves_like 'exports a merge commit-specific CSV' merge_request.metrics.update!(merged_at: 1.day.ago)
merge_request_2.metrics.update!(merged_at: 7.days.ago)
wait_for_requests
end end
context 'and there is a compliance violation' do it 'shows the compliance violations with details', :aggregate_failures do
let_it_be(:violation) { create(:compliance_violation, :approved_by_committer, severity_level: :high, merge_request: merge_request, violating_user: user) } expect(all('tbody > tr').count).to eq(2)
let_it_be(:violation_2) { create(:compliance_violation, :approved_by_merge_request_author, severity_level: :medium, merge_request: merge_request_2, violating_user: user) }
before do expect(first_row).to have_content('High')
merge_request.metrics.update!(merged_at: 1.day.ago) expect(first_row).to have_content('Approved by committer')
merge_request_2.metrics.update!(merged_at: 7.days.ago) expect(first_row).to have_content(merge_request.title)
wait_for_requests expect(first_row).to have_content('1 day ago')
end end
it 'shows the compliance violations with details', :aggregate_failures do it 'can sort the violations by clicking on a column header' do
expect(all('tbody > tr').count).to eq(2) click_column_header 'Severity'
expect(first_row).to have_content('High') expect(first_row).to have_content(merge_request_2.title)
expect(first_row).to have_content('Approved by committer') end
expect(first_row).to have_content(merge_request.title)
expect(first_row).to have_content('1 day ago')
end
it 'can sort the violations by clicking on a column header' do context 'violations filter' do
click_column_header 'Severity' it 'can filter by date range' do
set_date_range(7.days.ago.to_date, 6.days.ago.to_date)
expect(first_row).to have_content(merge_request_2.title) expect(page).to have_content(merge_request_2.title)
expect(page).not_to have_content(merge_request.title)
end end
context 'violations filter' do it 'can filter by project id' do
it 'can filter by date range' do filter_by_project(merge_request_2.project)
set_date_range(7.days.ago.to_date, 6.days.ago.to_date)
expect(page).to have_content(merge_request_2.title)
expect(page).not_to have_content(merge_request.title)
end
it 'can filter by project id' do expect(page).to have_content(merge_request_2.title)
filter_by_project(merge_request_2.project) expect(page).not_to have_content(merge_request.title)
expect(page).to have_content(merge_request_2.title)
expect(page).not_to have_content(merge_request.title)
end
end end
end end
end end
......
...@@ -70,7 +70,7 @@ describe('EE Approvals Group Settings App', () => { ...@@ -70,7 +70,7 @@ describe('EE Approvals Group Settings App', () => {
it.each` it.each`
findComponent | text | href findComponent | text | href
${findDescriptionLink} | ${'separation of duties'} | ${'/help/user/compliance/compliance_report/index#approval-status-and-separation-of-duties'} ${findDescriptionLink} | ${'separation of duties'} | ${'/help/user/compliance/compliance_report/index#separation-of-duties'}
${findLearnMoreLink} | ${'Learn more.'} | ${'/help/user/project/merge_requests/approvals/index.md'} ${findLearnMoreLink} | ${'Learn more.'} | ${'/help/user/project/merge_requests/approvals/index.md'}
`('has the correct link for $text', ({ findComponent, text, href }) => { `('has the correct link for $text', ({ findComponent, text, href }) => {
createWrapper(); createWrapper();
......
...@@ -6,7 +6,7 @@ import { nextTick } from 'vue'; ...@@ -6,7 +6,7 @@ import { nextTick } from 'vue';
import ComplianceDashboard from 'ee/compliance_dashboard/components/dashboard.vue'; import ComplianceDashboard from 'ee/compliance_dashboard/components/dashboard.vue';
import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue'; import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue';
import MergeRequestGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue'; import MergeRequestGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue';
import MergeCommitsExportButton from 'ee/compliance_dashboard/components/merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from 'ee/compliance_dashboard/components/shared/merge_commits_export_button.vue';
import { COMPLIANCE_TAB_COOKIE_KEY } from 'ee/compliance_dashboard/constants'; import { COMPLIANCE_TAB_COOKIE_KEY } from 'ee/compliance_dashboard/constants';
import { mapDashboardToDrawerData } from 'ee/compliance_dashboard/utils'; import { mapDashboardToDrawerData } from 'ee/compliance_dashboard/utils';
import { createMergeRequests } from '../mock_data'; import { createMergeRequests } from '../mock_data';
......
...@@ -33,8 +33,8 @@ describe('ApprovalStatus component', () => { ...@@ -33,8 +33,8 @@ describe('ApprovalStatus component', () => {
}); });
it('links to the approval status', () => { it('links to the approval status', () => {
expect(findLink().attributes('href')).toEqual( expect(findLink().attributes('href')).toBe(
'https://docs.gitlab.com/ee/user/compliance/compliance_report/#approval-status-and-separation-of-duties', '/help/user/compliance/compliance_report/index#separation-of-duties',
); );
}); });
......
...@@ -6,7 +6,7 @@ import * as Sentry from '@sentry/browser'; ...@@ -6,7 +6,7 @@ import * as Sentry from '@sentry/browser';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ComplianceReport from 'ee/compliance_dashboard/components/report.vue'; import ComplianceReport from 'ee/compliance_dashboard/components/report.vue';
import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue'; import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue';
import MergeCommitsExportButton from 'ee/compliance_dashboard/components/merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from 'ee/compliance_dashboard/components/shared/merge_commits_export_button.vue';
import ViolationReason from 'ee/compliance_dashboard/components/violations/reason.vue'; import ViolationReason from 'ee/compliance_dashboard/components/violations/reason.vue';
import ViolationFilter from 'ee/compliance_dashboard/components/violations/filter.vue'; import ViolationFilter from 'ee/compliance_dashboard/components/violations/filter.vue';
import getComplianceViolationsQuery from 'ee/compliance_dashboard/graphql/compliance_violations.query.graphql'; import getComplianceViolationsQuery from 'ee/compliance_dashboard/graphql/compliance_violations.query.graphql';
...@@ -125,9 +125,7 @@ describe('ComplianceReport component', () => { ...@@ -125,9 +125,7 @@ describe('ComplianceReport component', () => {
'The compliance report shows the merge request violations merged in protected environments.', 'The compliance report shows the merge request violations merged in protected environments.',
); );
expect(helpLink.text()).toBe('Learn more.'); expect(helpLink.text()).toBe('Learn more.');
expect(helpLink.attributes('href')).toBe( expect(helpLink.attributes('href')).toBe('/help/user/compliance/compliance_report/index.md');
'/help/user/compliance/compliance_report/index.md#approval-status-and-separation-of-duties',
);
}); });
it('renders the merge commit export button', () => { it('renders the merge commit export button', () => {
......
import { GlFormInput, GlForm, GlFormGroup } from '@gitlab/ui'; import { GlFormInput, GlForm, GlFormGroup } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import MergeCommitsExportButton from 'ee/compliance_dashboard/components/merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from 'ee/compliance_dashboard/components/shared/merge_commits_export_button.vue';
import { INPUT_DEBOUNCE, CUSTODY_REPORT_PARAMETER } from 'ee/compliance_dashboard/constants'; import { INPUT_DEBOUNCE, CUSTODY_REPORT_PARAMETER } from 'ee/compliance_dashboard/constants';
const CSV_EXPORT_PATH = '/merge_commit_reports'; const CSV_EXPORT_PATH = '/merge_commit_reports';
......
...@@ -4,11 +4,19 @@ import { createComplianceViolation } from '../mock_data'; ...@@ -4,11 +4,19 @@ import { createComplianceViolation } from '../mock_data';
describe('mapViolations', () => { describe('mapViolations', () => {
it('returns the expected result', () => { it('returns the expected result', () => {
const { mergeRequest } = mapViolations([createComplianceViolation()])[0]; const violation = createComplianceViolation();
const { mergeRequest } = mapViolations([violation])[0];
expect(mergeRequest).toMatchObject({ expect(mergeRequest).toMatchObject({
committers: [],
approvedByUsers: [],
participants: violation.mergeRequest.participants.nodes,
reference: mergeRequest.ref, reference: mergeRequest.ref,
mergedBy: convertObjectPropsToSnakeCase(mergeRequest.mergeUser), mergedBy: convertObjectPropsToSnakeCase(mergeRequest.mergeUser),
project: {
...violation.project,
complianceFramework: violation.mergeRequest.project?.complianceFrameworks?.nodes[0],
},
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment