Commit becf081f authored by Miguel Rincon's avatar Miguel Rincon

Merge branch '292266-feature-flag-remove-core_security_mr_widget_downloads' into 'master'

Remove core_security_mr_widget_downloads feature flag [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!51639
parents 283dc43d 1642b1a5
<script>
import { mapActions, mapGetters } from 'vuex';
import { GlLink, GlSprintf } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ReportSection from '~/reports/components/report_section.vue';
import { LOADING, ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
import { s__ } from '~/locale';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import createFlash from '~/flash';
import Api from '~/api';
import HelpIcon from './components/help_icon.vue';
import SecurityReportDownloadDropdown from './components/security_report_download_dropdown.vue';
import SecuritySummary from './components/security_summary.vue';
......@@ -24,8 +21,6 @@ import { extractSecurityReportArtifacts } from './utils';
export default {
store,
components: {
GlLink,
GlSprintf,
ReportSection,
HelpIcon,
SecurityReportDownloadDropdown,
......@@ -101,9 +96,6 @@ export default {
),
};
},
skip() {
return !this.canShowDownloads;
},
update(data) {
return extractSecurityReportArtifacts(this.$options.reportTypes, data);
},
......@@ -124,9 +116,6 @@ export default {
},
computed: {
...mapGetters(['groupedSummaryText', 'summaryStatus']),
canShowDownloads() {
return this.glFeatures.coreSecurityMrWidgetDownloads;
},
hasSecurityReports() {
return this.availableSecurityReports.length > 0;
},
......@@ -139,23 +128,6 @@ export default {
isLoadingReportArtifacts() {
return this.$apollo.queries.reportArtifacts.loading;
},
shouldShowDownloadGuidance() {
return !this.canShowDownloads && this.summaryStatus !== LOADING;
},
scansHaveRunMessage() {
return this.canShowDownloads
? this.$options.i18n.scansHaveRun
: this.$options.i18n.scansHaveRunWithDownloadGuidance;
},
},
created() {
if (!this.canShowDownloads) {
this.checkAvailableSecurityReports(this.$options.reportTypes)
.then((availableSecurityReports) => {
this.onCheckingAvailableSecurityReports(Array.from(availableSecurityReports));
})
.catch(this.showError);
}
},
methods: {
...mapActions(MODULE_SAST, {
......@@ -166,36 +138,6 @@ export default {
setSecretDetectionDiffEndpoint: 'setDiffEndpoint',
fetchSecretDetectionDiff: 'fetchDiff',
}),
async checkAvailableSecurityReports(reportTypes) {
const reportTypesSet = new Set(reportTypes);
const availableReportTypes = new Set();
let page = 1;
while (page) {
// eslint-disable-next-line no-await-in-loop
const { data: jobs, headers } = await Api.pipelineJobs(this.projectId, this.pipelineId, {
per_page: 100,
page,
});
jobs.forEach(({ artifacts = [] }) => {
artifacts.forEach(({ file_type }) => {
if (reportTypesSet.has(file_type)) {
availableReportTypes.add(file_type);
}
});
});
// If we've found artifacts for all the report types, stop looking!
if (availableReportTypes.size === reportTypesSet.size) {
return availableReportTypes;
}
page = parseIntPagination(normalizeHeaders(headers)).nextPage;
}
return availableReportTypes;
},
fetchCounts() {
if (!this.glFeatures.coreSecurityMrWidgetCounts) {
return;
......@@ -213,11 +155,6 @@ export default {
this.canShowCounts = true;
}
},
activatePipelinesTab() {
if (window.mrTabs) {
window.mrTabs.tabShown('pipelines');
}
},
onCheckingAvailableSecurityReports(availableSecurityReports) {
this.availableSecurityReports = availableSecurityReports;
this.fetchCounts();
......@@ -236,12 +173,6 @@ export default {
'SecurityReports|Failed to get security report information. Please reload the page or try again later.',
),
scansHaveRun: s__('SecurityReports|Security scans have run'),
scansHaveRunWithDownloadGuidance: s__(
'SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
),
downloadFromPipelineTab: s__(
'SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
),
},
summarySlots: [SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR],
};
......@@ -265,22 +196,7 @@ export default {
</span>
</template>
<template v-if="shouldShowDownloadGuidance" #sub-heading>
<span class="gl-font-sm">
<gl-sprintf :message="$options.i18n.downloadFromPipelineTab">
<template #link="{ content }">
<gl-link
class="gl-font-sm"
data-testid="show-pipelines"
@click="activatePipelinesTab"
>{{ content }}</gl-link
>
</template>
</gl-sprintf>
</span>
</template>
<template v-if="canShowDownloads" #action-buttons>
<template #action-buttons>
<security-report-download-dropdown
:artifacts="reportArtifacts"
:loading="isLoadingReportArtifacts"
......@@ -298,13 +214,7 @@ export default {
data-testid="security-mr-widget"
>
<template #error>
<gl-sprintf :message="scansHaveRunMessage">
<template #link="{ content }">
<gl-link data-testid="show-pipelines" @click="activatePipelinesTab">{{
content
}}</gl-link>
</template>
</gl-sprintf>
{{ $options.i18n.scansHaveRun }}
<help-icon
:help-path="securityReportsDocsPath"
......@@ -312,7 +222,7 @@ export default {
/>
</template>
<template v-if="canShowDownloads" #action-buttons>
<template #action-buttons>
<security-report-download-dropdown
:artifacts="reportArtifacts"
:loading="isLoadingReportArtifacts"
......
......@@ -38,7 +38,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
push_frontend_feature_flag(:core_security_mr_widget_downloads, @project, default_enabled: true)
push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true)
push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
push_frontend_feature_flag(:codequality_mr_diff, @project)
......
---
name: core_security_mr_widget_downloads
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48769
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273418
milestone: '13.7'
type: development
group: group::static analysis
default_enabled: true
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import MrWidgetOptions from 'ee/vue_merge_request_widget/mr_widget_options.vue';
import {
sastDiffSuccessMock,
......@@ -11,6 +12,8 @@ import {
coverageFuzzingDiffSuccessMock,
apiFuzzingDiffSuccessMock,
} from 'ee_jest/vue_shared/security_reports/mock_data';
import { securityReportDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data';
import createMockApollo from 'helpers/mock_apollo_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { trimText } from 'helpers/text_helper';
import waitForPromises from 'helpers/wait_for_promises';
......@@ -18,12 +21,12 @@ import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_download_paths.query.graphql';
import mockData, {
baseBrowserPerformance,
headBrowserPerformance,
baseLoadPerformance,
headLoadPerformance,
pipelineJobs,
} from './mock_data';
// Force Jest to transpile and cache
......@@ -32,6 +35,8 @@ import _GroupedSecurityReportsApp from 'ee/vue_shared/security_reports/grouped_s
// eslint-disable-next-line no-unused-vars
import _Deployment from '~/vue_merge_request_widget/components/deployment/deployment.vue';
Vue.use(VueApollo);
const SAST_SELECTOR = '.js-sast-widget';
const DAST_SELECTOR = '.js-dast-widget';
const DEPENDENCY_SCANNING_SELECTOR = '.js-dependency-scanning-widget';
......@@ -999,8 +1004,6 @@ describe('ee merge request widget options', () => {
});
describe('CE security report', () => {
const PIPELINE_JOBS_ENDPOINT = `/api/undefined/projects/${mockData.target_project_id}/pipelines/${mockData.pipeline.id}/jobs`;
describe.each`
context | canReadVulnerabilities | hasPipeline | featureFlag | shouldRender
${'user cannot read vulnerabilities'} | ${false} | ${true} | ${true} | ${true}
......@@ -1017,8 +1020,15 @@ describe('ee merge request widget options', () => {
gon.features = { coreSecurityMrWidget: featureFlag };
mock.onGet(PIPELINE_JOBS_ENDPOINT).replyOnce(200, pipelineJobs);
createComponent({ propsData: { mrData: gl.mrWidgetData } });
createComponent({
propsData: { mrData: gl.mrWidgetData },
apolloProvider: createMockApollo([
[
securityReportDownloadPathsQuery,
async () => ({ data: securityReportDownloadPathsQueryResponse }),
],
]),
});
return waitForPromises();
});
......
......@@ -133,14 +133,3 @@ export const codequalityParsedIssues = [
];
export { mockStore };
// TODO: Remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/249544
export const pipelineJobs = [
{
artifacts: [
{
file_type: 'sast',
},
],
},
];
......@@ -25174,9 +25174,6 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
msgstr ""
msgid "SecurityReports|Hide dismissed"
msgstr ""
......@@ -25240,9 +25237,6 @@ msgstr ""
msgid "SecurityReports|Security scans have run"
msgstr ""
msgid "SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
msgstr ""
msgid "SecurityReports|Select a project to add by using the project search field above."
msgstr ""
......
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import MockAdapter from 'axios-mock-adapter';
import Api from '~/api';
import createMockApollo from 'helpers/mock_apollo_helper';
import { securityReportDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data';
import axios from '~/lib/utils/axios_utils';
import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
......@@ -12,11 +14,14 @@ import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from './mock_data';
import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data';
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_download_paths.query.graphql';
jest.mock('~/smart_interval');
jest.mock('~/lib/utils/favicon');
Vue.use(VueApollo);
describe('MrWidgetOptions', () => {
let wrapper;
let mock;
......@@ -41,7 +46,7 @@ describe('MrWidgetOptions', () => {
gon.features = {};
});
const createComponent = (mrData = mockData) => {
const createComponent = (mrData = mockData, options = {}) => {
if (wrapper) {
wrapper.destroy();
}
......@@ -50,6 +55,7 @@ describe('MrWidgetOptions', () => {
propsData: {
mrData: { ...mrData },
},
...options,
});
return axios.waitForAll();
......@@ -815,36 +821,37 @@ describe('MrWidgetOptions', () => {
describe('security widget', () => {
describe.each`
context | hasPipeline | reportType | isFlagEnabled | shouldRender
${'security report and flag enabled'} | ${true} | ${'sast'} | ${true} | ${true}
${'security report and flag disabled'} | ${true} | ${'sast'} | ${false} | ${false}
${'no security report and flag enabled'} | ${true} | ${'foo'} | ${true} | ${false}
${'no pipeline and flag enabled'} | ${false} | ${'sast'} | ${true} | ${false}
`('given $context', ({ hasPipeline, reportType, isFlagEnabled, shouldRender }) => {
context | hasPipeline | isFlagEnabled | shouldRender
${'has pipeline and flag enabled'} | ${true} | ${true} | ${true}
${'has pipeline and flag disabled'} | ${true} | ${false} | ${false}
${'no pipeline and flag enabled'} | ${false} | ${true} | ${false}
`('given $context', ({ hasPipeline, isFlagEnabled, shouldRender }) => {
beforeEach(() => {
gon.features.coreSecurityMrWidget = isFlagEnabled;
if (hasPipeline) {
jest.spyOn(Api, 'pipelineJobs').mockResolvedValue({
data: [{ artifacts: [{ file_type: reportType }] }],
});
}
return createComponent({
const mrData = {
...mockData,
...(hasPipeline ? {} : { pipeline: undefined }),
...(hasPipeline ? {} : { pipeline: null }),
};
// Override top-level mocked requests, which always use a fresh copy of
// mockData, which always includes the full pipeline object.
mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]);
mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]);
return createComponent(mrData, {
apolloProvider: createMockApollo([
[
securityReportDownloadPathsQuery,
async () => ({ data: securityReportDownloadPathsQueryResponse }),
],
]),
});
});
if (shouldRender) {
it('renders', () => {
expect(findSecurityMrWidget().exists()).toBe(true);
});
} else {
it('does not render', () => {
expect(findSecurityMrWidget().exists()).toBe(false);
});
}
it(shouldRender ? 'renders' : 'does not render', () => {
expect(findSecurityMrWidget().exists()).toBe(shouldRender);
});
});
});
......
......@@ -322,6 +322,23 @@ export const secretScanningDiffSuccessMock = {
head_report_created_at: '2020-01-10T10:00:00.000Z',
};
export const securityReportDownloadPathsQueryNoArtifactsResponse = {
project: {
mergeRequest: {
headPipeline: {
id: 'gid://gitlab/Ci::Pipeline/176',
jobs: {
nodes: [],
__typename: 'CiJobConnection',
},
__typename: 'Pipeline',
},
__typename: 'MergeRequest',
},
__typename: 'Project',
},
};
export const securityReportDownloadPathsQueryResponse = {
project: {
mergeRequest: {
......
......@@ -8,11 +8,11 @@ import { trimText } from 'helpers/text_helper';
import waitForPromises from 'helpers/wait_for_promises';
import {
expectedDownloadDropdownProps,
securityReportDownloadPathsQueryNoArtifactsResponse,
securityReportDownloadPathsQueryResponse,
sastDiffSuccessMock,
secretScanningDiffSuccessMock,
} from 'jest/vue_shared/security_reports/mock_data';
import Api from '~/api';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
......@@ -60,6 +60,8 @@ describe('Security reports app', () => {
const pendingHandler = () => new Promise(() => {});
const successHandler = () => Promise.resolve({ data: securityReportDownloadPathsQueryResponse });
const successEmptyHandler = () =>
Promise.resolve({ data: securityReportDownloadPathsQueryNoArtifactsResponse });
const failureHandler = () => Promise.resolve({ errors: [{ message: 'some error' }] });
const createMockApolloProvider = (handler) => {
localVue.use(VueApollo);
......@@ -69,178 +71,85 @@ describe('Security reports app', () => {
return createMockApollo(requestHandlers);
};
const anyParams = expect.any(Object);
const findDownloadDropdown = () => wrapper.find(SecurityReportDownloadDropdown);
const findPipelinesTabAnchor = () => wrapper.find('[data-testid="show-pipelines"]');
const findHelpIconComponent = () => wrapper.find(HelpIcon);
const setupMockJobArtifact = (reportType) => {
jest
.spyOn(Api, 'pipelineJobs')
.mockResolvedValue({ data: [{ artifacts: [{ file_type: reportType }] }] });
};
const expectPipelinesTabAnchor = () => {
const mrTabsMock = { tabShown: jest.fn() };
window.mrTabs = mrTabsMock;
findPipelinesTabAnchor().trigger('click');
expect(mrTabsMock.tabShown.mock.calls).toEqual([['pipelines']]);
};
afterEach(() => {
wrapper.destroy();
delete window.mrTabs;
});
describe.each([false, true])(
'given the coreSecurityMrWidgetCounts feature flag is %p',
(coreSecurityMrWidgetCounts) => {
const createComponentWithFlag = (options) =>
createComponent(
merge(
{
provide: {
glFeatures: {
coreSecurityMrWidgetCounts,
},
},
},
options,
),
);
describe.each(SecurityReportsApp.reportTypes)('given a report type %p', (reportType) => {
beforeEach(() => {
window.mrTabs = { tabShown: jest.fn() };
setupMockJobArtifact(reportType);
createComponentWithFlag();
return wrapper.vm.$nextTick();
});
it('calls the pipelineJobs API correctly', () => {
expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
expect(Api.pipelineJobs).toHaveBeenCalledWith(
props.projectId,
props.pipelineId,
anyParams,
);
});
it('renders the expected message', () => {
expect(wrapper.text()).toMatchInterpolatedText(
SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
);
});
describe('clicking the anchor to the pipelines tab', () => {
it('calls the mrTabs.tabShown global', () => {
expectPipelinesTabAnchor();
});
});
it('renders a help link', () => {
expect(findHelpIconComponent().props()).toEqual({
helpPath: props.securityReportsDocsPath,
discoverProjectSecurityPath: props.discoverProjectSecurityPath,
});
});
describe('given the artifacts query is loading', () => {
beforeEach(() => {
createComponent({
apolloProvider: createMockApolloProvider(pendingHandler),
});
});
describe('given a report type "foo"', () => {
beforeEach(() => {
setupMockJobArtifact('foo');
createComponentWithFlag();
return wrapper.vm.$nextTick();
});
it('calls the pipelineJobs API correctly', () => {
expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
expect(Api.pipelineJobs).toHaveBeenCalledWith(
props.projectId,
props.pipelineId,
anyParams,
);
});
// TODO: Remove this assertion as part of
// https://gitlab.com/gitlab-org/gitlab/-/issues/273431
it('initially renders nothing', () => {
expect(wrapper.html()).toBe('');
});
});
it('renders nothing', () => {
expect(wrapper.html()).toBe('');
});
describe('given the artifacts query loads successfully', () => {
beforeEach(() => {
createComponent({
apolloProvider: createMockApolloProvider(successHandler),
});
});
describe('security artifacts on last page of multi-page response', () => {
const numPages = 3;
beforeEach(() => {
jest
.spyOn(Api, 'pipelineJobs')
.mockImplementation(async (projectId, pipelineId, { page }) => {
const requestedPage = parseInt(page, 10);
if (requestedPage < numPages) {
return {
// Some jobs with no relevant artifacts
data: [{}, {}],
headers: { 'x-next-page': String(requestedPage + 1) },
};
} else if (requestedPage === numPages) {
return {
data: [{ artifacts: [{ file_type: SecurityReportsApp.reportTypes[0] }] }],
};
}
throw new Error('Test failed due to request of non-existent jobs page');
});
createComponentWithFlag();
return wrapper.vm.$nextTick();
});
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
it('fetches all pages', () => {
expect(Api.pipelineJobs).toHaveBeenCalledTimes(numPages);
});
it('renders the expected message', () => {
expect(wrapper.text()).toContain(SecurityReportsApp.i18n.scansHaveRun);
});
it('renders the expected message', () => {
expect(wrapper.text()).toMatchInterpolatedText(
SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
);
});
it('renders a help link', () => {
expect(findHelpIconComponent().props()).toEqual({
helpPath: props.securityReportsDocsPath,
discoverProjectSecurityPath: props.discoverProjectSecurityPath,
});
});
});
describe('given an error from the API', () => {
let error;
beforeEach(() => {
error = new Error('an error');
jest.spyOn(Api, 'pipelineJobs').mockRejectedValue(error);
createComponentWithFlag();
return wrapper.vm.$nextTick();
});
describe('given the artifacts query loads successfully with no artifacts', () => {
beforeEach(() => {
createComponent({
apolloProvider: createMockApolloProvider(successEmptyHandler),
});
});
it('calls the pipelineJobs API correctly', () => {
expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
expect(Api.pipelineJobs).toHaveBeenCalledWith(
props.projectId,
props.pipelineId,
anyParams,
);
});
// TODO: Remove this assertion as part of
// https://gitlab.com/gitlab-org/gitlab/-/issues/273431
it('initially renders nothing', () => {
expect(wrapper.html()).toBe('');
});
});
it('renders nothing', () => {
expect(wrapper.html()).toBe('');
});
describe('given the artifacts query fails', () => {
beforeEach(() => {
createComponent({
apolloProvider: createMockApolloProvider(failureHandler),
});
});
it('calls createFlash correctly', () => {
expect(createFlash.mock.calls).toEqual([
[
{
message: SecurityReportsApp.i18n.apiError,
captureError: true,
error,
},
],
]);
});
it('calls createFlash correctly', () => {
expect(createFlash).toHaveBeenCalledWith({
message: SecurityReportsApp.i18n.apiError,
captureError: true,
error: expect.any(Error),
});
},
);
});
// TODO: Remove this assertion as part of
// https://gitlab.com/gitlab-org/gitlab/-/issues/273431
it('renders nothing', () => {
expect(wrapper.html()).toBe('');
});
});
describe('given the coreSecurityMrWidgetCounts feature flag is enabled', () => {
let mock;
......@@ -253,6 +162,7 @@ describe('Security reports app', () => {
coreSecurityMrWidgetCounts: true,
},
},
apolloProvider: createMockApolloProvider(successHandler),
}),
);
......@@ -274,11 +184,7 @@ describe('Security reports app', () => {
${REPORT_TYPE_SECRET_DETECTION} | ${'secretScanningComparisonPath'} | ${SECRET_SCANNING_COMPARISON_PATH} | ${secretScanningDiffSuccessMock} | ${SECRET_SCANNING_SUCCESS_MESSAGE}
`(
'given a $pathProp and $reportType artifact',
({ reportType, pathProp, path, successResponse, successMessage }) => {
beforeEach(() => {
setupMockJobArtifact(reportType);
});
({ pathProp, path, successResponse, successMessage }) => {
describe('when loading', () => {
beforeEach(() => {
mock = new MockAdapter(axios, { delayResponse: 1 });
......@@ -294,11 +200,11 @@ describe('Security reports app', () => {
});
it('should have loading message', () => {
expect(wrapper.text()).toBe('Security scanning is loading');
expect(wrapper.text()).toContain('Security scanning is loading');
});
it('should not render the pipeline tab anchor', () => {
expect(findPipelinesTabAnchor().exists()).toBe(false);
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
......@@ -319,8 +225,8 @@ describe('Security reports app', () => {
expect(trimText(wrapper.text())).toContain(successMessage);
});
it('should render the pipeline tab anchor', () => {
expectPipelinesTabAnchor();
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
......@@ -341,125 +247,25 @@ describe('Security reports app', () => {
expect(trimText(wrapper.text())).toContain('Loading resulted in an error');
});
it('should render the pipeline tab anchor', () => {
expectPipelinesTabAnchor();
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
},
);
});
describe('given coreSecurityMrWidgetDownloads feature flag is enabled', () => {
const createComponentWithFlagEnabled = (options) =>
createComponent(
merge(options, {
provide: {
glFeatures: {
coreSecurityMrWidgetDownloads: true,
},
},
}),
);
describe('given the query is loading', () => {
beforeEach(() => {
createComponentWithFlagEnabled({
apolloProvider: createMockApolloProvider(pendingHandler),
});
});
// TODO: Remove this assertion as part of
// https://gitlab.com/gitlab-org/gitlab/-/issues/273431
it('initially renders nothing', () => {
expect(wrapper.html()).toBe('');
});
});
describe('given the query loads successfully', () => {
beforeEach(() => {
createComponentWithFlagEnabled({
apolloProvider: createMockApolloProvider(successHandler),
});
});
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
it('renders the expected message', () => {
const text = wrapper.text();
expect(text).not.toContain(SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance);
expect(text).toContain(SecurityReportsApp.i18n.scansHaveRun);
});
describe('when the comparison endpoint is not provided', () => {
beforeEach(() => {
mock.onGet(path).replyOnce(500);
it('should not render the pipeline tab anchor', () => {
expect(findPipelinesTabAnchor().exists()).toBe(false);
});
});
createComponentWithFlagEnabled();
describe('given the query fails', () => {
beforeEach(() => {
createComponentWithFlagEnabled({
apolloProvider: createMockApolloProvider(failureHandler),
});
});
return waitForPromises();
});
it('calls createFlash correctly', () => {
expect(createFlash).toHaveBeenCalledWith({
message: SecurityReportsApp.i18n.apiError,
captureError: true,
error: expect.any(Error),
it('renders the basic scansHaveRun message', () => {
expect(wrapper.text()).toContain(SecurityReportsApp.i18n.scansHaveRun);
});
});
});
// TODO: Remove this assertion as part of
// https://gitlab.com/gitlab-org/gitlab/-/issues/273431
it('renders nothing', () => {
expect(wrapper.html()).toBe('');
});
});
});
describe('given coreSecurityMrWidgetCounts and coreSecurityMrWidgetDownloads feature flags are enabled', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(SAST_COMPARISON_PATH).replyOnce(200, sastDiffSuccessMock);
mock.onGet(SECRET_SCANNING_COMPARISON_PATH).replyOnce(200, secretScanningDiffSuccessMock);
createComponent({
propsData: {
sastComparisonPath: SAST_COMPARISON_PATH,
secretScanningComparisonPath: SECRET_SCANNING_COMPARISON_PATH,
},
provide: {
glFeatures: {
coreSecurityMrWidgetCounts: true,
coreSecurityMrWidgetDownloads: true,
},
},
apolloProvider: createMockApolloProvider(successHandler),
});
return waitForPromises();
});
afterEach(() => {
mock.restore();
});
it('renders the download dropdown', () => {
expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
it('renders the expected counts message', () => {
expect(trimText(wrapper.text())).toContain(
'Security scanning detected 3 potential vulnerabilities 2 Critical 1 High and 0 Others',
);
});
it('should not render the pipeline tab anchor', () => {
expect(findPipelinesTabAnchor().exists()).toBe(false);
});
},
);
});
});
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