diff --git a/app/assets/javascripts/security_configuration/components/configuration_table.vue b/app/assets/javascripts/security_configuration/components/configuration_table.vue index d1ac7190c37d8cbe941cae1299c99556d954608c..168d158a7863f026595f9ac6b0c90c40b3dc075b 100644 --- a/app/assets/javascripts/security_configuration/components/configuration_table.vue +++ b/app/assets/javascripts/security_configuration/components/configuration_table.vue @@ -1,5 +1,5 @@ <script> -import { GlLink, GlSprintf, GlTable, GlAlert } from '@gitlab/ui'; +import { GlLink, GlTable, GlAlert } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import { REPORT_TYPE_SAST, @@ -8,10 +8,11 @@ import { REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_CONTAINER_SCANNING, REPORT_TYPE_COVERAGE_FUZZING, + REPORT_TYPE_API_FUZZING, REPORT_TYPE_LICENSE_COMPLIANCE, } from '~/vue_shared/security_reports/constants'; -import { features } from './features_constants'; import ManageSast from './manage_sast.vue'; +import { scanners } from './scanners_constants'; import Upgrade from './upgrade.vue'; const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!'; @@ -20,12 +21,10 @@ const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`; export default { components: { GlLink, - GlSprintf, GlTable, GlAlert, }, data: () => ({ - features, errorMessage: '', }), methods: { @@ -45,6 +44,7 @@ export default { [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade, [REPORT_TYPE_CONTAINER_SCANNING]: Upgrade, [REPORT_TYPE_COVERAGE_FUZZING]: Upgrade, + [REPORT_TYPE_API_FUZZING]: Upgrade, [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade, }; @@ -64,7 +64,7 @@ export default { thClass, }, ], - items: features, + items: scanners, }, }; </script> diff --git a/app/assets/javascripts/security_configuration/components/features_constants.js b/app/assets/javascripts/security_configuration/components/scanners_constants.js similarity index 66% rename from app/assets/javascripts/security_configuration/components/features_constants.js rename to app/assets/javascripts/security_configuration/components/scanners_constants.js index c0eef0611a08bcca05377014371e481a63b9ec07..9846df0b4bf721184a4f79d96f4c4d4a82f07b68 100644 --- a/app/assets/javascripts/security_configuration/components/features_constants.js +++ b/app/assets/javascripts/security_configuration/components/scanners_constants.js @@ -1,5 +1,5 @@ import { helpPagePath } from '~/helpers/help_page_helper'; -import { s__ } from '~/locale'; +import { __, s__ } from '~/locale'; import { REPORT_TYPE_SAST, @@ -9,58 +9,65 @@ import { REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_CONTAINER_SCANNING, REPORT_TYPE_COVERAGE_FUZZING, + REPORT_TYPE_API_FUZZING, REPORT_TYPE_LICENSE_COMPLIANCE, } from '~/vue_shared/security_reports/constants'; /** * Translations & helpPagePaths for Static Security Configuration Page */ -export const SAST_NAME = s__('Static Application Security Testing (SAST)'); -export const SAST_DESCRIPTION = s__('Analyze your source code for known vulnerabilities.'); +export const SAST_NAME = __('Static Application Security Testing (SAST)'); +export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.'); export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index'); -export const DAST_NAME = s__('Dynamic Application Security Testing (DAST)'); -export const DAST_DESCRIPTION = s__('Analyze a review version of your web application.'); +export const DAST_NAME = __('Dynamic Application Security Testing (DAST)'); +export const DAST_DESCRIPTION = __('Analyze a review version of your web application.'); export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index'); -export const DAST_PROFILES_NAME = s__('DAST Scans'); -export const DAST_PROFILES_DESCRIPTION = s__('Analyze a review version of your web application.'); +export const DAST_PROFILES_NAME = __('DAST Scans'); +export const DAST_PROFILES_DESCRIPTION = __( + 'Saved scan settings and target site settings which are reusable.', +); export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index'); -export const SECRET_DETECTION_NAME = s__('Secret Detection'); -export const SECRET_DETECTION_DESCRIPTION = s__( +export const SECRET_DETECTION_NAME = __('Secret Detection'); +export const SECRET_DETECTION_DESCRIPTION = __( 'Analyze your source code and git history for secrets.', ); export const SECRET_DETECTION_HELP_PATH = helpPagePath( 'user/application_security/secret_detection/index', ); -export const DEPENDENCY_SCANNING_NAME = s__('Dependency Scanning'); -export const DEPENDENCY_SCANNING_DESCRIPTION = s__( +export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning'); +export const DEPENDENCY_SCANNING_DESCRIPTION = __( 'Analyze your dependencies for known vulnerabilities.', ); export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath( 'user/application_security/dependency_scanning/index', ); -export const CONTAINER_SCANNING_NAME = s__('Container Scanning'); -export const CONTAINER_SCANNING_DESCRIPTION = s__( +export const CONTAINER_SCANNING_NAME = __('Container Scanning'); +export const CONTAINER_SCANNING_DESCRIPTION = __( 'Check your Docker images for known vulnerabilities.', ); export const CONTAINER_SCANNING_HELP_PATH = helpPagePath( 'user/application_security/container_scanning/index', ); -export const COVERAGE_FUZZING_NAME = s__('Coverage Fuzzing'); -export const COVERAGE_FUZZING_DESCRIPTION = s__( +export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing'); +export const COVERAGE_FUZZING_DESCRIPTION = __( 'Find bugs in your code with coverage-guided fuzzing.', ); export const COVERAGE_FUZZING_HELP_PATH = helpPagePath( 'user/application_security/coverage_fuzzing/index', ); -export const LICENSE_COMPLIANCE_NAME = s__('License Compliance'); -export const LICENSE_COMPLIANCE_DESCRIPTION = s__( +export const API_FUZZING_NAME = __('API Fuzzing'); +export const API_FUZZING_DESCRIPTION = __('Find bugs in your code with API fuzzing.'); +export const API_FUZZING_HELP_PATH = helpPagePath('user/application_security/api_fuzzing/index'); + +export const LICENSE_COMPLIANCE_NAME = __('License Compliance'); +export const LICENSE_COMPLIANCE_DESCRIPTION = __( 'Search your project dependencies for their licenses and apply policies.', ); export const LICENSE_COMPLIANCE_HELP_PATH = helpPagePath( @@ -71,7 +78,7 @@ export const UPGRADE_CTA = s__( 'SecurityConfiguration|Available with %{linkStart}upgrade or free trial%{linkEnd}', ); -export const features = [ +export const scanners = [ { name: SAST_NAME, description: SAST_DESCRIPTION, @@ -90,12 +97,6 @@ export const features = [ helpPath: DAST_PROFILES_HELP_PATH, type: REPORT_TYPE_DAST_PROFILES, }, - { - name: SECRET_DETECTION_NAME, - description: SECRET_DETECTION_DESCRIPTION, - helpPath: SECRET_DETECTION_HELP_PATH, - type: REPORT_TYPE_SECRET_DETECTION, - }, { name: DEPENDENCY_SCANNING_NAME, description: DEPENDENCY_SCANNING_DESCRIPTION, @@ -108,12 +109,24 @@ export const features = [ helpPath: CONTAINER_SCANNING_HELP_PATH, type: REPORT_TYPE_CONTAINER_SCANNING, }, + { + name: SECRET_DETECTION_NAME, + description: SECRET_DETECTION_DESCRIPTION, + helpPath: SECRET_DETECTION_HELP_PATH, + type: REPORT_TYPE_SECRET_DETECTION, + }, { name: COVERAGE_FUZZING_NAME, description: COVERAGE_FUZZING_DESCRIPTION, helpPath: COVERAGE_FUZZING_HELP_PATH, type: REPORT_TYPE_COVERAGE_FUZZING, }, + { + name: API_FUZZING_NAME, + description: API_FUZZING_DESCRIPTION, + helpPath: API_FUZZING_HELP_PATH, + type: REPORT_TYPE_API_FUZZING, + }, { name: LICENSE_COMPLIANCE_NAME, description: LICENSE_COMPLIANCE_DESCRIPTION, diff --git a/app/assets/javascripts/security_configuration/components/upgrade.vue b/app/assets/javascripts/security_configuration/components/upgrade.vue index 04f3763d5daf244d676c2fc51b58da365aef13ec..518eb57731d05de5373331d0cc067d217c76cfc6 100644 --- a/app/assets/javascripts/security_configuration/components/upgrade.vue +++ b/app/assets/javascripts/security_configuration/components/upgrade.vue @@ -1,6 +1,6 @@ <script> import { GlLink, GlSprintf } from '@gitlab/ui'; -import { UPGRADE_CTA } from './features_constants'; +import { UPGRADE_CTA } from './scanners_constants'; export default { components: { diff --git a/app/assets/javascripts/vue_shared/security_reports/constants.js b/app/assets/javascripts/vue_shared/security_reports/constants.js index 56a8853412d291995fa6f9b8c5ebcda3c94a9be9..1cdcf87097f1feae9199c30539931413c81b0c56 100644 --- a/app/assets/javascripts/vue_shared/security_reports/constants.js +++ b/app/assets/javascripts/vue_shared/security_reports/constants.js @@ -23,7 +23,7 @@ export const REPORT_TYPE_SECRET_DETECTION = 'secret_detection'; export const REPORT_TYPE_DEPENDENCY_SCANNING = 'dependency_scanning'; export const REPORT_TYPE_CONTAINER_SCANNING = 'container_scanning'; export const REPORT_TYPE_COVERAGE_FUZZING = 'coverage_fuzzing'; -export const REPORT_TYPE_LICENSE_COMPLIANCE = 'license_compliance'; +export const REPORT_TYPE_LICENSE_COMPLIANCE = 'license_scanning'; export const REPORT_TYPE_API_FUZZING = 'api_fuzzing'; /** diff --git a/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml b/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml new file mode 100644 index 0000000000000000000000000000000000000000..5b901a6e158533fb78df60815b4ab05a5c126687 --- /dev/null +++ b/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml @@ -0,0 +1,5 @@ +--- +title: Add API Fuzzing to Security Configuration page, and re-order scanners +merge_request: 56022 +author: +type: changed diff --git a/ee/app/assets/javascripts/security_configuration/components/app.vue b/ee/app/assets/javascripts/security_configuration/components/app.vue index a67f6efdacd82db58214d6396a5c61cc830cccb1..d410b29f933ee203f0398b7fce1009f65ade5d77 100644 --- a/ee/app/assets/javascripts/security_configuration/components/app.vue +++ b/ee/app/assets/javascripts/security_configuration/components/app.vue @@ -1,23 +1,21 @@ <script> -import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui'; +import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui'; import { parseBoolean } from '~/lib/utils/common_utils'; -import { sprintf, s__, __ } from '~/locale'; +import { s__, __ } from '~/locale'; +import { scanners } from '~/security_configuration/components/scanners_constants'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import AutoFixSettings from './auto_fix_settings.vue'; -import FeatureStatus from './feature_status.vue'; -import ManageFeature from './manage_feature.vue'; +import ConfigurationTable from './configuration_table.vue'; export default { components: { GlAlert, GlLink, GlSprintf, - GlTable, AutoFixSettings, LocalStorageSync, - FeatureStatus, - ManageFeature, + ConfigurationTable, }, mixins: [glFeatureFlagsMixin()], props: { @@ -26,10 +24,6 @@ export default { required: false, default: false, }, - helpPagePath: { - type: String, - required: true, - }, autoDevopsHelpPagePath: { type: String, required: true, @@ -84,28 +78,6 @@ export default { devopsUrl() { return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath; }, - fields() { - const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!'; - const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`; - - return [ - { - key: 'feature', - label: s__('SecurityConfiguration|Security Control'), - thClass, - }, - { - key: 'status', - label: s__('SecurityConfiguration|Status'), - thClass, - }, - { - key: 'manage', - label: s__('SecurityConfiguration|Manage'), - thClass, - }, - ]; - }, shouldShowAutoDevopsAlert() { return Boolean( !parseBoolean(this.autoDevopsAlertDismissed) && @@ -114,16 +86,26 @@ export default { this.canEnableAutoDevops, ); }, + featuresForDisplay() { + const featuresByType = this.features.reduce((acc, feature) => { + acc[feature.type] = feature; + return acc; + }, {}); + + return scanners.map((scanner) => { + const feature = featuresByType[scanner.type] ?? {}; + + return { + ...feature, + ...scanner, + }; + }); + }, }, methods: { dismissAutoDevopsAlert() { this.autoDevopsAlertDismissed = 'true'; }, - getFeatureDocumentationLinkLabel(item) { - return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), { - featureName: item.name, - }); - }, }, autoDevopsAlertMessage: s__(` SecurityConfiguration|You can quickly enable all security scanning tools by @@ -166,40 +148,13 @@ export default { </gl-sprintf> </gl-alert> - <gl-table - ref="securityControlTable" - :items="features" - :fields="fields" - stacked="md" - :tbody-tr-attr="{ 'data-testid': 'security-scanner-row' }" - > - <template #cell(feature)="{ item }"> - <div class="gl-text-gray-900">{{ item.name }}</div> - <div> - {{ item.description }} - <gl-link - target="_blank" - :href="item.link" - :aria-label="getFeatureDocumentationLinkLabel(item)" - data-testid="docsLink" - > - {{ s__('SecurityConfiguration|More information') }} - </gl-link> - </div> - </template> - - <template #cell(status)="{ item }"> - <feature-status - :feature="item" - :gitlab-ci-present="gitlabCiPresent" - :gitlab-ci-history-path="gitlabCiHistoryPath" - /> - </template> + <configuration-table + :features="featuresForDisplay" + :auto-devops-enabled="autoDevopsEnabled" + :gitlab-ci-present="gitlabCiPresent" + :gitlab-ci-history-path="gitlabCiHistoryPath" + /> - <template #cell(manage)="{ item }"> - <manage-feature :feature="item" :auto-devops-enabled="autoDevopsEnabled" /> - </template> - </gl-table> <auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" /> </article> </template> diff --git a/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue b/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue new file mode 100644 index 0000000000000000000000000000000000000000..8696d1a1de447e479d5b48d83c05e1cd0cd9dae0 --- /dev/null +++ b/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue @@ -0,0 +1,98 @@ +<script> +import { GlLink, GlTable } from '@gitlab/ui'; +import { s__, sprintf } from '~/locale'; +import FeatureStatus from './feature_status.vue'; +import ManageFeature from './manage_feature.vue'; + +const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!'; +const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`; + +export default { + components: { + GlLink, + GlTable, + FeatureStatus, + ManageFeature, + }, + props: { + features: { + type: Array, + required: true, + }, + autoDevopsEnabled: { + type: Boolean, + required: false, + default: false, + }, + gitlabCiPresent: { + type: Boolean, + required: false, + default: false, + }, + gitlabCiHistoryPath: { + type: String, + required: false, + default: '', + }, + }, + methods: { + getFeatureDocumentationLinkLabel(item) { + return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), { + featureName: item.name, + }); + }, + }, + fields: [ + { + key: 'description', + label: s__('SecurityConfiguration|Security Control'), + thClass, + }, + { + key: 'status', + label: s__('SecurityConfiguration|Status'), + thClass, + }, + { + key: 'manage', + label: s__('SecurityConfiguration|Manage'), + thClass, + }, + ], +}; +</script> + +<template> + <gl-table + :items="features" + :fields="$options.fields" + stacked="md" + :tbody-tr-attr="{ 'data-testid': 'security-scanner-row' }" + > + <template #cell(description)="{ item }"> + <div class="gl-text-gray-900">{{ item.name }}</div> + <div> + {{ item.description }} + <gl-link + target="_blank" + :href="item.helpPath" + :aria-label="getFeatureDocumentationLinkLabel(item)" + > + {{ s__('SecurityConfiguration|More information') }} + </gl-link> + </div> + </template> + + <template #cell(status)="{ item }"> + <feature-status + :feature="item" + :gitlab-ci-present="gitlabCiPresent" + :gitlab-ci-history-path="gitlabCiHistoryPath" + /> + </template> + + <template #cell(manage)="{ item }"> + <manage-feature :feature="item" :auto-devops-enabled="autoDevopsEnabled" /> + </template> + </gl-table> +</template> diff --git a/ee/app/assets/javascripts/security_configuration/index.js b/ee/app/assets/javascripts/security_configuration/index.js index 7c5c26621a1c170b2e0eaf95e37415ce76beb71a..ab15b0309d17993dcf751a4dd377d3385172301b 100644 --- a/ee/app/assets/javascripts/security_configuration/index.js +++ b/ee/app/assets/javascripts/security_configuration/index.js @@ -11,7 +11,6 @@ export const initSecurityConfiguration = (el) => { autoDevopsHelpPagePath, autoDevopsPath, features, - helpPagePath, latestPipelinePath, autoFixEnabled, autoFixHelpPath, @@ -33,7 +32,6 @@ export const initSecurityConfiguration = (el) => { autoDevopsHelpPagePath, autoDevopsPath, features: JSON.parse(features), - helpPagePath, latestPipelinePath, ...parseBooleanDataAttributes(el, [ 'autoDevopsEnabled', diff --git a/ee/app/presenters/projects/security/configuration_presenter.rb b/ee/app/presenters/projects/security/configuration_presenter.rb index 121e17373d3fae0785881cefac6fca66efa19921..723ec9cf106f86ae40ff0117d313a9a7f6118182 100644 --- a/ee/app/presenters/projects/security/configuration_presenter.rb +++ b/ee/app/presenters/projects/security/configuration_presenter.rb @@ -9,46 +9,6 @@ class ConfigurationPresenter < Gitlab::View::Presenter::Delegated presents :project - SCAN_DOCS = { - container_scanning: 'user/application_security/container_scanning/index', - dast: 'user/application_security/dast/index', - dast_profiles: 'user/application_security/dast/index', - dependency_scanning: 'user/application_security/dependency_scanning/index', - license_scanning: 'user/compliance/license_compliance/index', - sast: 'user/application_security/sast/index', - secret_detection: 'user/application_security/secret_detection/index', - coverage_fuzzing: 'user/application_security/coverage_fuzzing/index', - api_fuzzing: 'user/application_security/api_fuzzing/index' - }.freeze - - def self.localized_scan_descriptions - { - container_scanning: _('Check your Docker images for known vulnerabilities.'), - dast: _('Analyze a review version of your web application.'), - dast_profiles: _('Saved scan settings and target site settings which are reusable.'), - dependency_scanning: _('Analyze your dependencies for known vulnerabilities.'), - license_scanning: _('Search your project dependencies for their licenses and apply policies.'), - sast: _('Analyze your source code for known vulnerabilities.'), - secret_detection: _('Analyze your source code and git history for secrets.'), - coverage_fuzzing: _('Find bugs in your code with coverage-guided fuzzing.'), - api_fuzzing: _('Find bugs in your code with API fuzzing.') - }.freeze - end - - def self.localized_scan_names - { - container_scanning: _('Container Scanning'), - dast: _('Dynamic Application Security Testing (DAST)'), - dast_profiles: _('DAST Scans'), - dependency_scanning: _('Dependency Scanning'), - license_scanning: _('License Compliance'), - sast: _('Static Application Security Testing (SAST)'), - secret_detection: _('Secret Detection'), - coverage_fuzzing: _('Coverage Fuzzing'), - api_fuzzing: _('API Fuzzing') - }.freeze - end - def to_h { auto_devops_enabled: auto_devops_source?, @@ -115,8 +75,9 @@ def latest_pipeline_path end # DAST On-demand scans is a static (non job) entry. Add it manually following DAST + # TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/323375 def dast_profiles_insert(scans) - index = scans.index { |scan| scan[:name] == localized_scan_names[:dast] } + index = scans.index { |scan| scan[:type] == :dast } unless index.nil? scans.insert(index + 1, scan(:dast_profiles, configured: true, status: s_('SecurityConfiguration|Available for on-demand DAST'))) @@ -130,10 +91,7 @@ def scan(type, configured: false, status:) type: type, configured: configured, status: status, - description: self.class.localized_scan_descriptions[type], - link: help_page_path(SCAN_DOCS[type]), - configuration_path: configuration_path(type), - name: localized_scan_names[type] + configuration_path: configuration_path(type) } end @@ -141,10 +99,6 @@ def scan_types ::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types end - def localized_scan_names - @localized_scan_names ||= self.class.localized_scan_names - end - def project_settings project.security_setting end diff --git a/ee/spec/frontend/security_configuration/components/app_spec.js b/ee/spec/frontend/security_configuration/components/app_spec.js index 43ab9ef1afb6844bcafd3733a1ef00bc6910f5c3..cebba404035ea259b48c846b0ff0c6c80c07b502 100644 --- a/ee/spec/frontend/security_configuration/components/app_spec.js +++ b/ee/spec/frontend/security_configuration/components/app_spec.js @@ -2,10 +2,10 @@ import { GlAlert, GlLink } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { merge } from 'lodash'; import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue'; -import FeatureStatus from 'ee/security_configuration/components/feature_status.vue'; -import ManageFeature from 'ee/security_configuration/components/manage_feature.vue'; +import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import stubChildren from 'helpers/stub_children'; +import { scanners } from '~/security_configuration/components/scanners_constants'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import { generateFeatures } from './helpers'; @@ -31,7 +31,6 @@ describe('Security Configuration App', () => { { stubs: { ...stubChildren(SecurityConfigurationApp), - GlTable: false, GlSprintf: false, }, propsData, @@ -51,13 +50,8 @@ describe('Security Configuration App', () => { }); const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' }); - const getFeaturesTable = () => wrapper.find({ ref: 'securityControlTable' }); - const getFeaturesRows = () => getFeaturesTable().findAll('tbody tr'); + const getConfigurationTable = () => wrapper.find(ConfigurationTable); const getAlert = () => wrapper.find(GlAlert); - const getRowCells = (row) => { - const [feature, status, manage] = row.findAll('td').wrappers; - return { feature, status, manage }; - }; describe('header', () => { it.each` @@ -161,30 +155,31 @@ describe('Security Configuration App', () => { }); describe('features table', () => { - it('passes the expected data to the GlTable', () => { - const features = generateFeatures(5); + it('passes the expected features to the configuration table', () => { + const features = generateFeatures(scanners.length); createComponent({ propsData: { features } }); - - expect(getFeaturesTable().classes('b-table-stacked-md')).toBeTruthy(); - const rows = getFeaturesRows(); - expect(rows).toHaveLength(5); - - for (let i = 0; i < features.length; i += 1) { - const { feature, status, manage } = getRowCells(rows.at(i)); - expect(feature.text()).toMatch(features[i].name); - expect(feature.text()).toMatch(features[i].description); - expect(status.find(FeatureStatus).props()).toEqual({ - feature: features[i], - gitlabCiPresent: propsData.gitlabCiPresent, - gitlabCiHistoryPath: propsData.gitlabCiHistoryPath, + const table = getConfigurationTable(); + const receivedFeatures = table.props('features'); + + scanners.forEach((scanner, i) => { + expect(receivedFeatures[i]).toMatchObject({ + ...features[i], + name: scanner.name, + description: scanner.description, + helpPath: scanner.helpPath, }); - expect(manage.find(ManageFeature).props()).toEqual({ - feature: features[i], - autoDevopsEnabled: propsData.autoDevopsEnabled, - }); - expect(feature.find(GlLink).props('href')).toBe(features[i].href); - } + }); + }); + + it('passes the expected props data to the configuration table', () => { + createComponent(); + + expect(getConfigurationTable().props()).toMatchObject({ + autoDevopsEnabled: propsData.autoDevopsEnabled, + gitlabCiPresent: propsData.gitlabCiPresent, + gitlabCiHistoryPath: propsData.gitlabCiHistoryPath, + }); }); }); }); diff --git a/ee/spec/frontend/security_configuration/components/configuration_table_spec.js b/ee/spec/frontend/security_configuration/components/configuration_table_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..41a7cba5c1261560d2746b97a722f164302abdb0 --- /dev/null +++ b/ee/spec/frontend/security_configuration/components/configuration_table_spec.js @@ -0,0 +1,75 @@ +import { GlLink } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue'; +import FeatureStatus from 'ee/security_configuration/components/feature_status.vue'; +import ManageFeature from 'ee/security_configuration/components/manage_feature.vue'; +import stubChildren from 'helpers/stub_children'; +import { generateFeatures } from './helpers'; + +const propsData = { + features: [], + autoDevopsEnabled: false, + gitlabCiPresent: false, + gitlabCiHistoryPath: '/ci/history', +}; + +describe('ConfigurationTable component', () => { + let wrapper; + const mockFeatures = [ + ...generateFeatures(1, { + name: 'foo', + description: 'Foo description', + helpPath: '/help/foo', + }), + ...generateFeatures(1, { + name: 'bar', + description: 'Bar description', + helpPath: '/help/bar', + }), + ]; + + const createComponent = (props) => { + wrapper = mount(ConfigurationTable, { + stubs: { + ...stubChildren(ConfigurationTable), + GlTable: false, + }, + propsData: { + ...propsData, + ...props, + }, + }); + }; + + const getRows = () => wrapper.findAll('tbody tr'); + const getRowCells = (row) => { + const [description, status, manage] = row.findAll('td').wrappers; + return { description, status, manage }; + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it.each(mockFeatures)('renders the feature %p correctly', (feature) => { + createComponent({ features: [feature] }); + + expect(wrapper.classes('b-table-stacked-md')).toBeTruthy(); + const rows = getRows(); + expect(rows).toHaveLength(1); + + const { description, status, manage } = getRowCells(rows.at(0)); + expect(description.text()).toMatch(feature.name); + expect(description.text()).toMatch(feature.description); + expect(status.find(FeatureStatus).props()).toEqual({ + feature, + gitlabCiPresent: propsData.gitlabCiPresent, + gitlabCiHistoryPath: propsData.gitlabCiHistoryPath, + }); + expect(manage.find(ManageFeature).props()).toEqual({ + feature, + autoDevopsEnabled: propsData.autoDevopsEnabled, + }); + expect(description.find(GlLink).attributes('href')).toBe(feature.helpPath); + }); +}); diff --git a/ee/spec/frontend/security_configuration/components/helpers.js b/ee/spec/frontend/security_configuration/components/helpers.js index 7de2b971dcefa2f647015e65ae259f5db23f3b74..ec0db18a36a9274a2a1ffef82e5cb875391c144d 100644 --- a/ee/spec/frontend/security_configuration/components/helpers.js +++ b/ee/spec/frontend/security_configuration/components/helpers.js @@ -1,9 +1,8 @@ +import { scanners } from '~/security_configuration/components/scanners_constants'; + export const generateFeatures = (n, overrides = {}) => { return [...Array(n).keys()].map((i) => ({ - type: `scan-type-${i}`, - name: `name-feature-${i}`, - description: `description-feature-${i}`, - link: `link-feature-${i}`, + type: scanners[i % scanners.length].type, configuration_path: i % 2 ? `configuration_path-${i}` : null, configured: i % 2 === 0, status: i % 2 === 0 ? 'Enabled' : 'Not enabled', diff --git a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb index f9a35ddef9ac148f8c67f8ea00aafb36518410af..c3dd8500ac77f7125eb2900d1586e02c062a3b28 100644 --- a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb +++ b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb @@ -263,10 +263,7 @@ def security_scan(type, configured:, auto_dev_ops_enabled: false) "type" => type.to_s, "configured" => configured, "status" => status_str, - "description" => described_class.localized_scan_descriptions[type], - "link" => help_page_path(described_class::SCAN_DOCS[type]), - "configuration_path" => configuration_path, - "name" => described_class.localized_scan_names[type] + "configuration_path" => configuration_path } end diff --git a/spec/frontend/security_configuration/configuration_table_spec.js b/spec/frontend/security_configuration/configuration_table_spec.js index a9d9a0dbf1a371c4f4200bab44798c7089d1656d..b8a574dc4e0c74bb376d4929a33259b7be8617cc 100644 --- a/spec/frontend/security_configuration/configuration_table_spec.js +++ b/spec/frontend/security_configuration/configuration_table_spec.js @@ -1,7 +1,7 @@ import { mount } from '@vue/test-utils'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import ConfigurationTable from '~/security_configuration/components/configuration_table.vue'; -import { features, UPGRADE_CTA } from '~/security_configuration/components/features_constants'; +import { scanners, UPGRADE_CTA } from '~/security_configuration/components/scanners_constants'; import { REPORT_TYPE_SAST, @@ -25,20 +25,20 @@ describe('Configuration Table Component', () => { createComponent(); }); - describe.each(features.map((feature, i) => [feature, i]))('given feature %s', (feature, i) => { + describe.each(scanners.map((scanner, i) => [scanner, i]))('given scanner %s', (scanner, i) => { it('should match strings', () => { - expect(wrapper.text()).toContain(feature.name); - expect(wrapper.text()).toContain(feature.description); - if (feature.type === REPORT_TYPE_SAST) { - expect(wrapper.findByTestId(feature.type).text()).toBe('Configure via Merge Request'); - } else if (feature.type !== REPORT_TYPE_SECRET_DETECTION) { - expect(wrapper.findByTestId(feature.type).text()).toMatchInterpolatedText(UPGRADE_CTA); + expect(wrapper.text()).toContain(scanner.name); + expect(wrapper.text()).toContain(scanner.description); + if (scanner.type === REPORT_TYPE_SAST) { + expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via Merge Request'); + } else if (scanner.type !== REPORT_TYPE_SECRET_DETECTION) { + expect(wrapper.findByTestId(scanner.type).text()).toMatchInterpolatedText(UPGRADE_CTA); } }); it('should show expected help link', () => { const helpLink = findHelpLinks().at(i); - expect(helpLink.attributes('href')).toBe(feature.helpPath); + expect(helpLink.attributes('href')).toBe(scanner.helpPath); }); }); }); diff --git a/spec/frontend/security_configuration/upgrade_spec.js b/spec/frontend/security_configuration/upgrade_spec.js index b516b926dc51389d4041d5539d97ed22525ad215..1f0cc795fc527af8f152c2d555b708d1fd3a8260 100644 --- a/spec/frontend/security_configuration/upgrade_spec.js +++ b/spec/frontend/security_configuration/upgrade_spec.js @@ -1,5 +1,5 @@ import { mount } from '@vue/test-utils'; -import { UPGRADE_CTA } from '~/security_configuration/components/features_constants'; +import { UPGRADE_CTA } from '~/security_configuration/components/scanners_constants'; import Upgrade from '~/security_configuration/components/upgrade.vue'; const TEST_URL = 'http://www.example.test';