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>2021-05-19 18:44:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/frontend/vue_shared/security_reports
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/security_reports')
-rw-r--r--spec/frontend/vue_shared/security_reports/components/apollo_mocks.js12
-rw-r--r--spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js184
-rw-r--r--spec/frontend/vue_shared/security_reports/mock_data.js120
-rw-r--r--spec/frontend/vue_shared/security_reports/security_reports_app_spec.js13
-rw-r--r--spec/frontend/vue_shared/security_reports/utils_spec.js25
5 files changed, 335 insertions, 19 deletions
diff --git a/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js b/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
new file mode 100644
index 00000000000..066f9a57bc6
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
@@ -0,0 +1,12 @@
+export const buildConfigureSecurityFeatureMockFactory = (mutationType) => ({
+ successPath = 'testSuccessPath',
+ errors = [],
+} = {}) => ({
+ data: {
+ [mutationType]: {
+ successPath,
+ errors,
+ __typename: `${mutationType}Payload`,
+ },
+ },
+});
diff --git a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
new file mode 100644
index 00000000000..517eee6a729
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
@@ -0,0 +1,184 @@
+import { GlButton } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { featureToMutationMap } from 'ee_else_ce/security_configuration/components/constants';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { humanize } from '~/lib/utils/text_utility';
+import { redirectTo } from '~/lib/utils/url_utility';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
+import { buildConfigureSecurityFeatureMockFactory } from './apollo_mocks';
+
+jest.mock('~/lib/utils/url_utility');
+
+Vue.use(VueApollo);
+
+const projectPath = 'namespace/project';
+
+describe('ManageViaMr component', () => {
+ let wrapper;
+
+ const findButton = () => wrapper.findComponent(GlButton);
+
+ function createMockApolloProvider(mutation, handler) {
+ const requestHandlers = [[mutation, handler]];
+
+ return createMockApollo(requestHandlers);
+ }
+
+ function createComponent({
+ featureName = 'SAST',
+ featureType = 'sast',
+ isFeatureConfigured = false,
+ variant = undefined,
+ category = undefined,
+ ...options
+ } = {}) {
+ wrapper = extendedWrapper(
+ mount(ManageViaMr, {
+ provide: {
+ projectPath,
+ },
+ propsData: {
+ feature: {
+ name: featureName,
+ type: featureType,
+ configured: isFeatureConfigured,
+ },
+ variant,
+ category,
+ },
+ ...options,
+ }),
+ );
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ // This component supports different report types/mutations depending on
+ // whether it's in a CE or EE context. This makes sure we are only testing
+ // the ones available in the current test context.
+ const supportedReportTypes = Object.entries(featureToMutationMap).map(
+ ([featureType, { getMutationPayload, mutationId }]) => {
+ const { mutation, variables: mutationVariables } = getMutationPayload(projectPath);
+ return [humanize(featureType), featureType, mutation, mutationId, mutationVariables];
+ },
+ );
+
+ describe.each(supportedReportTypes)(
+ '%s',
+ (featureName, featureType, mutation, mutationId, mutationVariables) => {
+ const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(
+ mutationId,
+ );
+ const successHandler = jest.fn(async () => buildConfigureSecurityFeatureMock());
+ const noSuccessPathHandler = async () =>
+ buildConfigureSecurityFeatureMock({
+ successPath: '',
+ });
+ const errorHandler = async () =>
+ buildConfigureSecurityFeatureMock({
+ errors: ['foo'],
+ });
+ const pendingHandler = () => new Promise(() => {});
+
+ describe('when feature is configured', () => {
+ beforeEach(() => {
+ const apolloProvider = createMockApolloProvider(mutation, successHandler);
+ createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: true });
+ });
+
+ it('it does not render a button', () => {
+ expect(findButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when feature is not configured', () => {
+ beforeEach(() => {
+ const apolloProvider = createMockApolloProvider(mutation, successHandler);
+ createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: false });
+ });
+
+ it('it does render a button', () => {
+ expect(findButton().exists()).toBe(true);
+ });
+
+ it('clicking on the button triggers the configure mutation', () => {
+ findButton().trigger('click');
+
+ expect(successHandler).toHaveBeenCalledTimes(1);
+ expect(successHandler).toHaveBeenCalledWith(mutationVariables);
+ });
+ });
+
+ describe('given a pending response', () => {
+ beforeEach(() => {
+ const apolloProvider = createMockApolloProvider(mutation, pendingHandler);
+ createComponent({ apolloProvider, featureName, featureType });
+ });
+
+ it('renders spinner correctly', async () => {
+ const button = findButton();
+ expect(button.props('loading')).toBe(false);
+ await button.trigger('click');
+ expect(button.props('loading')).toBe(true);
+ });
+ });
+
+ describe('given a successful response', () => {
+ beforeEach(() => {
+ const apolloProvider = createMockApolloProvider(mutation, successHandler);
+ createComponent({ apolloProvider, featureName, featureType });
+ });
+
+ it('should call redirect helper with correct value', async () => {
+ await wrapper.trigger('click');
+ await waitForPromises();
+ expect(redirectTo).toHaveBeenCalledTimes(1);
+ expect(redirectTo).toHaveBeenCalledWith('testSuccessPath');
+ // This is done for UX reasons. If the loading prop is set to false
+ // on success, then there's a period where the button is clickable
+ // again. Instead, we want the button to display a loading indicator
+ // for the remainder of the lifetime of the page (i.e., until the
+ // browser can start painting the new page it's been redirected to).
+ expect(findButton().props().loading).toBe(true);
+ });
+ });
+
+ describe.each`
+ handler | message
+ ${noSuccessPathHandler} | ${`${featureName} merge request creation mutation failed`}
+ ${errorHandler} | ${'foo'}
+ `('given an error response', ({ handler, message }) => {
+ beforeEach(() => {
+ const apolloProvider = createMockApolloProvider(mutation, handler);
+ createComponent({ apolloProvider, featureName, featureType });
+ });
+
+ it('should catch and emit error', async () => {
+ await wrapper.trigger('click');
+ await waitForPromises();
+ expect(wrapper.emitted('error')).toEqual([[message]]);
+ expect(findButton().props('loading')).toBe(false);
+ });
+ });
+ },
+ );
+
+ describe('button props', () => {
+ it('passes the variant and category props to the GlButton', () => {
+ const variant = 'danger';
+ const category = 'tertiary';
+ createComponent({ variant, category });
+
+ expect(wrapper.findComponent(GlButton).props()).toMatchObject({
+ variant,
+ category,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/mock_data.js b/spec/frontend/vue_shared/security_reports/mock_data.js
index 7918f70d702..bd9ce3b7314 100644
--- a/spec/frontend/vue_shared/security_reports/mock_data.js
+++ b/spec/frontend/vue_shared/security_reports/mock_data.js
@@ -322,7 +322,7 @@ export const secretScanningDiffSuccessMock = {
head_report_created_at: '2020-01-10T10:00:00.000Z',
};
-export const securityReportDownloadPathsQueryNoArtifactsResponse = {
+export const securityReportMergeRequestDownloadPathsQueryNoArtifactsResponse = {
project: {
mergeRequest: {
headPipeline: {
@@ -339,7 +339,7 @@ export const securityReportDownloadPathsQueryNoArtifactsResponse = {
},
};
-export const securityReportDownloadPathsQueryResponse = {
+export const securityReportMergeRequestDownloadPathsQueryResponse = {
project: {
mergeRequest: {
headPipeline: {
@@ -447,8 +447,114 @@ export const securityReportDownloadPathsQueryResponse = {
},
};
+export const securityReportPipelineDownloadPathsQueryResponse = {
+ project: {
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/176',
+ jobs: {
+ nodes: [
+ {
+ name: 'secret_detection',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1399/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1399/artifacts/download?file_type=secret_detection',
+ fileType: 'SECRET_DETECTION',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ {
+ name: 'bandit-sast',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1400/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1400/artifacts/download?file_type=sast',
+ fileType: 'SAST',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ {
+ name: 'eslint-sast',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1401/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1401/artifacts/download?file_type=sast',
+ fileType: 'SAST',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ {
+ name: 'all_artifacts',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1402/artifacts/download?file_type=archive',
+ fileType: 'ARCHIVE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1402/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1402/artifacts/download?file_type=metadata',
+ fileType: 'METADATA',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ ],
+ __typename: 'CiJobConnection',
+ },
+ __typename: 'Pipeline',
+ },
+ __typename: 'MergeRequest',
+ },
+ __typename: 'Project',
+};
+
/**
- * These correspond to SAST jobs in the securityReportDownloadPathsQueryResponse above.
+ * These correspond to SAST jobs in the securityReportMergeRequestDownloadPathsQueryResponse above.
*/
export const sastArtifacts = [
{
@@ -464,7 +570,7 @@ export const sastArtifacts = [
];
/**
- * These correspond to Secret Detection jobs in the securityReportDownloadPathsQueryResponse above.
+ * These correspond to Secret Detection jobs in the securityReportMergeRequestDownloadPathsQueryResponse above.
*/
export const secretDetectionArtifacts = [
{
@@ -481,7 +587,7 @@ export const expectedDownloadDropdownProps = {
};
/**
- * These correspond to any jobs with zip archives in the securityReportDownloadPathsQueryResponse above.
+ * These correspond to any jobs with zip archives in the securityReportMergeRequestDownloadPathsQueryResponse above.
*/
export const archiveArtifacts = [
{
@@ -492,7 +598,7 @@ export const archiveArtifacts = [
];
/**
- * These correspond to any jobs with trace data in the securityReportDownloadPathsQueryResponse above.
+ * These correspond to any jobs with trace data in the securityReportMergeRequestDownloadPathsQueryResponse above.
*/
export const traceArtifacts = [
{
@@ -518,7 +624,7 @@ export const traceArtifacts = [
];
/**
- * These correspond to any jobs with metadata data in the securityReportDownloadPathsQueryResponse above.
+ * These correspond to any jobs with metadata data in the securityReportMergeRequestDownloadPathsQueryResponse above.
*/
export const metadataArtifacts = [
{
diff --git a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
index 0b4816a951e..038d7754776 100644
--- a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
+++ b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
@@ -9,8 +9,8 @@ import { trimText } from 'helpers/text_helper';
import waitForPromises from 'helpers/wait_for_promises';
import {
expectedDownloadDropdownProps,
- securityReportDownloadPathsQueryNoArtifactsResponse,
- securityReportDownloadPathsQueryResponse,
+ securityReportMergeRequestDownloadPathsQueryNoArtifactsResponse,
+ securityReportMergeRequestDownloadPathsQueryResponse,
sastDiffSuccessMock,
secretScanningDiffSuccessMock,
} from 'jest/vue_shared/security_reports/mock_data';
@@ -22,7 +22,7 @@ import {
REPORT_TYPE_SAST,
REPORT_TYPE_SECRET_DETECTION,
} from '~/vue_shared/security_reports/constants';
-import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_download_paths.query.graphql';
+import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_merge_request_download_paths.query.graphql';
import SecurityReportsApp from '~/vue_shared/security_reports/security_reports_app.vue';
jest.mock('~/flash');
@@ -59,12 +59,13 @@ describe('Security reports app', () => {
};
const pendingHandler = () => new Promise(() => {});
- const successHandler = () => Promise.resolve({ data: securityReportDownloadPathsQueryResponse });
+ const successHandler = () =>
+ Promise.resolve({ data: securityReportMergeRequestDownloadPathsQueryResponse });
const successEmptyHandler = () =>
- Promise.resolve({ data: securityReportDownloadPathsQueryNoArtifactsResponse });
+ Promise.resolve({ data: securityReportMergeRequestDownloadPathsQueryNoArtifactsResponse });
const failureHandler = () => Promise.resolve({ errors: [{ message: 'some error' }] });
const createMockApolloProvider = (handler) => {
- const requestHandlers = [[securityReportDownloadPathsQuery, handler]];
+ const requestHandlers = [[securityReportMergeRequestDownloadPathsQuery, handler]];
return createMockApollo(requestHandlers);
};
diff --git a/spec/frontend/vue_shared/security_reports/utils_spec.js b/spec/frontend/vue_shared/security_reports/utils_spec.js
index aa9e54fa10c..b7129ece698 100644
--- a/spec/frontend/vue_shared/security_reports/utils_spec.js
+++ b/spec/frontend/vue_shared/security_reports/utils_spec.js
@@ -3,9 +3,13 @@ import {
REPORT_TYPE_SECRET_DETECTION,
REPORT_FILE_TYPES,
} from '~/vue_shared/security_reports/constants';
-import { extractSecurityReportArtifacts } from '~/vue_shared/security_reports/utils';
import {
- securityReportDownloadPathsQueryResponse,
+ extractSecurityReportArtifactsFromMergeRequest,
+ extractSecurityReportArtifactsFromPipeline,
+} from '~/vue_shared/security_reports/utils';
+import {
+ securityReportMergeRequestDownloadPathsQueryResponse,
+ securityReportPipelineDownloadPathsQueryResponse,
sastArtifacts,
secretDetectionArtifacts,
archiveArtifacts,
@@ -13,7 +17,18 @@ import {
metadataArtifacts,
} from './mock_data';
-describe('extractSecurityReportArtifacts', () => {
+describe.each([
+ [
+ 'extractSecurityReportArtifactsFromMergeRequest',
+ extractSecurityReportArtifactsFromMergeRequest,
+ securityReportMergeRequestDownloadPathsQueryResponse,
+ ],
+ [
+ 'extractSecurityReportArtifactsFromPipelines',
+ extractSecurityReportArtifactsFromPipeline,
+ securityReportPipelineDownloadPathsQueryResponse,
+ ],
+])('%s', (funcName, extractFunc, response) => {
it.each`
reportTypes | expectedArtifacts
${[]} | ${[]}
@@ -27,9 +42,7 @@ describe('extractSecurityReportArtifacts', () => {
`(
'returns the expected artifacts given report types $reportTypes',
({ reportTypes, expectedArtifacts }) => {
- expect(
- extractSecurityReportArtifacts(reportTypes, securityReportDownloadPathsQueryResponse),
- ).toEqual(expectedArtifacts);
+ expect(extractFunc(reportTypes, response)).toEqual(expectedArtifacts);
},
);
});