Commit 011bf15a authored by Paul Gascou-Vaillancourt's avatar Paul Gascou-Vaillancourt Committed by Fabio Pitino

Remove "Saved scans" tab from DAST configuration

This removes the "Saved scans" tab from the DAST configuration page.
Saved scans are now available in the "On-demand Scans" page and can be
managed from there.

Changelog: removed
EE: true
parent 82253cc6
......@@ -76,10 +76,17 @@ export default {
return this.liveOnDemandScanCounts ?? this.initialOnDemandScanCounts;
},
hasData() {
// Scheduled scans aren't included in the total count yet because they are dastProfiles and
// not pipelines. When https://gitlab.com/gitlab-org/gitlab/-/issues/342950 is addressed, we
// will be able to rely on the "all" count only here.
return this.onDemandScanCounts.all + this.onDemandScanCounts.scheduled > 0;
// Scheduled and saved scans aren't included in the total count because they are
// dastProfiles, not pipelines.
// When https://gitlab.com/gitlab-org/gitlab/-/issues/342950 is addressed, we won't need to
// include scheduled scans in the calculation. We'll still need to include saved scans as
// those will likely neverr be considered pipelines.
return (
this.onDemandScanCounts.all +
this.onDemandScanCounts.scheduled +
this.onDemandScanCounts.saved >
0
);
},
tabs() {
return {
......
......@@ -10,8 +10,8 @@ import {
import { __, s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import ScanTypeBadge from 'ee/security_configuration/dast_profiles/components/dast_scan_type_badge.vue';
import dastProfileRunMutation from 'ee/security_configuration/dast_profiles/graphql/dast_profile_run.mutation.graphql';
import dastProfileDelete from 'ee/security_configuration/dast_profiles/graphql/dast_profile_delete.mutation.graphql';
import dastProfileRunMutation from '../../graphql/dast_profile_run.mutation.graphql';
import dastProfileDelete from '../../graphql/dast_profile_delete.mutation.graphql';
import handlesErrors from '../../mixins/handles_errors';
import { removeProfile } from '../../graphql/cache_utils';
import dastProfilesQuery from '../../graphql/dast_profiles.query.graphql';
......
......@@ -104,7 +104,7 @@ export default {
SITE_PROFILES_QUERY,
),
},
inject: ['projectPath', 'profilesLibraryPath'],
inject: ['projectPath', 'onDemandScansPath'],
props: {
defaultBranch: {
type: String,
......@@ -268,7 +268,7 @@ export default {
this.showErrors(ERROR_RUN_SCAN, errors);
this.loading = false;
} else if (!runAfter) {
redirectTo(this.profilesLibraryPath);
redirectTo(this.onDemandScansPath);
this.clearStorage = true;
} else {
this.clearStorage = true;
......@@ -283,7 +283,7 @@ export default {
},
onCancelClicked() {
this.clearStorage = true;
redirectTo(this.profilesLibraryPath);
redirectTo(this.onDemandScansPath);
},
showErrors(errorType, errors = []) {
this.errorType = errorType;
......@@ -330,7 +330,7 @@ export default {
<header class="gl-mb-6">
<div class="gl-mt-6 gl-display-flex">
<h2 class="gl-flex-grow-1 gl-my-0">{{ title }}</h2>
<gl-button :href="profilesLibraryPath" data-testid="manage-profiles-link">
<gl-button :href="onDemandScansPath" data-testid="manage-profiles-link">
{{ s__('OnDemandScans|Manage DAST scans') }}
</gl-button>
</div>
......
......@@ -11,7 +11,7 @@ export default () => {
const {
projectPath,
defaultBranch,
profilesLibraryPath,
onDemandScansPath,
scannerProfilesLibraryPath,
siteProfilesLibraryPath,
newSiteProfilePath,
......@@ -25,7 +25,7 @@ export default () => {
apolloProvider,
provide: {
projectPath,
profilesLibraryPath,
onDemandScansPath,
scannerProfilesLibraryPath,
siteProfilesLibraryPath,
newScannerProfilePath,
......
<script>
import { GlButton } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { ERROR_RUN_SCAN, ERROR_MESSAGES } from 'ee/on_demand_scans_form/settings';
import { redirectTo } from '~/lib/utils/url_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import dastProfileRunMutation from '../graphql/dast_profile_run.mutation.graphql';
import ProfilesList from './dast_profiles_list.vue';
import DastScanBranch from './dast_scan_branch.vue';
import ScanSchedule from './dast_scan_schedule.vue';
import ScanTypeBadge from './dast_scan_type_badge.vue';
export default {
components: {
GlButton,
ProfilesList,
DastScanBranch,
ScanSchedule,
ScanTypeBadge,
},
mixins: [glFeatureFlagsMixin()],
props: {
errorMessage: {
type: String,
required: false,
default: '',
},
errorDetails: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
isRunningScan: null,
hasRunScanError: false,
runScanErrors: [],
};
},
computed: {
error() {
if (this.hasRunScanError) {
return {
errorMessage: ERROR_MESSAGES[ERROR_RUN_SCAN],
errorDetails: this.runScanErrors,
};
}
const { errorMessage, errorDetails } = this;
return { errorMessage, errorDetails };
},
},
watch: {
errorMessage() {
this.hasRunScanError = false;
},
},
methods: {
async runScan({ id }) {
this.isRunningScan = id;
this.hasRunScanError = false;
try {
const {
data: {
dastProfileRun: { pipelineUrl, errors },
},
} = await this.$apollo.mutate({
mutation: dastProfileRunMutation,
variables: {
input: {
id,
},
},
});
if (errors.length) {
this.handleRunScanError({ errors });
} else {
redirectTo(pipelineUrl);
}
} catch (error) {
this.handleRunScanError(error);
}
},
handleRunScanError({ exception = null, errors = [] } = {}) {
this.isRunningScan = null;
this.hasRunScanError = true;
this.runScanErrors = errors;
if (exception !== null) {
Sentry.captureException(exception);
}
},
},
};
</script>
<template>
<profiles-list
:error-message="error.errorMessage"
:error-details="error.errorDetails"
v-bind="$attrs"
v-on="$listeners"
>
<template #cell(name)="{ item: { name, branch, editPath } }">
{{ name }}
<dast-scan-branch :branch="branch" :edit-path="editPath" />
</template>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #cell(dastScannerProfile.scanType)="{ value }">
<scan-type-badge :scan-type="value" />
</template>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #cell(dastProfileSchedule)="{ value }">
<scan-schedule :schedule="value || null" />
</template>
<template #actions="{ profile }">
<gl-button
size="small"
data-testid="dast-scan-run-button"
:loading="isRunningScan === profile.id"
:disabled="Boolean(isRunningScan)"
@click="runScan(profile)"
>{{ s__('DastProfiles|Run scan') }}</gl-button
>
</template>
</profiles-list>
</template>
......@@ -10,18 +10,11 @@ export default () => {
}
const {
dataset: {
newDastSavedScanPath,
newDastScannerProfilePath,
newDastSiteProfilePath,
projectFullPath,
timezones,
},
dataset: { newDastScannerProfilePath, newDastSiteProfilePath, projectFullPath, timezones },
} = el;
const props = {
createNewProfilePaths: {
savedScan: newDastSavedScanPath,
scannerProfile: newDastScannerProfilePath,
siteProfile: newDastSiteProfilePath,
},
......
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query DastProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) {
project(fullPath: $fullPath) {
id
dastProfiles(after: $after, before: $before, first: $first, last: $last) {
pageInfo {
...PageInfo
}
edges {
node {
id
name
dastSiteProfile {
id
targetUrl
}
dastScannerProfile {
id
scanType
}
dastProfileSchedule {
id
active
startsAt
timezone
cadence {
unit
duration
}
}
branch {
name
exists
}
editPath
}
}
}
}
}
import DastSavedScansList from 'ee/security_configuration/dast_profiles/components/dast_saved_scans_list.vue';
import DastScannerProfileList from 'ee/security_configuration/dast_profiles/components/dast_scanner_profiles_list.vue';
import DastSiteProfileList from 'ee/security_configuration/dast_profiles/components/dast_site_profiles_list.vue';
import { dastProfilesDeleteResponse } from 'ee/security_configuration/dast_profiles/graphql/cache_utils';
import dastProfileDelete from 'ee/security_configuration/dast_profiles/graphql/dast_profile_delete.mutation.graphql';
import dastProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_profiles.query.graphql';
import dastScannerProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_scanner_profiles.query.graphql';
import dastScannerProfilesDelete from 'ee/security_configuration/dast_profiles/graphql/dast_scanner_profiles_delete.mutation.graphql';
import dastSiteProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql';
......@@ -11,54 +8,6 @@ import dastSiteProfilesDelete from 'ee/security_configuration/dast_profiles/grap
import { s__ } from '~/locale';
export const getProfileSettings = ({ createNewProfilePaths }) => ({
dastProfiles: {
profileType: 'dastProfiles',
tabName: 'saved-scans',
createNewProfilePath: createNewProfilePaths.savedScan,
graphQL: {
query: dastProfilesQuery,
deletion: {
mutation: dastProfileDelete,
optimisticResponse: dastProfilesDeleteResponse({
mutationName: 'dastProfileDelete',
payloadTypeName: 'DastProfileDeletePayload',
}),
},
},
component: DastSavedScansList,
tableFields: [
{
label: s__('DastProfiles|Scan'),
key: 'name',
},
{
label: s__('DastProfiles|Target'),
key: 'dastSiteProfile.targetUrl',
},
{
label: s__('DastProfiles|Scan mode'),
key: 'dastScannerProfile.scanType',
},
{
label: s__('DastProfiles|Schedule'),
key: 'dastProfileSchedule',
},
],
i18n: {
createNewLinkText: s__('DastProfiles|DAST Scan'),
name: s__('DastProfiles|Saved Scans'),
errorMessages: {
fetchNetworkError: s__(
'DastProfiles|Could not fetch saved scans. Please refresh the page, or try again later.',
),
deletionNetworkError: s__(
'DastProfiles|Could not delete saved scan. Please refresh the page, or try again later.',
),
deletionBackendError: s__('DastProfiles|Could not delete saved scans:'),
},
noProfilesMessage: s__('DastProfiles|No scans saved yet'),
},
},
siteProfiles: {
profileType: 'siteProfiles',
tabName: 'site-profiles',
......
......@@ -27,7 +27,7 @@ module Projects::OnDemandScansHelper
def on_demand_scans_form_data(project)
common_data(project).merge({
'default-branch' => project.default_branch,
'profiles-library-path' => project_security_configuration_dast_scans_path(project),
'on-demand-scans-path' => project_on_demand_scans_path(project, anchor: 'saved'),
'scanner-profiles-library-path' => project_security_configuration_dast_scans_path(project, anchor: 'scanner-profiles'),
'site-profiles-library-path' => project_security_configuration_dast_scans_path(project, anchor: 'site-profiles'),
'new-scanner-profile-path' => new_project_security_configuration_dast_scans_dast_scanner_profile_path(project),
......
......@@ -3,7 +3,6 @@
module Projects::Security::DastProfilesHelper
def dast_profiles_list_data(project)
{
'new_dast_saved_scan_path' => new_project_on_demand_scan_path(project),
'new_dast_site_profile_path' => new_project_security_configuration_dast_scans_dast_site_profile_path(project),
'new_dast_scanner_profile_path' => new_project_security_configuration_dast_scans_dast_scanner_profile_path(project),
'project_full_path' => project.path_with_namespace,
......
......@@ -8,7 +8,7 @@ RSpec.describe 'User creates On-demand Scan' do
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project) }
let(:profile_library_path) { project_security_configuration_dast_scans_path(project) }
let(:on_demand_scans_path) { project_on_demand_scans_path(project) }
before_all do
project.add_developer(user)
......@@ -37,12 +37,12 @@ RSpec.describe 'User creates On-demand Scan' do
click_button 'Save scan'
wait_for_requests
expect(current_path).to eq(profile_library_path)
expect(current_path).to eq(on_demand_scans_path)
end
it 'on cancel', :js do
click_button 'Cancel'
expect(current_path).to eq(profile_library_path)
expect(current_path).to eq(on_demand_scans_path)
end
end
......
......@@ -329,34 +329,6 @@ RSpec.describe 'DAST profiles (GraphQL fixtures)' do
end
end
describe 'dast_profiles' do
path = 'security_configuration/dast_profiles/graphql/dast_profiles.query.graphql'
let_it_be(:dast_profiles) do
[
create(:dast_profile, project: project),
create(:dast_profile, project: project)
]
end
before do
dast_profiles.first.branch_name = SecureRandom.hex
dast_profiles.first.save!(validate: false)
end
it "graphql/#{path}.json" do
query = get_graphql_query_as_string(path, ee: true)
post_graphql(query, current_user: current_user, variables: {
fullPath: project.full_path,
first: 20
})
expect_graphql_errors_to_be_empty
expect(graphql_data_at(:project, :dastProfiles, :edges)).to have_attributes(size: dast_profiles.length)
end
end
describe 'dast_site_validations' do
context 'failed site validations' do
path = 'security_configuration/dast_profiles/graphql/dast_failed_site_validations.query.graphql'
......@@ -405,6 +377,11 @@ RSpec.describe 'DAST profiles (GraphQL fixtures)' do
create_list(:dast_profile, 3, project: project)
end
before do
dast_profiles.last.branch_name = SecureRandom.hex
dast_profiles.last.save!(validate: false)
end
it "graphql/#{path}.json" do
query = get_graphql_query_as_string(path, ee: true)
......
......@@ -34,6 +34,7 @@ describe('OnDemandScans', () => {
running: 3,
finished: 9,
scheduled: 5,
saved: 3,
};
const emptyInitialPipelineCounts = Object.fromEntries(PIPELINE_TABS_KEYS.map((key) => [key, 0]));
......@@ -53,6 +54,15 @@ describe('OnDemandScans', () => {
return createMockApollo([[onDemandScansCounts, requestHandler]]);
};
// Assertions
const expectTabsToBeRendered = () => {
expect(findAllTab().exists()).toBe(true);
expect(findRunningTab().exists()).toBe(true);
expect(findFinishedTab().exists()).toBe(true);
expect(findScheduledTab().exists()).toBe(true);
expect(findSavedTab().exists()).toBe(true);
};
const createComponent = (options = {}) => {
wrapper = shallowMountExtended(
OnDemandScans,
......@@ -117,6 +127,28 @@ describe('OnDemandScans', () => {
expect(findEmptyState().exists()).toBe(false);
});
describe('non-empty states', () => {
it.each`
description | counts
${'scheduled'} | ${{ scheduled: 0 }}
${'saved'} | ${{ saved: 0 }}
`(
'shows the tabs when there are no pipelines but there are $description scans',
({ counts }) => {
createComponent({
propsData: {
initialOnDemandScanCounts: {
all: 0,
...counts,
},
},
});
expectTabsToBeRendered();
},
);
});
describe('when there is data', () => {
beforeEach(() => {
createComponent();
......@@ -139,11 +171,7 @@ describe('OnDemandScans', () => {
});
it('renders the tabs', () => {
expect(findAllTab().exists()).toBe(true);
expect(findRunningTab().exists()).toBe(true);
expect(findFinishedTab().exists()).toBe(true);
expect(findScheduledTab().exists()).toBe(true);
expect(findSavedTab().exists()).toBe(true);
expectTabsToBeRendered();
});
it('sets the initial route to /all', () => {
......
......@@ -7,8 +7,8 @@ import SavedTab from 'ee/on_demand_scans/components/tabs/saved.vue';
import BaseTab from 'ee/on_demand_scans/components/tabs/base_tab.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import dastProfilesQuery from 'ee/on_demand_scans/graphql/dast_profiles.query.graphql';
import dastProfileRunMutation from 'ee/security_configuration/dast_profiles/graphql/dast_profile_run.mutation.graphql';
import dastProfileDeleteMutation from 'ee/security_configuration/dast_profiles/graphql/dast_profile_delete.mutation.graphql';
import dastProfileRunMutation from 'ee/on_demand_scans/graphql/dast_profile_run.mutation.graphql';
import dastProfileDeleteMutation from 'ee/on_demand_scans/graphql/dast_profile_delete.mutation.graphql';
import { createRouter } from 'ee/on_demand_scans/router';
import { SAVED_TAB_TABLE_FIELDS, LEARN_MORE_TEXT } from 'ee/on_demand_scans/constants';
import { s__ } from '~/locale';
......
......@@ -31,7 +31,7 @@ const dastSiteValidationDocsPath = '/application_security/dast/index#dast-site-v
const projectPath = 'group/project';
const defaultBranch = 'main';
const selectedBranch = 'some-other-branch';
const profilesLibraryPath = '/security/configuration/dast_scans';
const onDemandScansPath = '/on_demand_scans#saved';
const scannerProfilesLibraryPath = '/security/configuration/dast_scans#scanner-profiles';
const siteProfilesLibraryPath = '/security/configuration/dast_scans#site-profiles';
const newScannerProfilePath = '/security/configuration/dast_scans/dast_scanner_profile/new';
......@@ -162,7 +162,7 @@ describe('OnDemandScansForm', () => {
mocks: defaultMocks,
provide: {
projectPath,
profilesLibraryPath,
onDemandScansPath,
scannerProfilesLibraryPath,
siteProfilesLibraryPath,
newScannerProfilePath,
......@@ -357,7 +357,7 @@ describe('OnDemandScansForm', () => {
describe.each`
action | actionFunction | submitButtonLoading | saveButtonLoading | runAfter | redirectPath
${'submit'} | ${submitForm} | ${true} | ${false} | ${true} | ${pipelineUrl}
${'save'} | ${saveScan} | ${false} | ${true} | ${false} | ${profilesLibraryPath}
${'save'} | ${saveScan} | ${false} | ${true} | ${false} | ${onDemandScansPath}
`(
'on $action',
({ actionFunction, submitButtonLoading, saveButtonLoading, runAfter, redirectPath }) => {
......@@ -517,7 +517,7 @@ describe('OnDemandScansForm', () => {
itClearsLocalStorage();
it('redirects to profiles library', () => {
expect(redirectTo).toHaveBeenCalledWith(profilesLibraryPath);
expect(redirectTo).toHaveBeenCalledWith(onDemandScansPath);
});
});
......
......@@ -7,7 +7,6 @@ import DastProfiles from 'ee/security_configuration/dast_profiles/components/das
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
const TEST_NEW_DAST_SAVED_SCAN_PATH = '/-/on_demand_scans/new';
const TEST_NEW_DAST_SCANNER_PROFILE_PATH = '/-/on_demand_scans/scanner_profiles/new';
const TEST_NEW_DAST_SITE_PROFILE_PATH = '/-/on_demand_scans/site_profiles/new';
const TEST_PROJECT_FULL_PATH = '/namespace/project';
......@@ -22,7 +21,6 @@ describe('EE - DastProfiles', () => {
const createComponentFactory = (mountFn = shallowMount) => (options = {}) => {
const defaultProps = {
createNewProfilePaths: {
savedScan: TEST_NEW_DAST_SAVED_SCAN_PATH,
scannerProfile: TEST_NEW_DAST_SCANNER_PROFILE_PATH,
siteProfile: TEST_NEW_DAST_SITE_PROFILE_PATH,
},
......@@ -104,7 +102,6 @@ describe('EE - DastProfiles', () => {
it.each`
itemName | href
${'DAST Scan'} | ${TEST_NEW_DAST_SAVED_SCAN_PATH}
${'Site Profile'} | ${TEST_NEW_DAST_SITE_PROFILE_PATH}
${'Scanner Profile'} | ${TEST_NEW_DAST_SCANNER_PROFILE_PATH}
`('shows a "$itemName" dropdown item that links to $href', ({ itemName, href }) => {
......@@ -128,8 +125,7 @@ describe('EE - DastProfiles', () => {
it.each`
tabName | shouldBeSelectedByDefault
${'Saved Scans'} | ${true}
${'Site Profiles'} | ${false}
${'Site Profiles'} | ${true}
${'Scanner Profiles'} | ${false}
`(
'shows a "$tabName" tab which has "selected" set to "$shouldBeSelectedByDefault"',
......@@ -146,9 +142,8 @@ describe('EE - DastProfiles', () => {
describe.each`
tabName | index | givenLocationHash
${'Saved Scans'} | ${0} | ${'#saved-scans'}
${'Site Profiles'} | ${1} | ${'#site-profiles'}
${'Scanner Profiles'} | ${2} | ${'#scanner-profiles'}
${'Site Profiles'} | ${0} | ${'#site-profiles'}
${'Scanner Profiles'} | ${1} | ${'#scanner-profiles'}
`('with location hash set to "$givenLocationHash"', ({ tabName, index, givenLocationHash }) => {
beforeEach(() => {
setWindowLocation(givenLocationHash);
......@@ -177,9 +172,6 @@ describe('EE - DastProfiles', () => {
it.each`
profileType | key | givenData | expectedValue | exposedAsProp
${'dastProfiles'} | ${'errorMessage'} | ${{ errorMessage: 'foo' }} | ${'foo'} | ${true}
${'dastProfiles'} | ${'errorDetails'} | ${{ errorDetails: ['foo'] }} | ${['foo']} | ${true}
${'dastProfiles'} | ${'has-more-profiles-to-load'} | ${{ pageInfo: { hasNextPage: true } }} | ${'true'} | ${false}
${'siteProfiles'} | ${'error-message'} | ${{ errorMessage: 'foo' }} | ${'foo'} | ${false}
${'siteProfiles'} | ${'error-details'} | ${{ errorDetails: ['foo'] }} | ${'foo'} | ${false}
${'siteProfiles'} | ${'has-more-profiles-to-load'} | ${{ pageInfo: { hasNextPage: true } }} | ${'true'} | ${false}
......@@ -204,7 +196,6 @@ describe('EE - DastProfiles', () => {
describe.each`
description | profileType
${'Saved Scans List'} | ${'dastProfiles'}
${'Site Profiles List'} | ${'siteProfiles'}
${'Scanner Profiles List'} | ${'scannerProfiles'}
`('$description', ({ profileType }) => {
......
import { mount, shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { ERROR_RUN_SCAN, ERROR_MESSAGES } from 'ee/on_demand_scans_form/settings';
import ProfilesList from 'ee/security_configuration/dast_profiles/components/dast_profiles_list.vue';
import Component from 'ee/security_configuration/dast_profiles/components/dast_saved_scans_list.vue';
import DastScanBranch from 'ee/security_configuration/dast_profiles/components/dast_scan_branch.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
import { savedScans } from '../mocks/mock_data';
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
describe('EE - DastSavedScansList', () => {
let wrapper;
const defaultProps = {
profiles: [],
tableLabel: 'Saved scans',
fields: [
{ key: 'name' },
{ key: 'dastSiteProfile.targetUrl' },
{ key: 'dastScannerProfile.scanType' },
],
profilesPerPage: 10,
errorMessage: '',
errorDetails: [],
noProfilesMessage: 'No scans saved yet',
fullPath: '/namespace/project',
hasMoreProfilesToLoad: false,
isLoading: false,
};
const wrapperFactory = (mountFn = shallowMount) => (options = {}) => {
wrapper = extendedWrapper(
mountFn(
Component,
merge(
{
propsData: defaultProps,
},
options,
),
),
);
};
const createFullComponent = wrapperFactory(mount);
const findProfileList = () => wrapper.findComponent(ProfilesList);
afterEach(() => {
wrapper.destroy();
});
it('renders profile list properly', () => {
createFullComponent({
propsData: { profiles: savedScans },
});
expect(findProfileList().exists()).toBe(true);
});
it('renders branch information for each profile', () => {
createFullComponent({
propsData: { profiles: savedScans },
});
expect(wrapper.findAll(DastScanBranch)).toHaveLength(savedScans.length);
});
it('passes down the props properly', () => {
createFullComponent();
expect(findProfileList().props()).toEqual(defaultProps);
});
it('sets listeners on profile list component', () => {
const inputHandler = jest.fn();
createFullComponent({
listeners: {
input: inputHandler,
},
});
findProfileList().vm.$emit('input');
expect(inputHandler).toHaveBeenCalled();
});
describe('run scan', () => {
const pipelineUrl = '/pipeline/url';
const successHandler = jest.fn().mockResolvedValue({
data: {
dastProfileRun: {
pipelineUrl,
errors: [],
},
},
});
it('puts the clicked button in the loading state and disabled other buttons', async () => {
createFullComponent({
propsData: { profiles: savedScans },
mocks: {
$apollo: {
mutate: successHandler,
},
},
});
const buttons = wrapper.findAll('[data-testid="dast-scan-run-button"]');
expect(buttons.at(0).props('loading')).toBe(false);
expect(buttons.at(1).props('disabled')).toBe(false);
await buttons.at(0).trigger('click');
expect(buttons.at(0).props('loading')).toBe(true);
expect(buttons.at(1).props('disabled')).toBe(true);
});
it('redirects to the running pipeline page on success', async () => {
createFullComponent({
propsData: { profiles: savedScans },
mocks: {
$apollo: {
mutate: successHandler,
},
},
});
wrapper.findByTestId('dast-scan-run-button').trigger('click');
await waitForPromises();
expect(redirectTo).toHaveBeenCalledWith(pipelineUrl);
expect(createFlash).not.toHaveBeenCalled();
});
it('passes the error message down to the list on failure but does not block errors passed by the parent', async () => {
const initialErrorMessage = 'Initial error message';
const finalErrorMessage = 'Final error message';
createFullComponent({
propsData: {
profiles: savedScans,
errorMessage: initialErrorMessage,
},
mocks: {
$apollo: {
mutate: jest.fn().mockRejectedValue(),
},
},
});
const profilesList = findProfileList();
expect(profilesList.props('errorMessage')).toBe(initialErrorMessage);
wrapper.findByTestId('dast-scan-run-button').trigger('click');
await waitForPromises();
expect(profilesList.props('errorMessage')).toBe(ERROR_MESSAGES[ERROR_RUN_SCAN]);
expect(redirectTo).not.toHaveBeenCalled();
await wrapper.setProps({ errorMessage: finalErrorMessage });
expect(profilesList.props('errorMessage')).toBe(finalErrorMessage);
});
it('passes the error message and details down to the list if the API responds with errors-as-data', async () => {
const errors = ['error-as-data'];
createFullComponent({
propsData: { profiles: savedScans },
mocks: {
$apollo: {
mutate: jest.fn().mockResolvedValue({
data: {
dastProfileRun: {
pipelineUrl: null,
errors,
},
},
}),
},
},
});
wrapper.findByTestId('dast-scan-run-button').trigger('click');
await waitForPromises();
expect(findProfileList().props('errorMessage')).toBe(ERROR_MESSAGES[ERROR_RUN_SCAN]);
expect(findProfileList().props('errorDetails')).toBe(errors);
expect(redirectTo).not.toHaveBeenCalled();
});
});
});
import { GlIcon, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import profilesFixtures from 'test_fixtures/graphql/on_demand_scans/graphql/dast_profiles.query.graphql.json';
import DastScanBranch from 'ee/security_configuration/dast_profiles/components/dast_scan_branch.vue';
import { savedScans } from '../mocks/mock_data';
const [scanWithExistingBranch, scanWithInexistingBranch] = savedScans;
const [
scanWithInexistingBranch,
scanWithExistingBranch,
] = profilesFixtures.data.project.pipelines.nodes;
describe('EE - DastSavedScansList', () => {
let wrapper;
......@@ -31,6 +34,7 @@ describe('EE - DastSavedScansList', () => {
const { branch, editPath } = scanWithInexistingBranch;
createWrapper({ branch, editPath });
});
it('renders a warning message', () => {
expect(wrapper.text()).toContain('Branch missing');
expect(wrapper.find(GlIcon).props('name')).toBe('warning');
......
import siteProfilesFixture from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql.basic.json';
import scannerProfilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_scanner_profiles.query.graphql.basic.json';
import profilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_profiles.query.graphql.json';
import policySiteProfilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql.from_policies.json';
import policyScannerProfilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_scanner_profiles.query.graphql.from_policies.json';
import dastFailedSiteValidationsFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_failed_site_validations.query.graphql.json';
......@@ -28,7 +27,5 @@ export const scannerProfiles = scannerProfilesFixtures.data.project.scannerProfi
({ node }) => node,
);
export const savedScans = profilesFixtures.data.project.dastProfiles.edges.map(({ node }) => node);
export const failedSiteValidations =
dastFailedSiteValidationsFixtures.data.project.validations.nodes;
......@@ -52,7 +52,7 @@ RSpec.describe Projects::OnDemandScansHelper do
expect(helper.on_demand_scans_form_data(project)).to match(
'default-branch' => "default-branch",
'project-path' => "foo/bar",
'profiles-library-path' => "/#{project.full_path}/-/security/configuration/dast_scans",
'on-demand-scans-path' => "/#{project.full_path}/-/on_demand_scans#saved",
'scanner-profiles-library-path' => "/#{project.full_path}/-/security/configuration/dast_scans#scanner-profiles",
'site-profiles-library-path' => "/#{project.full_path}/-/security/configuration/dast_scans#site-profiles",
'new-scanner-profile-path' => "/#{project.full_path}/-/security/configuration/dast_scans/dast_scanner_profiles/new",
......
......@@ -15,7 +15,6 @@ RSpec.describe Projects::Security::DastProfilesHelper do
it 'returns proper data' do
expect(helper.dast_profiles_list_data(project)).to eq(
{
'new_dast_saved_scan_path' => "/#{project.full_path}/-/on_demand_scans/new",
'new_dast_site_profile_path' => "/#{project.full_path}/-/security/configuration/dast_scans/dast_site_profiles/new",
'new_dast_scanner_profile_path' => "/#{project.full_path}/-/security/configuration/dast_scans/dast_scanner_profiles/new",
'project_full_path' => "foo/bar",
......
......@@ -12,10 +12,6 @@ RSpec.describe "projects/security/dast_profiles/show", type: :view do
expect(rendered).to have_selector('.js-dast-profiles')
end
it 'passes new dast saved scan path' do
expect(rendered).to include '/on_demand_scans/new'
end
it 'passes new dast site profile path' do
expect(rendered).to include '/security/configuration/dast_scans/dast_site_profiles/new'
end
......
......@@ -10859,12 +10859,6 @@ msgstr ""
msgid "DastProfiles|Could not create the site profile. Please try again."
msgstr ""
msgid "DastProfiles|Could not delete saved scan. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not delete saved scans:"
msgstr ""
msgid "DastProfiles|Could not delete scanner profile. Please refresh the page, or try again later."
msgstr ""
......@@ -10877,9 +10871,6 @@ msgstr ""
msgid "DastProfiles|Could not delete site profiles:"
msgstr ""
msgid "DastProfiles|Could not fetch saved scans. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
......@@ -10892,9 +10883,6 @@ msgstr ""
msgid "DastProfiles|Could not update the site profile. Please try again."
msgstr ""
msgid "DastProfiles|DAST Scan"
msgstr ""
msgid "DastProfiles|Debug messages"
msgstr ""
......@@ -10967,9 +10955,6 @@ msgstr ""
msgid "DastProfiles|No scanner profiles created yet"
msgstr ""
msgid "DastProfiles|No scans saved yet"
msgstr ""
msgid "DastProfiles|No site profiles created yet"
msgstr ""
......@@ -10997,9 +10982,6 @@ msgstr ""
msgid "DastProfiles|Rest API"
msgstr ""
msgid "DastProfiles|Run scan"
msgstr ""
msgid "DastProfiles|Run the AJAX spider, in addition to the traditional spider, to crawl the target site."
msgstr ""
......@@ -11009,12 +10991,6 @@ msgstr ""
msgid "DastProfiles|Save profile"
msgstr ""
msgid "DastProfiles|Saved Scans"
msgstr ""
msgid "DastProfiles|Scan"
msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
......@@ -11027,9 +11003,6 @@ msgstr ""
msgid "DastProfiles|Scanner name"
msgstr ""
msgid "DastProfiles|Schedule"
msgstr ""
msgid "DastProfiles|Select branch"
msgstr ""
......@@ -11051,9 +11024,6 @@ msgstr ""
msgid "DastProfiles|Spider timeout"
msgstr ""
msgid "DastProfiles|Target"
msgstr ""
msgid "DastProfiles|Target URL"
msgstr ""
......
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