Commit 16d095e2 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch...

Merge branch '241362-dast-scanner-profile-library-implementation-iteration-1-add-new-profile-link-frontend' into 'master'

DAST Scanner Profile Library: Add new-profile link

See merge request gitlab-org/gitlab!40469
parents 5ec10aa4 16e54231
<script>
import * as Sentry from '@sentry/browser';
import { GlButton, GlTab, GlTabs } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlTab, GlTabs } from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ProfilesList from './dast_profiles_list.vue';
import dastSiteProfilesQuery from '../graphql/dast_site_profiles.query.graphql';
import dastSiteProfilesDelete from '../graphql/dast_site_profiles_delete.mutation.graphql';
import * as cacheUtils from '../graphql/cache_utils';
import { getProfileSettings } from '../settings/profiles';
export default {
components: {
GlButton,
GlDropdown,
GlDropdownItem,
GlTab,
GlTabs,
ProfilesList,
},
mixins: [glFeatureFlagMixin()],
props: {
newDastSiteProfilePath: {
type: String,
createNewProfilePaths: {
type: Object,
required: true,
validator: ({ scannerProfile, siteProfile }) =>
Boolean(scannerProfile) && Boolean(siteProfile),
},
projectFullPath: {
type: String,
......@@ -60,6 +66,16 @@ export default {
},
},
computed: {
profileSettings() {
const { glFeatures, createNewProfilePaths } = this;
return getProfileSettings(
{
createNewProfilePaths,
},
glFeatures,
);
},
hasMoreSiteProfiles() {
return this.siteProfilesPageInfo.hasNextPage;
},
......@@ -152,6 +168,11 @@ export default {
},
profilesPerPage: 10,
i18n: {
heading: s__('DastProfiles|Manage Profiles'),
newProfileDropdownLabel: s__('DastProfiles|New Profile'),
subHeading: s__(
'DastProfiles|Save commonly used configurations for target sites and scan specifications as profiles. Use these with an on-demand scan.',
),
errorMessages: {
fetchNetworkError: s__(
'DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later.',
......@@ -170,23 +191,25 @@ export default {
<header>
<div class="gl-display-flex gl-align-items-center gl-pt-6 gl-pb-4">
<h2 class="my-0">
{{ s__('DastProfiles|Manage Profiles') }}
{{ $options.i18n.heading }}
</h2>
<gl-button
:href="newDastSiteProfilePath"
category="primary"
<gl-dropdown
:text="$options.i18n.newProfileDropdownLabel"
variant="success"
right
class="gl-ml-auto"
>
{{ s__('DastProfiles|New Site Profile') }}
</gl-button>
<gl-dropdown-item
v-for="{ i18n, createNewProfilePath, key } in profileSettings"
:key="key"
:href="createNewProfilePath"
>
{{ i18n.title }}
</gl-dropdown-item>
</gl-dropdown>
</div>
<p>
{{
s__(
'DastProfiles|Save commonly used configurations for target sites and scan specifications as profiles. Use these with an on-demand scan.',
)
}}
{{ $options.i18n.subHeading }}
</p>
</header>
......
......@@ -10,11 +10,14 @@ export default () => {
}
const {
dataset: { newDastSiteProfilePath, projectFullPath },
dataset: { newDastScannerProfilePath, newDastSiteProfilePath, projectFullPath },
} = el;
const props = {
newDastSiteProfilePath,
createNewProfilePaths: {
scannerProfile: newDastScannerProfilePath,
siteProfile: newDastSiteProfilePath,
},
projectFullPath,
};
......
import { s__ } from '~/locale';
const hasNoFeatureFlagOrIsEnabled = glFeatures => ([, { featureFlag }]) => {
if (!featureFlag) {
return true;
}
return Boolean(glFeatures[featureFlag]);
};
export const getProfileSettings = ({ createNewProfilePaths }, glFeatures) => {
const settings = {
siteProfiles: {
key: 'siteProfiles',
createNewProfilePath: createNewProfilePaths.siteProfile,
i18n: {
title: s__('DastProfiles|Site Profile'),
},
},
scannerProfiles: {
key: 'scannerProfiles',
createNewProfilePath: createNewProfilePaths.scannerProfile,
featureFlag: 'securityOnDemandScansScannerProfiles',
i18n: {
title: s__('DastProfiles|Scanner Profile'),
},
},
};
return Object.fromEntries(
Object.entries(settings).filter(hasNoFeatureFlagOrIsEnabled(glFeatures)),
);
};
......@@ -3,6 +3,9 @@
module Projects
class DastProfilesController < Projects::ApplicationController
before_action :authorize_read_on_demand_scans!
before_action do
push_frontend_feature_flag(:security_on_demand_scans_scanner_profiles, project, default_enabled: false)
end
def index
end
......
......@@ -2,5 +2,6 @@
- breadcrumb_title s_('DastProfiles|Manage profiles')
- page_title s_('DastProfiles|Manage profiles')
.js-dast-profiles{ data: { new_dast_site_profile_path: new_namespace_project_dast_site_profile_path(namespace_id: @project.namespace, project_id: @project.path),
project_full_path: @project.path_with_namespace } }
.js-dast-profiles{ data: { new_dast_site_profile_path: new_project_dast_site_profile_path(@project),
new_dast_scanner_profile_path: new_project_dast_scanner_profile_path(@project),
project_full_path: @project.path_with_namespace } }
---
title: 'DAST Scanner Profile Library: change new-profile button to dropdown'
merge_request: 40469
author:
type: changed
import { mount, shallowMount } from '@vue/test-utils';
import { within } from '@testing-library/dom';
import { merge } from 'lodash';
import { GlDropdown } from '@gitlab/ui';
import DastProfiles from 'ee/dast_profiles/components/dast_profiles.vue';
import DastProfilesList from 'ee/dast_profiles/components/dast_profiles_list.vue';
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';
......@@ -12,7 +14,10 @@ describe('EE - DastProfiles', () => {
const createComponentFactory = (mountFn = shallowMount) => (options = {}) => {
const defaultProps = {
newDastSiteProfilePath: TEST_NEW_DAST_SITE_PROFILE_PATH,
createNewProfilePaths: {
scannerProfile: TEST_NEW_DAST_SCANNER_PROFILE_PATH,
siteProfile: TEST_NEW_DAST_SITE_PROFILE_PATH,
},
projectFullPath: TEST_PROJECT_FULL_PATH,
};
......@@ -43,28 +48,68 @@ describe('EE - DastProfiles', () => {
const createComponent = createComponentFactory();
const createFullComponent = createComponentFactory(mount);
const withFeatureFlag = (featureFlagName, { enabled, disabled }) => {
it.each([true, false])(`with ${featureFlagName} enabled: "%s"`, featureFlagStatus => {
createComponent({
provide: {
glFeatures: {
[featureFlagName]: featureFlagStatus,
},
},
});
if (featureFlagStatus) {
enabled();
} else {
disabled();
}
});
};
const withinComponent = () => within(wrapper.element);
const getSiteProfilesComponent = () => wrapper.find(DastProfilesList);
const getDropdownComponent = () => wrapper.find(GlDropdown);
const getSiteProfilesDropdownItem = text =>
within(getDropdownComponent().element).queryByText(text);
afterEach(() => {
wrapper.destroy();
});
describe('header', () => {
beforeEach(() => {
it('shows a heading that describes the purpose of the page', () => {
createFullComponent();
});
it('shows a heading that describes the purpose of the page', () => {
const heading = withinComponent().getByRole('heading', { name: /manage profiles/i });
expect(heading).not.toBe(null);
});
it(`shows a "New Site Profile" anchor that links to ${TEST_NEW_DAST_SITE_PROFILE_PATH}`, () => {
const newProfileButton = withinComponent().getByRole('link', { name: /new site profile/i });
it('has a "New Profile" dropdown menu', () => {
createComponent();
expect(getDropdownComponent().props('text')).toBe('New Profile');
});
it(`shows a "Site Profile" dropdown item that links to ${TEST_NEW_DAST_SITE_PROFILE_PATH}`, () => {
createComponent();
expect(newProfileButton.getAttribute('href')).toBe(TEST_NEW_DAST_SITE_PROFILE_PATH);
expect(getSiteProfilesDropdownItem('Site Profile').getAttribute('href')).toBe(
TEST_NEW_DAST_SITE_PROFILE_PATH,
);
});
describe(`shows a "Scanner Profile" dropdown item that links to ${TEST_NEW_DAST_SCANNER_PROFILE_PATH}`, () => {
withFeatureFlag('securityOnDemandScansScannerProfiles', {
enabled: () => {
expect(getSiteProfilesDropdownItem('Scanner Profile').getAttribute('href')).toBe(
TEST_NEW_DAST_SCANNER_PROFILE_PATH,
);
},
disabled: () => {
expect(getSiteProfilesDropdownItem('Scanner Profile')).toBe(null);
},
});
});
});
......
......@@ -16,6 +16,10 @@ RSpec.describe "projects/dast_profiles/index", type: :view do
expect(rendered).to include '/on_demand_scans/profiles/dast_site_profiles/new'
end
it 'passes new dast scanner profile path' do
expect(rendered).to include '/on_demand_scans/profiles/dast_scanner_profiles/new'
end
it 'passes project\'s full path' do
expect(rendered).to include @project.path_with_namespace
end
......
......@@ -7749,10 +7749,10 @@ msgstr ""
msgid "DastProfiles|Manage profiles"
msgstr ""
msgid "DastProfiles|New Scanner Profile"
msgid "DastProfiles|New Profile"
msgstr ""
msgid "DastProfiles|New Site Profile"
msgid "DastProfiles|New Scanner Profile"
msgstr ""
msgid "DastProfiles|New scanner profile"
......@@ -7776,6 +7776,12 @@ msgstr ""
msgid "DastProfiles|Save profile"
msgstr ""
msgid "DastProfiles|Scanner Profile"
msgstr ""
msgid "DastProfiles|Site Profile"
msgstr ""
msgid "DastProfiles|Site Profiles"
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