diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/frontend/security_configuration | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) | |
download | gitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'spec/frontend/security_configuration')
4 files changed, 270 insertions, 102 deletions
diff --git a/spec/frontend/security_configuration/components/app_spec.js b/spec/frontend/security_configuration/components/app_spec.js index d4ee9e6e43d..0a2b18caf25 100644 --- a/spec/frontend/security_configuration/components/app_spec.js +++ b/spec/frontend/security_configuration/components/app_spec.js @@ -20,6 +20,7 @@ import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, } from '~/security_configuration/components/constants'; import FeatureCard from '~/security_configuration/components/feature_card.vue'; +import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; import UpgradeBanner from '~/security_configuration/components/upgrade_banner.vue'; import { @@ -39,7 +40,11 @@ describe('App component', () => { let wrapper; let userCalloutDismissSpy; - const createComponent = ({ shouldShowCallout = true, ...propsData }) => { + const createComponent = ({ + shouldShowCallout = true, + secureVulnerabilityTraining = true, + ...propsData + }) => { userCalloutDismissSpy = jest.fn(); wrapper = extendedWrapper( @@ -50,6 +55,9 @@ describe('App component', () => { autoDevopsHelpPagePath, autoDevopsPath, projectPath, + glFeatures: { + secureVulnerabilityTraining, + }, }, stubs: { ...stubChildren(SecurityConfigurationApp), @@ -71,6 +79,7 @@ describe('App component', () => { const findTabs = () => wrapper.findAllComponents(GlTab); const findByTestId = (id) => wrapper.findByTestId(id); const findFeatureCards = () => wrapper.findAllComponents(FeatureCard); + const findTrainingProviderList = () => wrapper.findComponent(TrainingProviderList); const findManageViaMRErrorAlert = () => wrapper.findByTestId('manage-via-mr-error-alert'); const findLink = ({ href, text, container = wrapper }) => { const selector = `a[href="${href}"]`; @@ -138,20 +147,20 @@ describe('App component', () => { expect(mainHeading.text()).toContain('Security Configuration'); }); - it('renders GlTab Component ', () => { - expect(findTab().exists()).toBe(true); - }); + describe('tabs', () => { + const expectedTabs = ['security-testing', 'compliance-testing', 'vulnerability-management']; - it('renders right amount of tabs with correct title ', () => { - expect(findTabs()).toHaveLength(2); - }); + it('renders GlTab Component', () => { + expect(findTab().exists()).toBe(true); + }); - it('renders security-testing tab', () => { - expect(findByTestId('security-testing-tab').exists()).toBe(true); - }); + it('renders correct amount of tabs', () => { + expect(findTabs()).toHaveLength(expectedTabs.length); + }); - it('renders compliance-testing tab', () => { - expect(findByTestId('compliance-testing-tab').exists()).toBe(true); + it.each(expectedTabs)('renders the %s tab', (tabName) => { + expect(findByTestId(`${tabName}-tab`).exists()).toBe(true); + }); }); it('renders right amount of feature cards for given props with correct props', () => { @@ -173,6 +182,10 @@ describe('App component', () => { expect(findComplianceViewHistoryLink().exists()).toBe(false); expect(findSecurityViewHistoryLink().exists()).toBe(false); }); + + it('renders TrainingProviderList component', () => { + expect(findTrainingProviderList().exists()).toBe(true); + }); }); describe('Manage via MR Error Alert', () => { @@ -418,4 +431,22 @@ describe('App component', () => { expect(findSecurityViewHistoryLink().attributes('href')).toBe('test/historyPath'); }); }); + + describe('when secureVulnerabilityTraining feature flag is disabled', () => { + beforeEach(() => { + createComponent({ + augmentedSecurityFeatures: securityFeaturesMock, + augmentedComplianceFeatures: complianceFeaturesMock, + secureVulnerabilityTraining: false, + }); + }); + + it('renders correct amount of tabs', () => { + expect(findTabs()).toHaveLength(2); + }); + + it('does not render the vulnerability-management tab', () => { + expect(wrapper.findByTestId('vulnerability-management-tab').exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/security_configuration/components/training_provider_list_spec.js b/spec/frontend/security_configuration/components/training_provider_list_spec.js new file mode 100644 index 00000000000..60cc36a634c --- /dev/null +++ b/spec/frontend/security_configuration/components/training_provider_list_spec.js @@ -0,0 +1,88 @@ +import { GlLink, GlToggle, GlCard, GlSkeletonLoader } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; +import waitForPromises from 'helpers/wait_for_promises'; +import { securityTrainingProviders, mockResolvers } from '../mock_data'; + +Vue.use(VueApollo); + +describe('TrainingProviderList component', () => { + let wrapper; + let mockApollo; + let mockSecurityTrainingProvidersData; + + const createComponent = () => { + mockApollo = createMockApollo([], mockResolvers); + + wrapper = shallowMount(TrainingProviderList, { + apolloProvider: mockApollo, + }); + }; + + const waitForQueryToBeLoaded = () => waitForPromises(); + + const findCards = () => wrapper.findAllComponents(GlCard); + const findLinks = () => wrapper.findAllComponents(GlLink); + const findToggles = () => wrapper.findAllComponents(GlToggle); + const findLoader = () => wrapper.findComponent(GlSkeletonLoader); + + beforeEach(() => { + mockSecurityTrainingProvidersData = jest.fn(); + mockSecurityTrainingProvidersData.mockResolvedValue(securityTrainingProviders); + + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + mockApollo = null; + }); + + describe('when loading', () => { + it('shows the loader', () => { + expect(findLoader().exists()).toBe(true); + }); + + it('does not show the cards', () => { + expect(findCards().exists()).toBe(false); + }); + }); + + describe('basic structure', () => { + beforeEach(async () => { + await waitForQueryToBeLoaded(); + }); + + it('renders correct amount of cards', () => { + expect(findCards()).toHaveLength(securityTrainingProviders.length); + }); + + securityTrainingProviders.forEach(({ name, description, url, isEnabled }, index) => { + it(`shows the name for card ${index}`, () => { + expect(findCards().at(index).text()).toContain(name); + }); + + it(`shows the description for card ${index}`, () => { + expect(findCards().at(index).text()).toContain(description); + }); + + it(`shows the learn more link for card ${index}`, () => { + expect(findLinks().at(index).attributes()).toEqual({ + target: '_blank', + href: url, + }); + }); + + it(`shows the toggle with the correct value for card ${index}`, () => { + expect(findToggles().at(index).props('value')).toEqual(isEnabled); + }); + + it('does not show loader when query is populated', () => { + expect(findLoader().exists()).toBe(false); + }); + }); + }); +}); diff --git a/spec/frontend/security_configuration/mock_data.js b/spec/frontend/security_configuration/mock_data.js new file mode 100644 index 00000000000..cdb859c3800 --- /dev/null +++ b/spec/frontend/security_configuration/mock_data.js @@ -0,0 +1,30 @@ +export const securityTrainingProviders = [ + { + id: 101, + name: 'Kontra', + description: 'Interactive developer security education.', + url: 'https://application.security/', + isEnabled: false, + }, + { + id: 102, + name: 'SecureCodeWarrior', + description: 'Security training with guide and learning pathways.', + url: 'https://www.securecodewarrior.com/', + isEnabled: true, + }, +]; + +export const securityTrainingProvidersResponse = { + data: { + securityTrainingProviders, + }, +}; + +export const mockResolvers = { + Query: { + securityTrainingProviders() { + return securityTrainingProviders; + }, + }, +}; diff --git a/spec/frontend/security_configuration/utils_spec.js b/spec/frontend/security_configuration/utils_spec.js index eaed4532baa..241e69204d2 100644 --- a/spec/frontend/security_configuration/utils_spec.js +++ b/spec/frontend/security_configuration/utils_spec.js @@ -1,101 +1,120 @@ -import { augmentFeatures } from '~/security_configuration/utils'; - -const mockSecurityFeatures = [ - { - name: 'SAST', - type: 'SAST', - }, -]; - -const mockComplianceFeatures = [ - { - name: 'LICENSE_COMPLIANCE', - type: 'LICENSE_COMPLIANCE', - }, -]; - -const mockFeaturesWithSecondary = [ - { - name: 'DAST', - type: 'DAST', - secondary: { - type: 'DAST PROFILES', - name: 'DAST PROFILES', +import { augmentFeatures, translateScannerNames } from '~/security_configuration/utils'; +import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; + +describe('augmentFeatures', () => { + const mockSecurityFeatures = [ + { + name: 'SAST', + type: 'SAST', }, - }, -]; - -const mockInvalidCustomFeature = [ - { - foo: 'bar', - }, -]; - -const mockValidCustomFeature = [ - { - name: 'SAST', - type: 'SAST', - customField: 'customvalue', - }, -]; - -const mockValidCustomFeatureSnakeCase = [ - { - name: 'SAST', - type: 'SAST', - custom_field: 'customvalue', - }, -]; - -const expectedOutputDefault = { - augmentedSecurityFeatures: mockSecurityFeatures, - augmentedComplianceFeatures: mockComplianceFeatures, -}; - -const expectedOutputSecondary = { - augmentedSecurityFeatures: mockSecurityFeatures, - augmentedComplianceFeatures: mockFeaturesWithSecondary, -}; - -const expectedOutputCustomFeature = { - augmentedSecurityFeatures: mockValidCustomFeature, - augmentedComplianceFeatures: mockComplianceFeatures, -}; - -describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => { - it('given an empty array', () => { - expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual( - expectedOutputDefault, - ); + ]; + + const mockComplianceFeatures = [ + { + name: 'LICENSE_COMPLIANCE', + type: 'LICENSE_COMPLIANCE', + }, + ]; + + const mockFeaturesWithSecondary = [ + { + name: 'DAST', + type: 'DAST', + secondary: { + type: 'DAST PROFILES', + name: 'DAST PROFILES', + }, + }, + ]; + + const mockInvalidCustomFeature = [ + { + foo: 'bar', + }, + ]; + + const mockValidCustomFeature = [ + { + name: 'SAST', + type: 'SAST', + customField: 'customvalue', + }, + ]; + + const mockValidCustomFeatureSnakeCase = [ + { + name: 'SAST', + type: 'SAST', + custom_field: 'customvalue', + }, + ]; + + const expectedOutputDefault = { + augmentedSecurityFeatures: mockSecurityFeatures, + augmentedComplianceFeatures: mockComplianceFeatures, + }; + + const expectedOutputSecondary = { + augmentedSecurityFeatures: mockSecurityFeatures, + augmentedComplianceFeatures: mockFeaturesWithSecondary, + }; + + const expectedOutputCustomFeature = { + augmentedSecurityFeatures: mockValidCustomFeature, + augmentedComplianceFeatures: mockComplianceFeatures, + }; + + describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => { + it('given an empty array', () => { + expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual( + expectedOutputDefault, + ); + }); + + it('given an invalid populated array', () => { + expect( + augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature), + ).toEqual(expectedOutputDefault); + }); + + it('features have secondary key', () => { + expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual( + expectedOutputSecondary, + ); + }); + + it('given a valid populated array', () => { + expect( + augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature), + ).toEqual(expectedOutputCustomFeature); + }); }); - it('given an invalid populated array', () => { - expect( - augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature), - ).toEqual(expectedOutputDefault); + describe('returns an object with camelcased keys', () => { + it('given a customfeature in snakecase', () => { + expect( + augmentFeatures( + mockSecurityFeatures, + mockComplianceFeatures, + mockValidCustomFeatureSnakeCase, + ), + ).toEqual(expectedOutputCustomFeature); + }); }); +}); - it('features have secondary key', () => { - expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual( - expectedOutputSecondary, - ); +describe('translateScannerNames', () => { + it.each(['', undefined, null, 1, 'UNKNOWN_SCANNER_KEY'])('returns %p as is', (key) => { + expect(translateScannerNames([key])).toEqual([key]); }); - it('given a valid populated array', () => { - expect( - augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature), - ).toEqual(expectedOutputCustomFeature); + it('returns an empty array if no input is provided', () => { + expect(translateScannerNames([])).toEqual([]); }); -}); -describe('returns an object with camelcased keys', () => { - it('given a customfeature in snakecase', () => { - expect( - augmentFeatures( - mockSecurityFeatures, - mockComplianceFeatures, - mockValidCustomFeatureSnakeCase, - ), - ).toEqual(expectedOutputCustomFeature); + it('returns translated scanner names', () => { + expect(translateScannerNames(Object.keys(SCANNER_NAMES_MAP))).toEqual( + Object.values(SCANNER_NAMES_MAP), + ); }); }); |