Commit 53c80aa2 authored by Frédéric Caplette's avatar Frédéric Caplette

Merge branch '299358-update-compliance-report-query-with-filter-variables' into 'master'

Update compliance report to sync filter query with apollo variables

See merge request gitlab-org/gitlab!78451
parents 815208ba 1555efde
...@@ -9,6 +9,7 @@ import UrlSync from '~/vue_shared/components/url_sync.vue'; ...@@ -9,6 +9,7 @@ import UrlSync from '~/vue_shared/components/url_sync.vue';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import complianceViolationsQuery from '../graphql/compliance_violations.query.graphql'; import complianceViolationsQuery from '../graphql/compliance_violations.query.graphql';
import { mapViolations } from '../graphql/mappers'; import { mapViolations } from '../graphql/mappers';
import { parseViolationsQueryFilter } from '../utils';
import MergeCommitsExportButton from './merge_requests/merge_commits_export_button.vue'; import MergeCommitsExportButton from './merge_requests/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';
...@@ -45,7 +46,7 @@ export default { ...@@ -45,7 +46,7 @@ export default {
}, },
data() { data() {
return { return {
urlQuery: {}, urlQuery: { ...this.defaultQuery },
queryError: false, queryError: false,
violations: [], violations: [],
showDrawer: false, showDrawer: false,
...@@ -58,7 +59,8 @@ export default { ...@@ -58,7 +59,8 @@ export default {
query: complianceViolationsQuery, query: complianceViolationsQuery,
variables() { variables() {
return { return {
fullPath: 'groups-path', fullPath: this.groupPath,
filter: parseViolationsQueryFilter(this.urlQuery),
}; };
}, },
update(data) { update(data) {
......
query getComplianceViolations($fullPath: ID!) { # TODO: Add the correct filter type once it has been added in https://gitlab.com/gitlab-org/gitlab/-/issues/347325
group(fullPath: $fullPath) @client { query getComplianceViolations($fullPath: ID!, $filter: Object) {
group(fullPath: $fullPath, filter: $filter) @client {
id id
mergeRequestViolations { mergeRequestViolations {
nodes { nodes {
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { convertToGraphQLIds } from '~/graphql_shared/utils'; import { convertToGraphQLIds } from '~/graphql_shared/utils';
import { TYPE_PROJECT } from '~/graphql_shared/constants'; import { TYPE_PROJECT } from '~/graphql_shared/constants';
import { formatDate } from '~/lib/utils/datetime_utility';
import { ISO_SHORT_FORMAT } from '~/vue_shared/constants';
export const mapDashboardToDrawerData = (mergeRequest) => ({ export const mapDashboardToDrawerData = (mergeRequest) => ({
id: mergeRequest.id, id: mergeRequest.id,
...@@ -21,3 +23,9 @@ export const convertProjectIdsToGraphQl = (projectIds) => ...@@ -21,3 +23,9 @@ export const convertProjectIdsToGraphQl = (projectIds) =>
TYPE_PROJECT, TYPE_PROJECT,
projectIds.filter((id) => Boolean(id)), projectIds.filter((id) => Boolean(id)),
); );
export const parseViolationsQueryFilter = ({ createdBefore, createdAfter, projectIds }) => ({
projectIds: projectIds ? convertProjectIdsToGraphQl(projectIds) : [],
createdBefore: formatDate(createdBefore, ISO_SHORT_FORMAT),
createdAfter: formatDate(createdAfter, ISO_SHORT_FORMAT),
});
...@@ -17,6 +17,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; ...@@ -17,6 +17,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import { parseViolationsQueryFilter } from 'ee/compliance_dashboard/utils';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -26,10 +27,12 @@ describe('ComplianceReport component', () => { ...@@ -26,10 +27,12 @@ describe('ComplianceReport component', () => {
const mergeCommitsCsvExportPath = '/csv'; const mergeCommitsCsvExportPath = '/csv';
const groupPath = 'group-path'; const groupPath = 'group-path';
const createdAfter = '2021-11-16';
const createdBefore = '2021-12-15';
const defaultQuery = { const defaultQuery = {
projectIds: ['gid://gitlab/Project/20'], projectIds: ['20'],
createdAfter: '2021-11-16', createdAfter,
createdBefore: '2021-12-15', createdBefore,
}; };
const mockGraphQlError = new Error('GraphQL networkError'); const mockGraphQlError = new Error('GraphQL networkError');
...@@ -54,6 +57,13 @@ describe('ComplianceReport component', () => { ...@@ -54,6 +57,13 @@ describe('ComplianceReport component', () => {
await nextTick(); await nextTick();
}; };
const expectApolloVariables = (variables) => [
{},
variables,
expect.anything(),
expect.anything(),
];
function createMockApolloProvider() { function createMockApolloProvider() {
return createMockApollo([], { Query: { group: mockResolver } }); return createMockApollo([], { Query: { group: mockResolver } });
} }
...@@ -110,6 +120,7 @@ describe('ComplianceReport component', () => { ...@@ -110,6 +120,7 @@ describe('ComplianceReport component', () => {
describe('when initializing', () => { describe('when initializing', () => {
beforeEach(() => { beforeEach(() => {
mockResolver = jest.fn();
wrapper = createComponent(mount); wrapper = createComponent(mount);
}); });
...@@ -117,6 +128,16 @@ describe('ComplianceReport component', () => { ...@@ -117,6 +128,16 @@ describe('ComplianceReport component', () => {
expect(findViolationsTable().exists()).toBe(true); expect(findViolationsTable().exists()).toBe(true);
expect(findTableLoadingIcon().exists()).toBe(true); expect(findTableLoadingIcon().exists()).toBe(true);
}); });
it('fetches the list of merge request violations with the filter query', async () => {
expect(mockResolver).toHaveBeenCalledTimes(1);
expect(mockResolver).toHaveBeenCalledWith(
...expectApolloVariables({
fullPath: groupPath,
filter: parseViolationsQueryFilter(defaultQuery),
}),
);
});
}); });
describe('when the query fails', () => { describe('when the query fails', () => {
...@@ -244,6 +265,13 @@ describe('ComplianceReport component', () => { ...@@ -244,6 +265,13 @@ describe('ComplianceReport component', () => {
}); });
describe('violation filter', () => { describe('violation filter', () => {
beforeEach(() => {
mockResolver = jest.fn().mockReturnValue(resolvers.Query.group());
wrapper = createComponent(mount);
return waitForPromises();
});
it('configures the filter', () => { it('configures the filter', () => {
expect(findViolationFilter().props()).toMatchObject({ expect(findViolationFilter().props()).toMatchObject({
groupPath, groupPath,
...@@ -251,21 +279,38 @@ describe('ComplianceReport component', () => { ...@@ -251,21 +279,38 @@ describe('ComplianceReport component', () => {
}); });
}); });
it('updates the URL query when the filters changed', async () => { describe('when the filters changed', () => {
const query = { foo: 'bar', projectIds: [1, 2, 3] }; const query = { createdAfter, createdBefore, projectIds: [1, 2, 3] };
await findViolationFilter().vm.$emit('filters-changed', query); beforeEach(() => {
return findViolationFilter().vm.$emit('filters-changed', query);
});
it('updates the URL query', () => {
expect(findUrlSync().props('query')).toMatchObject(query); expect(findUrlSync().props('query')).toMatchObject(query);
}); });
it('clears the project URL query param when the filters changed and the project array is empty', async () => { it('shows the table loading icon', () => {
const query = { foo: 'bar', projectIds: [] }; expect(findTableLoadingIcon().exists()).toBe(true);
});
await findViolationFilter().vm.$emit('filters-changed', query); it('clears the project URL query param if the project array is empty', async () => {
await findViolationFilter().vm.$emit('filters-changed', { ...query, projectIds: [] });
expect(findUrlSync().props('query')).toMatchObject({ ...query, projectIds: null }); expect(findUrlSync().props('query')).toMatchObject({ ...query, projectIds: null });
}); });
it('fetches the filtered violations', async () => {
expect(mockResolver).toHaveBeenCalledTimes(2);
expect(mockResolver).toHaveBeenNthCalledWith(
2,
...expectApolloVariables({
fullPath: groupPath,
filter: parseViolationsQueryFilter(query),
}),
);
});
});
}); });
}); });
......
import * as utils from 'ee/compliance_dashboard/utils'; import * as utils from 'ee/compliance_dashboard/utils';
describe('compliance report utils', () => { describe('compliance report utils', () => {
const projectIds = ['1', '2'];
const projectGraphQlIds = ['gid://gitlab/Project/1', 'gid://gitlab/Project/2'];
describe('parseViolationsQueryFilter', () => {
it('returns the expected result', () => {
const query = {
projectIds,
createdAfter: '2021-12-06',
createdBefore: '2022-01-06',
};
expect(utils.parseViolationsQueryFilter(query)).toStrictEqual({
projectIds: projectGraphQlIds,
createdAfter: query.createdAfter,
createdBefore: query.createdBefore,
});
});
});
describe('convertProjectIdsToGraphQl', () => { describe('convertProjectIdsToGraphQl', () => {
it('returns the expected result', () => { it('returns the expected result', () => {
expect(utils.convertProjectIdsToGraphQl(['1', '2'])).toStrictEqual([ expect(utils.convertProjectIdsToGraphQl(projectIds)).toStrictEqual(projectGraphQlIds);
'gid://gitlab/Project/1',
'gid://gitlab/Project/2',
]);
}); });
}); });
}); });
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