Commit 61601329 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch '341382-dast-view-scans-remove-saved-scans-tab' into 'master'

Remove "Saved scans" tab from DAST configuration

See merge request gitlab-org/gitlab!77288
parents f3a1cbfc 011bf15a
......@@ -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
......
......@@ -10877,12 +10877,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 ""
......@@ -10895,9 +10889,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 ""
......@@ -10910,9 +10901,6 @@ msgstr ""
msgid "DastProfiles|Could not update the site profile. Please try again."
msgstr ""
msgid "DastProfiles|DAST Scan"
msgstr ""
msgid "DastProfiles|Debug messages"
msgstr ""
......@@ -10985,9 +10973,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 ""
......@@ -11015,9 +11000,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 ""
......@@ -11027,12 +11009,6 @@ msgstr ""
msgid "DastProfiles|Save profile"
msgstr ""
msgid "DastProfiles|Saved Scans"
msgstr ""
msgid "DastProfiles|Scan"
msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
......@@ -11045,9 +11021,6 @@ msgstr ""
msgid "DastProfiles|Scanner name"
msgstr ""
msgid "DastProfiles|Schedule"
msgstr ""
msgid "DastProfiles|Select branch"
msgstr ""
......@@ -11069,9 +11042,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