Commit d009cbad authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 7c42b1e0 3feae782
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { SIDEBAR_COLLAPSED_CLASS } from './contextual_sidebar';
const isRefactoring = document.body.classList.contains('sidebar-refactoring');
const HIDE_INTERVAL_TIMEOUT = 300;
const COLLAPSED_PANEL_WIDTH = isRefactoring ? 48 : 50;
const COLLAPSED_PANEL_WIDTH = 48;
const IS_OVER_CLASS = 'is-over';
const IS_ABOVE_CLASS = 'is-above';
const IS_SHOWING_FLY_OUT_CLASS = 'is-showing-fly-out';
......@@ -89,12 +88,12 @@ export const moveSubItemsToPosition = (el, subItems) => {
const boundingRect = el.getBoundingClientRect();
const left = sidebar ? sidebar.offsetWidth : COLLAPSED_PANEL_WIDTH;
let top = calculateTop(boundingRect, subItems.offsetHeight);
if (isRefactoring && hasSubItems) {
if (hasSubItems) {
top -= header.offsetHeight;
} else if (isRefactoring) {
} else {
top = boundingRect.top;
}
const isAbove = top < boundingRect.top;
const isAbove = top <= boundingRect.top;
subItems.classList.add('fly-out-list');
subItems.style.transform = `translate3d(${left}px, ${Math.floor(top) - getHeaderHeight()}px, 0)`; // eslint-disable-line no-param-reassign
......
import { initStaticSecurityConfiguration } from '~/security_configuration';
import { initCESecurityConfiguration } from '~/security_configuration';
initStaticSecurityConfiguration(document.querySelector('#js-security-configuration-static'));
initCESecurityConfiguration(document.querySelector('#js-security-configuration-static'));
......@@ -112,6 +112,7 @@ export default {
<feature-card
v-for="feature in augmentedSecurityFeatures"
:key="feature.type"
data-testid="security-testing-card"
:feature="feature"
class="gl-mb-6"
/>
......
......@@ -7,11 +7,7 @@ import { securityFeatures, complianceFeatures } from './components/constants';
import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue';
import { augmentFeatures } from './utils';
export const initStaticSecurityConfiguration = (el) => {
if (!el) {
return null;
}
export const initRedesignedSecurityConfiguration = (el) => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
......@@ -26,33 +22,50 @@ export const initStaticSecurityConfiguration = (el) => {
gitlabCiHistoryPath,
} = el.dataset;
if (gon.features.securityConfigurationRedesign) {
const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
securityFeatures,
complianceFeatures,
features ? JSON.parse(features) : [],
);
const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
securityFeatures,
complianceFeatures,
features ? JSON.parse(features) : [],
);
return new Vue({
el,
apolloProvider,
provide: {
projectPath,
upgradePath,
},
render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, {
props: {
augmentedComplianceFeatures,
augmentedSecurityFeatures,
latestPipelinePath,
gitlabCiHistoryPath,
...parseBooleanDataAttributes(el, ['gitlabCiPresent']),
},
});
},
});
};
export const initCESecurityConfiguration = (el) => {
if (!el) {
return null;
}
return new Vue({
el,
apolloProvider,
provide: {
projectPath,
upgradePath,
},
render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, {
props: {
augmentedComplianceFeatures,
augmentedSecurityFeatures,
latestPipelinePath,
gitlabCiHistoryPath,
...parseBooleanDataAttributes(el, ['gitlabCiPresent']),
},
});
},
});
if (gon.features?.securityConfigurationRedesign) {
return initRedesignedSecurityConfiguration(el);
}
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
const { projectPath, upgradePath } = el.dataset;
return new Vue({
el,
apolloProvider,
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => {
const featuresByType = features.reduce((acc, feature) => {
acc[feature.type] = feature;
acc[feature.type] = convertObjectPropsToCamelCase(feature, { deep: true });
return acc;
}, {});
......
......@@ -74,6 +74,7 @@ export default {
<template>
<gl-button
v-if="!feature.configured"
data-testid="configure-via-mr-button"
:loading="isLoading"
:variant="variant"
:category="category"
......
......@@ -39,7 +39,7 @@
@import 'framework/selects';
@import 'framework/sidebar';
@import 'framework/contextual_sidebar_header';
@import 'framework/contextual_sidebar_refactoring/contextual_sidebar';
@import 'framework/contextual_sidebar';
@import 'framework/tables';
@import 'framework/notes';
@import 'framework/tabs';
......
//
// VARIABLES
//
$top-level-item-color: $purple-900;
//
// TEMPORARY OVERRIDES
// Needed while we serve both *_base and *_variant stylesheets
// TODO: These have to be removed during the ':sidebar_refactor' flag rollout
//
&.gl-dark .nav-sidebar li.active {
box-shadow: none;
}
&.gl-dark .nav-sidebar .sidebar-sub-level-items {
box-shadow: none;
border: 1px solid $border-color;
}
&.gl-dark .sidebar-top-level-items .context-header a .avatar-container.rect-avatar .avatar.s32 {
color: $white;
}
&.gl-dark .nav-sidebar li a,
&.gl-dark .toggle-sidebar-button .collapse-text,
&.gl-dark .toggle-sidebar-button .icon-chevron-double-lg-left,
&.gl-dark .toggle-sidebar-button .icon-chevron-double-lg-right,
&.gl-dark .sidebar-top-level-items .context-header a .sidebar-context-title,
&.gl-dark .nav-sidebar-inner-scroll > div.context-header a .sidebar-context-title,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a:hover,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item.active a,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
color: $gray-darkest;
}
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item a:hover,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item.active a,
&.gl-dark .nav-sidebar a.has-sub-items + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
@include gl-mt-0;
}
&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item a,
&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item a:hover,
&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item.active a,
&.gl-dark .nav-sidebar a:not(.has-sub-items) + .sidebar-sub-level-items .fly-out-top-item .fly-out-top-item-container {
background: $white;
color: $gray-darkest;
&::before {
border-right-color: $white;
}
}
&.gl-dark .nav-sidebar .sidebar-sub-level-items {
background-color: $white;
}
&.ui-indigo .nav-sidebar li.active:not(.fly-out-top-item) > a {
color: $top-level-item-color;
}
&.ui-indigo .nav-sidebar li.active .nav-icon-container svg {
fill: $top-level-item-color;
}
.nav-sidebar {
box-shadow: none;
li.active {
background-color: transparent;
box-shadow: none !important; // TODO: This should be updated in `theme_helper.scss` together with ':sidebar_refactor' rollout
}
}
//
// MIXINS
//
......@@ -112,7 +35,6 @@ $top-level-item-color: $purple-900;
.icon-chevron-double-lg-left {
@include gl-rotate-180;
@include gl-display-block; // TODO: shouldn't be needed after the flag roll out
@include gl-m-0;
}
}
......@@ -219,15 +141,10 @@ $top-level-item-color: $purple-900;
.avatar.s32 {
@extend .rect-avatar.s32;
//color: $gray-900;
box-shadow: $avatar-box-shadow;
}
}
}
.sidebar-context-title {
color: $top-level-item-color;
}
}
@mixin top-level-item {
......@@ -258,8 +175,6 @@ $top-level-item-color: $purple-900;
@include gl-cursor-default;
@include gl-pointer-events-none;
@include gl-font-sm;
background-color: $purple-900;
color: $white;
@if $has-sub-items {
@include gl-mt-0;
......@@ -269,7 +184,8 @@ $top-level-item-color: $purple-900;
@include gl-my-n2;
@include gl-mt-0;
@include gl-relative;
background-color: $black;
@include gl-text-white;
background: var(--black, $black);
strong {
@include gl-font-weight-normal;
......@@ -287,6 +203,7 @@ $top-level-item-color: $purple-900;
border-top: $gl-spacing-scale-2 solid transparent;
border-bottom: $gl-spacing-scale-2 solid transparent;
border-right: $gl-spacing-scale-2 solid $black;
border-right-color: var(--black, $black);
}
}
}
......@@ -343,7 +260,7 @@ $top-level-item-color: $purple-900;
a {
@include gl-text-decoration-none;
color: $top-level-item-color;
color: $gray-900;
}
li {
......@@ -392,13 +309,15 @@ $top-level-item-color: $purple-900;
}
a.has-sub-items + .sidebar-sub-level-items {
@include gl-mt-n2;
.fly-out-top-item {
@include fly-out-top-item($has-sub-items: true);
}
}
a.has-sub-items + .sidebar-sub-level-items.fly-out-list {
@include gl-mt-n2;
}
@media (min-width: map-get($grid-breakpoints, md)) and (max-width: map-get($grid-breakpoints, xl) - 1px) {
&:not(.sidebar-expanded-mobile) {
@include collapse-contextual-sidebar;
......@@ -445,8 +364,8 @@ $top-level-item-color: $purple-900;
}
.badge.badge-pill {
@include gl-font-weight-normal; // TODO: update in `theme_helper.scss`
color: $blue-700; // TODO: update in `theme_helper.scss`
@include gl-font-weight-normal;
color: $blue-700;
}
}
}
......@@ -484,7 +403,6 @@ $top-level-item-color: $purple-900;
@include side-panel-toggle;
background-color: $gray-50;
border-top: 1px solid $border-color;
color: $top-level-item-color;
position: fixed;
bottom: 0;
width: $contextual-sidebar-width;
......@@ -538,14 +456,10 @@ $top-level-item-color: $purple-900;
//
// PANELS-SPECIFIC
// TODO: Check whether we can remove these in favor of the utility-classes
//
.settings-avatar {
background-color: $white;
svg {
fill: $gl-text-color-secondary;
margin: auto;
}
}
......
......@@ -32,6 +32,7 @@
.sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
color: $gray-900;
&.text-secondary {
font-weight: normal;
......
body:not(.sidebar-refactoring) {
@import 'contextual_sidebar_base';
}
body.sidebar-refactoring {
@import 'contextual_sidebar_variant';
}
@mixin collapse-contextual-sidebar-content {
@include context-header-collapsed;
.sidebar-top-level-items > li {
.sidebar-sub-level-items {
&:not(.flyout-list) {
display: none;
}
}
}
.nav-icon-container {
margin-right: 0;
}
.toggle-sidebar-button {
padding: 16px;
width: $contextual-sidebar-collapsed-width - 1px;
.collapse-text,
.icon-chevron-double-lg-left {
display: none;
}
.icon-chevron-double-lg-right {
display: block;
margin: 0;
}
}
}
@mixin collapse-contextual-sidebar {
width: $contextual-sidebar-collapsed-width;
.nav-sidebar-inner-scroll {
overflow-x: hidden;
}
.badge.badge-pill:not(.fly-out-badge),
.nav-item-name {
@include gl-sr-only;
}
.sidebar-top-level-items > li > a {
min-height: 45px;
}
.fly-out-top-item {
display: block;
}
.avatar-container {
margin: 0 auto;
}
}
@at-root {
.page-with-contextual-sidebar {
transition: padding-left $sidebar-transition-duration;
@include media-breakpoint-up(md) {
padding-left: $contextual-sidebar-collapsed-width;
}
@include media-breakpoint-up(xl) {
padding-left: $contextual-sidebar-width;
}
.issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
padding: 10px 0 15px;
}
}
.page-with-icon-sidebar {
@include media-breakpoint-up(md) {
padding-left: $contextual-sidebar-collapsed-width;
}
}
.settings-avatar {
background-color: $white;
svg {
fill: $gl-text-color-secondary;
margin: auto;
}
}
.nav-sidebar {
transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
z-index: 600;
width: $contextual-sidebar-width;
top: $header-height;
bottom: 0;
left: 0;
background-color: $gray-light;
box-shadow: inset -1px 0 0 $border-color;
transform: translate3d(0, 0, 0);
&:not(.sidebar-collapsed-desktop) {
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
box-shadow: inset -1px 0 0 $border-color, 2px 1px 3px $dropdown-shadow-color;
}
}
&.sidebar-collapsed-desktop {
@include collapse-contextual-sidebar;
}
&.sidebar-expanded-mobile {
left: 0;
}
a {
text-decoration: none;
}
ul {
padding-left: 0;
list-style: none;
}
li {
white-space: nowrap;
a {
transition: padding $sidebar-transition-duration;
display: flex;
align-items: center;
padding: 12px $gl-padding;
color: $gl-text-color-secondary;
}
.nav-item-name {
flex: 1;
}
&.active {
> a {
font-weight: $gl-font-weight-bold;
}
}
}
@include media-breakpoint-down(sm) {
left: (-$contextual-sidebar-width);
}
.nav-icon-container {
display: flex;
margin-right: 8px;
}
.fly-out-top-item {
display: none;
}
svg {
height: 16px;
width: 16px;
}
@media (min-width: map-get($grid-breakpoints, md)) and (max-width: map-get($grid-breakpoints, xl) - 1px) {
&:not(.sidebar-expanded-mobile) {
@include collapse-contextual-sidebar;
@include collapse-contextual-sidebar-content;
}
}
}
.nav-sidebar-inner-scroll {
height: 100%;
width: 100%;
overflow: auto;
}
.sidebar-sub-level-items {
display: none;
padding-bottom: 8px;
> li {
a {
padding: 8px 16px 8px 40px;
&:hover,
&:focus {
background: $link-active-background;
color: $gl-text-color;
}
}
&.active {
a {
&,
&:hover,
&:focus {
background: $link-active-background;
}
}
}
}
}
.sidebar-top-level-items {
margin-bottom: 60px;
> li {
> a {
@include media-breakpoint-up(sm) {
margin-right: 1px;
}
&:hover {
color: $gl-text-color;
}
}
&.is-showing-fly-out {
> a {
margin-right: 1px;
}
.sidebar-sub-level-items {
@include media-breakpoint-up(sm) {
position: fixed;
top: 0;
left: 0;
min-width: 150px;
margin-top: -1px;
padding: 4px 1px;
background-color: $white;
box-shadow: 2px 1px 3px $dropdown-shadow-color;
border: 1px solid $gray-darker;
border-left: 0;
border-radius: 0 3px 3px 0;
&::before {
content: '';
position: absolute;
top: -30px;
bottom: -30px;
left: -10px;
right: -30px;
z-index: -1;
}
&.is-above {
margin-top: 1px;
}
.divider {
height: 1px;
margin: 4px -1px;
padding: 0;
background-color: $dropdown-divider-bg;
}
> .active {
box-shadow: none;
> a {
background-color: transparent;
}
}
a {
padding: 8px 16px;
color: $gl-text-color;
&:hover,
&:focus {
background-color: $gray-darker;
}
}
}
}
}
.badge.badge-pill {
background-color: $inactive-badge-background;
color: $gl-text-color-secondary;
}
&.active {
background: $link-active-background;
> a {
margin-left: 4px;
// Subtract width of left border on active element
padding-left: $gl-padding-12;
}
.badge.badge-pill {
font-weight: $gl-font-weight-bold;
}
.sidebar-sub-level-items:not(.is-fly-out-only) {
display: block;
}
}
&.active > a:hover,
&.is-over > a {
background-color: $link-hover-background;
}
}
}
// Collapsed nav
.toggle-sidebar-button,
.close-nav-button {
@include side-panel-toggle;
}
.toggle-sidebar-button,
.close-nav-button {
position: fixed;
bottom: 0;
width: $contextual-sidebar-width - 1px;
border-top: 1px solid $border-color;
svg {
margin-right: 8px;
}
.icon-chevron-double-lg-right {
display: none;
}
}
.collapse-text {
white-space: nowrap;
overflow: hidden;
}
.sidebar-collapsed-desktop {
@include collapse-contextual-sidebar-content;
}
.fly-out-top-item {
> a {
display: flex;
}
.fly-out-badge {
margin-left: 8px;
}
}
.fly-out-top-item-name {
flex: 1;
}
// Mobile nav
.close-nav-button {
display: none;
}
@include media-breakpoint-down(sm) {
.close-nav-button {
display: flex;
}
.toggle-sidebar-button {
display: none;
}
.mobile-overlay {
display: none;
&.mobile-nav-open {
display: block;
position: fixed;
background-color: $black-transparent;
height: 100%;
width: 100%;
z-index: $zindex-dropdown-menu;
}
}
}
}
......@@ -253,3 +253,14 @@ $well-inner-border: $gray-200;
color: $gray-900;
border-color: $gray-800;
}
.nav-sidebar {
li.active {
box-shadow: none;
}
.sidebar-sub-level-items.fly-out-list {
box-shadow: none;
border: 1px solid $border-color;
}
}
......@@ -174,20 +174,20 @@
}
// Sidebar
.nav-sidebar li.active {
box-shadow: inset 4px 0 0 $border-and-box-shadow;
> a {
color: $sidebar-text;
}
.nav-icon-container svg {
fill: $sidebar-text;
}
.nav-sidebar li.active > a {
color: $sidebar-text;
}
.sidebar-top-level-items > li.active .badge.badge-pill {
color: $sidebar-text;
.nav-sidebar {
.fly-out-top-item {
a,
a:hover,
&.active a,
.fly-out-top-item-container {
background-color: $purple-900;
color: $white;
}
}
}
.nav-links li {
......@@ -213,7 +213,6 @@
.ide-sidebar-link {
&.active {
color: $border-and-box-shadow;
box-shadow: inset 3px 0 $border-and-box-shadow;
&.is-right {
box-shadow: inset -3px 0 $border-and-box-shadow;
......
......@@ -6,7 +6,7 @@ body {
$indigo-200,
$indigo-500,
$indigo-700,
$indigo-800,
$purple-900,
$indigo-900,
$white
);
......
- page_classes = page_class << @html_class
- page_classes = page_classes.flatten.compact
- body_classes = [user_application_theme, user_tab_width, @body_class, client_class_list]
- body_classes << 'sidebar-refactoring' if sidebar_refactor_enabled?
!!! 5
%html{ lang: I18n.locale, class: page_classes }
......
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign, default_enabled: :yaml)
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign, @project, default_enabled: :yaml)
- @content_class = "limit-container-width" unless fluid_layout || !redesign_enabled
#js-security-configuration-static{ data: { project_path: @project.full_path, upgrade_path: security_upgrade_path } }
---
name: security_configuration_redesign_ee
introduced_by_url:
rollout_issue_url:
milestone: '14.1'
type: development
group: group::analyzer frontend
default_enabled: false
......@@ -28,10 +28,15 @@ For each security control the page displays:
## UI redesign
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326926) in 14.0 for GitLab Free and Premium, behind a feature flag, disabled by default.
> - Enabled on GitLab.com.
> - Enabled on GitLab.com for Free & Premium.
> - Recommended for production use.
> - It can be enabled or disabled for a single project.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ui-redesign). **(FREE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333109) in 14.1 for GitLab Ultimate, behind a feature flag, disabled by default.
> - Disabled on GitLab.com.
> - Not recommended for production use.
> - It can be enabled or disabled for a single project.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ui-redesign-for-ultimate). **(ULTIMATE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
......@@ -98,3 +103,28 @@ Feature.disable(:security_configuration_redesign)
# For a single project
Feature.disable(:security_configuration_redesign, Project.find(<project id>))
```
## Enable or disable UI redesign for Ultimate **(ULTIMATE SELF)**
The Security Configuration redesign is under development, and is not ready for
production use. It is deployed behind a feature flag that is **disabled by
default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) can enable it.
To enable it:
```ruby
# For the instance
Feature.enable(:security_configuration_redesign_ee)
# For a single project
Feature.enable(:security_configuration_redesign_ee, Project.find(<project id>))
```
To disable it:
```ruby
# For the instance
Feature.disable(:security_configuration_redesign_ee)
# For a single project
Feature.disable(:security_configuration_redesign_ee, Project.find(<project id>))
```
import { initSecurityConfiguration } from 'ee/security_configuration';
import { initStaticSecurityConfiguration } from '~/security_configuration';
import { initCESecurityConfiguration } from '~/security_configuration';
const el = document.querySelector('#js-security-configuration');
if (el) {
initSecurityConfiguration(el);
} else {
initStaticSecurityConfiguration(document.querySelector('#js-security-configuration-static'));
initCESecurityConfiguration(document.querySelector('#js-security-configuration-static'));
}
import Vue from 'vue';
import { parseBooleanDataAttributes } from '~/lib/utils/dom_utils';
import { initRedesignedSecurityConfiguration } from '~/security_configuration';
import SecurityConfigurationApp from './components/app.vue';
export const initSecurityConfiguration = (el) => {
......@@ -7,6 +8,10 @@ export const initSecurityConfiguration = (el) => {
return null;
}
if (gon.features?.securityConfigurationRedesignEE) {
return initRedesignedSecurityConfiguration(el);
}
const {
autoDevopsHelpPagePath,
autoDevopsPath,
......
......@@ -24,6 +24,10 @@ module EE
end
feature_category :static_application_security_testing
before_action only: [:show] do
push_frontend_feature_flag(:security_configuration_redesign_ee, project, default_enabled: :yaml)
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
......
......@@ -75,7 +75,8 @@ module Projects
{
type: type,
configured: configured,
configuration_path: configuration_path(type)
configuration_path: configuration_path(type),
available: feature_available(type)
}
end
......@@ -95,6 +96,13 @@ module Projects
api_fuzzing: project_security_configuration_api_fuzzing_path(project)
}[type]
end
def feature_available(type)
# SAST and Secret Detection are always available, but this isn't
# reflected by our license model yet.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/333113
%w[sast secret_detection].include?(type) || project.licensed_feature_available?(type)
end
end
end
end
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign_ee, @project, default_enabled: :yaml)
- @content_class = "limit-container-width" unless fluid_layout || !redesign_enabled
- if @configuration.nil?
= render_ce 'projects/security/configuration/show'
......
......@@ -15,9 +15,72 @@ RSpec.describe 'User sees Security Configuration table', :js do
sign_in(user)
end
context 'with security_dashboard feature available' do
context 'with security_dashboard feature available and redesign feature flag on' do
before do
stub_licensed_features(security_dashboard: true, sast: true, dast: true)
end
context 'with no SAST report' do
it 'shows SAST is not enabled' do
visit(project_security_configuration_path(project))
within_sast_card do
expect(page).to have_text('SAST')
expect(page).to have_text('Not enabled')
expect(page).to have_link('Enable SAST')
end
end
end
context 'with SAST report' do
before do
create(:ci_build, :sast, pipeline: pipeline, status: 'success')
end
it 'shows SAST is enabled' do
visit(project_security_configuration_path(project))
within_sast_card do
expect(page).to have_text('SAST')
expect(page).to have_text('Enabled')
expect(page).to have_link('Configure SAST')
end
end
end
context 'with no DAST report' do
it 'shows DAST is not enabled' do
visit(project_security_configuration_path(project))
within_dast_card do
expect(page).to have_text('DAST')
expect(page).to have_text('Not enabled')
expect(page).to have_link('Enable DAST')
end
end
end
context 'with DAST report' do
before do
create(:ci_build, :dast, pipeline: pipeline, status: 'success')
end
it 'shows DAST is enabled' do
visit(project_security_configuration_path(project))
within_dast_card do
expect(page).to have_text('DAST')
expect(page).to have_text('Enabled')
expect(page).to have_link('Configure DAST')
end
end
end
end
context 'with security_dashboard feature available and redesign feature flag off' do
before do
stub_licensed_features(security_dashboard: true)
stub_feature_flags(security_configuration_redesign_ee: false)
end
context 'with no SAST report' do
......@@ -27,7 +90,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_sast_row do
expect(page).to have_text('SAST')
expect(page).to have_text('Not enabled')
expect(page).to have_css('[data-testid="enable-button"]')
expect(page).to have_link('Enable')
end
end
end
......@@ -43,7 +106,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_sast_row do
expect(page).to have_text('SAST')
expect(page).to have_text('Enabled')
expect(page).to have_css('[data-testid="configure-button"]')
expect(page).to have_link('Configure')
end
end
end
......@@ -55,7 +118,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_dast_row do
expect(page).to have_text('DAST')
expect(page).to have_text('Not enabled')
expect(page).to have_css('[data-testid="enable-button"]')
expect(page).to have_link('Enable')
end
end
end
......@@ -71,7 +134,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_dast_row do
expect(page).to have_text('DAST')
expect(page).to have_text('Enabled')
expect(page).to have_css('[data-testid="configure-button"]')
expect(page).to have_link('Configure')
end
end
......@@ -97,4 +160,16 @@ RSpec.describe 'User sees Security Configuration table', :js do
yield
end
end
def within_sast_card
within '[data-testid="security-testing-card"]:nth-of-type(1)' do
yield
end
end
def within_dast_card
within '[data-testid="security-testing-card"]:nth-of-type(2)' do
yield
end
end
end
......@@ -17,6 +17,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
before do
project.add_maintainer(current_user)
stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
end
describe '#to_h' do
......@@ -265,7 +266,8 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
{
"type" => type.to_s,
"configured" => configured,
"configuration_path" => configuration_path
"configuration_path" => configuration_path,
"available" => licensed_scan_types.include?(type)
}
end
......@@ -277,4 +279,8 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
api_fuzzing: project_security_configuration_api_fuzzing_path(project)
}[type]
end
def licensed_scan_types
::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types - [:cluster_image_scanning]
end
end
......@@ -35,7 +35,15 @@ const mockValidCustomFeature = [
{
name: 'SAST',
type: 'SAST',
customfield: 'customvalue',
customField: 'customvalue',
},
];
const mockValidCustomFeatureSnakeCase = [
{
name: 'SAST',
type: 'SAST',
custom_field: 'customvalue',
},
];
......@@ -79,3 +87,15 @@ describe('returns an object with augmentedSecurityFeatures and augmentedComplian
).toEqual(expectedOutputCustomFeature);
});
});
describe('returns an object with camelcased keys', () => {
it('given a customfeature in snakecase', () => {
expect(
augmentFeatures(
mockSecurityFeatures,
mockComplianceFeatures,
mockValidCustomFeatureSnakeCase,
),
).toEqual(expectedOutputCustomFeature);
});
});
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