From 7a15fb07cf363079c4c4683850ee131d80e75f75 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 15 Sep 2023 18:11:45 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../components/source_branch_dropdown_spec.js | 146 +++++++++++++++------ spec/frontend/jira_connect/branches/mock_data.js | 15 +++ .../frontend/search/sidebar/components/app_spec.js | 74 +++++++---- .../sidebar/components/notes_filters_spec.js | 28 ++++ .../sidebar/components/projects_filters_spec.js | 28 ++++ .../sidebar/components/projects_filters_specs.js | 28 ---- .../continuous_vulnerability_scan_spec.js | 124 +++++++++++++++++ .../components/feature_card_spec.js | 18 +++ 8 files changed, 364 insertions(+), 97 deletions(-) create mode 100644 spec/frontend/search/sidebar/components/notes_filters_spec.js create mode 100644 spec/frontend/search/sidebar/components/projects_filters_spec.js delete mode 100644 spec/frontend/search/sidebar/components/projects_filters_specs.js create mode 100644 spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js (limited to 'spec/frontend') diff --git a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js index cf2dacb50d8..95658f66d09 100644 --- a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js +++ b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js @@ -1,58 +1,45 @@ -import { GlCollapsibleListbox } from '@gitlab/ui'; -import { mount, shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import { GlCollapsibleListbox } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; + import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue'; import { BRANCHES_PER_PAGE } from '~/jira_connect/branches/constants'; import getProjectQuery from '~/jira_connect/branches/graphql/queries/get_project.query.graphql'; -import { mockProjects } from '../mock_data'; - -const mockProject = { - id: 'test', - repository: { - branchNames: ['main', 'f-test', 'release'], - rootRef: 'main', - }, -}; -const mockSelectedProject = mockProjects[0]; - -const mockProjectQueryResponse = { - data: { - project: mockProject, - }, -}; -const mockGetProjectQuery = jest.fn().mockResolvedValue(mockProjectQueryResponse); -const mockQueryLoading = jest.fn().mockReturnValue(new Promise(() => {})); +import { + mockBranchNames, + mockBranchNames2, + mockProjects, + mockProjectQueryResponse, +} from '../mock_data'; + +Vue.use(VueApollo); describe('SourceBranchDropdown', () => { let wrapper; + const mockSelectedProject = mockProjects[0]; + const querySuccessHandler = jest.fn().mockResolvedValue(mockProjectQueryResponse()); + const queryLoadingHandler = jest.fn().mockReturnValue(new Promise(() => {})); + const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); - const assertListboxItems = () => { + const assertListboxItems = (branchNames = mockBranchNames) => { const listboxItems = findListbox().props('items'); - expect(listboxItems).toHaveLength(mockProject.repository.branchNames.length); - expect(listboxItems.map((item) => item.text)).toEqual(mockProject.repository.branchNames); + expect(listboxItems).toHaveLength(branchNames.length); + expect(listboxItems.map((item) => item.text)).toEqual(branchNames); }; - function createMockApolloProvider({ getProjectQueryLoading = false } = {}) { - Vue.use(VueApollo); - - const mockApollo = createMockApollo([ - [getProjectQuery, getProjectQueryLoading ? mockQueryLoading : mockGetProjectQuery], - ]); + const createComponent = ({ props, handler = querySuccessHandler } = {}) => { + const mockApollo = createMockApollo([[getProjectQuery, handler]]); - return mockApollo; - } - - function createComponent({ mockApollo, props, mountFn = shallowMount } = {}) { - wrapper = mountFn(SourceBranchDropdown, { - apolloProvider: mockApollo || createMockApolloProvider(), + wrapper = shallowMount(SourceBranchDropdown, { + apolloProvider: mockApollo, propsData: props, }); - } + }; describe('when `selectedProject` prop is not specified', () => { beforeEach(() => { @@ -78,6 +65,7 @@ describe('SourceBranchDropdown', () => { loading: false, searchable: true, searching: false, + selected: null, toggleText: 'Select a branch', }); }); @@ -92,23 +80,26 @@ describe('SourceBranchDropdown', () => { describe('when branches are loading', () => { it('sets loading prop to true', () => { createComponent({ - mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }), props: { selectedProject: mockSelectedProject }, + handler: queryLoadingHandler, }); - expect(findListbox().props('loading')).toEqual(true); + expect(findListbox().props('loading')).toBe(true); }); }); describe('when branches have loaded', () => { describe('when searching branches', () => { it('triggers a refetch', async () => { - createComponent({ mountFn: mount, props: { selectedProject: mockSelectedProject } }); + createComponent({ props: { selectedProject: mockSelectedProject } }); await waitForPromises(); const mockSearchTerm = 'mai'; + expect(querySuccessHandler).toHaveBeenCalledTimes(1); + await findListbox().vm.$emit('search', mockSearchTerm); - expect(mockGetProjectQuery).toHaveBeenCalledWith({ + expect(querySuccessHandler).toHaveBeenCalledTimes(2); + expect(querySuccessHandler).toHaveBeenLastCalledWith({ branchNamesLimit: BRANCHES_PER_PAGE, branchNamesOffset: 0, branchNamesSearchPattern: `*${mockSearchTerm}*`, @@ -129,10 +120,15 @@ describe('SourceBranchDropdown', () => { loading: false, searchable: true, searching: false, + selected: null, toggleText: 'Select a branch', }); }); + it('disables infinite scroll', () => { + expect(findListbox().props('infiniteScroll')).toBe(false); + }); + it('omits monospace styling from listbox', () => { expect(findListbox().classes()).not.toContain('gl-font-monospace'); }); @@ -142,19 +138,19 @@ describe('SourceBranchDropdown', () => { }); it("emits `change` event with the repository's `rootRef` by default", () => { - expect(wrapper.emitted('change')[0]).toEqual([mockProject.repository.rootRef]); + expect(wrapper.emitted('change')[0]).toEqual([mockBranchNames[0]]); }); describe('when selecting a listbox item', () => { it('emits `change` event with the selected branch name', () => { - const mockBranchName = mockProject.repository.branchNames[1]; + const mockBranchName = mockBranchNames[1]; findListbox().vm.$emit('select', mockBranchName); expect(wrapper.emitted('change')[1]).toEqual([mockBranchName]); }); }); describe('when `selectedBranchName` prop is specified', () => { - const mockBranchName = mockProject.repository.branchNames[2]; + const mockBranchName = mockBranchNames[2]; beforeEach(() => { wrapper.setProps({ @@ -162,6 +158,10 @@ describe('SourceBranchDropdown', () => { }); }); + it('sets listbox selected to `selectedBranchName`', () => { + expect(findListbox().props('selected')).toBe(mockBranchName); + }); + it('sets listbox text to `selectedBranchName` value', () => { expect(findListbox().props('toggleText')).toBe(mockBranchName); }); @@ -170,6 +170,66 @@ describe('SourceBranchDropdown', () => { expect(findListbox().classes()).toContain('gl-font-monospace'); }); }); + + describe('when full page of branches returns', () => { + const fullPageBranchNames = Array(BRANCHES_PER_PAGE) + .fill(1) + .map((_, i) => mockBranchNames[i % mockBranchNames.length]); + + beforeEach(async () => { + createComponent({ + props: { selectedProject: mockSelectedProject }, + handler: () => Promise.resolve(mockProjectQueryResponse(fullPageBranchNames)), + }); + await waitForPromises(); + }); + + it('enables infinite scroll', () => { + expect(findListbox().props('infiniteScroll')).toBe(true); + }); + }); + }); + + describe('when loading more branches from infinite scroll', () => { + const queryLoadMoreHandler = jest.fn(); + + beforeEach(async () => { + queryLoadMoreHandler.mockResolvedValueOnce(mockProjectQueryResponse()); + queryLoadMoreHandler.mockResolvedValueOnce(mockProjectQueryResponse(mockBranchNames2)); + createComponent({ + props: { selectedProject: mockSelectedProject }, + handler: queryLoadMoreHandler, + }); + + await waitForPromises(); + + await findListbox().vm.$emit('bottom-reached'); + }); + + it('sets loading more prop to true', () => { + expect(findListbox().props('infiniteScrollLoading')).toBe(true); + }); + + it('triggers load more query', () => { + expect(queryLoadMoreHandler).toHaveBeenLastCalledWith({ + branchNamesLimit: BRANCHES_PER_PAGE, + branchNamesOffset: 3, + branchNamesSearchPattern: '*', + projectPath: 'test-path', + }); + }); + + it('renders available source branches as listbox items', async () => { + await waitForPromises(); + + assertListboxItems([...mockBranchNames, ...mockBranchNames2]); + }); + + it('sets loading more prop to false once done', async () => { + await waitForPromises(); + + expect(findListbox().props('infiniteScrollLoading')).toBe(false); + }); }); }); }); diff --git a/spec/frontend/jira_connect/branches/mock_data.js b/spec/frontend/jira_connect/branches/mock_data.js index 742ab5392c8..1720e0118c8 100644 --- a/spec/frontend/jira_connect/branches/mock_data.js +++ b/spec/frontend/jira_connect/branches/mock_data.js @@ -1,3 +1,6 @@ +export const mockBranchNames = ['main', 'f-test', 'release']; +export const mockBranchNames2 = ['dev', 'dev-1', 'dev-2']; + export const mockProjects = [ { id: 'test', @@ -28,3 +31,15 @@ export const mockProjects = [ }, }, ]; + +export const mockProjectQueryResponse = (branchNames = mockBranchNames) => ({ + data: { + project: { + id: 'gid://gitlab/Project/27', + repository: { + branchNames, + rootRef: 'main', + }, + }, + }, +}); diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js index 3944ba86942..72c9537bd61 100644 --- a/spec/frontend/search/sidebar/components/app_spec.js +++ b/spec/frontend/search/sidebar/components/app_spec.js @@ -2,7 +2,11 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; -import { SEARCH_TYPE_ZOEKT, SEARCH_TYPE_ADVANCED } from '~/search/sidebar/constants'; +import { + SEARCH_TYPE_ZOEKT, + SEARCH_TYPE_ADVANCED, + SEARCH_TYPE_BASIC, +} from '~/search/sidebar/constants'; import { MOCK_QUERY } from 'jest/search/mock_data'; import { toggleSuperSidebarCollapsed } from '~/super_sidebar/super_sidebar_collapsed_state_manager'; import GlobalSearchSidebar from '~/search/sidebar/components/app.vue'; @@ -10,6 +14,7 @@ import IssuesFilters from '~/search/sidebar/components/issues_filters.vue'; import MergeRequestsFilters from '~/search/sidebar/components/merge_requests_filters.vue'; import BlobsFilters from '~/search/sidebar/components/blobs_filters.vue'; import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue'; +import NotesFilters from '~/search/sidebar/components/notes_filters.vue'; import ScopeLegacyNavigation from '~/search/sidebar/components/scope_legacy_navigation.vue'; import SmallScreenDrawerNavigation from '~/search/sidebar/components/small_screen_drawer_navigation.vue'; import ScopeSidebarNavigation from '~/search/sidebar/components/scope_sidebar_navigation.vue'; @@ -37,6 +42,11 @@ describe('GlobalSearchSidebar', () => { wrapper = shallowMount(GlobalSearchSidebar, { store, + provide: { + glFeatures: { + searchNotesHideArchivedProjects: true, + }, + }, }); }; @@ -45,6 +55,7 @@ describe('GlobalSearchSidebar', () => { const findMergeRequestsFilters = () => wrapper.findComponent(MergeRequestsFilters); const findBlobsFilters = () => wrapper.findComponent(BlobsFilters); const findProjectsFilters = () => wrapper.findComponent(ProjectsFilters); + const findNotesFilters = () => wrapper.findComponent(NotesFilters); const findScopeLegacyNavigation = () => wrapper.findComponent(ScopeLegacyNavigation); const findSmallScreenDrawerNavigation = () => wrapper.findComponent(SmallScreenDrawerNavigation); const findScopeSidebarNavigation = () => wrapper.findComponent(ScopeSidebarNavigation); @@ -62,18 +73,26 @@ describe('GlobalSearchSidebar', () => { }); describe.each` - scope | filter - ${'issues'} | ${findIssuesFilters} - ${'merge_requests'} | ${findMergeRequestsFilters} - ${'blobs'} | ${findBlobsFilters} - `('with sidebar $scope scope:', ({ scope, filter }) => { + scope | filter | searchType | isShown + ${'issues'} | ${findIssuesFilters} | ${SEARCH_TYPE_BASIC} | ${true} + ${'merge_requests'} | ${findMergeRequestsFilters} | ${SEARCH_TYPE_BASIC} | ${true} + ${'projects'} | ${findProjectsFilters} | ${SEARCH_TYPE_BASIC} | ${true} + ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_BASIC} | ${false} + ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_ADVANCED} | ${true} + ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_ZOEKT} | ${false} + ${'notes'} | ${findNotesFilters} | ${SEARCH_TYPE_BASIC} | ${false} + ${'notes'} | ${findNotesFilters} | ${SEARCH_TYPE_ADVANCED} | ${true} + `('with sidebar $scope scope:', ({ scope, filter, searchType, isShown }) => { beforeEach(() => { getterSpies.currentScope = jest.fn(() => scope); - createComponent({ urlQuery: { scope }, searchType: SEARCH_TYPE_ADVANCED }); + createComponent({ urlQuery: { scope }, searchType }); }); - it(`shows filter ${filter.name.replace('find', '')}`, () => { - expect(filter().exists()).toBe(true); + it(`renders correctly filter ${filter.name.replace( + 'find', + '', + )} when search_type ${searchType}`, () => { + expect(filter().exists()).toBe(isShown); }); }); @@ -101,24 +120,27 @@ describe('GlobalSearchSidebar', () => { describe.each` currentScope | sidebarNavShown | legacyNavShown ${'issues'} | ${false} | ${true} - ${''} | ${false} | ${false} + ${'test'} | ${false} | ${true} ${'issues'} | ${true} | ${false} - ${''} | ${true} | ${false} - `('renders navigation', ({ currentScope, sidebarNavShown, legacyNavShown }) => { - beforeEach(() => { - getterSpies.currentScope = jest.fn(() => currentScope); - createComponent({ useSidebarNavigation: sidebarNavShown }); - }); - - it(`${!legacyNavShown ? 'hides' : 'shows'} the legacy navigation`, () => { - expect(findScopeLegacyNavigation().exists()).toBe(legacyNavShown); - expect(findSmallScreenDrawerNavigation().exists()).toBe(legacyNavShown); - }); - - it(`${!sidebarNavShown ? 'hides' : 'shows'} the sidebar navigation`, () => { - expect(findScopeSidebarNavigation().exists()).toBe(sidebarNavShown); - }); - }); + ${'test'} | ${true} | ${false} + `( + 'renders navigation for scope $currentScope', + ({ currentScope, sidebarNavShown, legacyNavShown }) => { + beforeEach(() => { + getterSpies.currentScope = jest.fn(() => currentScope); + createComponent({ useSidebarNavigation: sidebarNavShown }); + }); + + it(`renders navigation correctly with legacyNavShown ${legacyNavShown}`, () => { + expect(findScopeLegacyNavigation().exists()).toBe(legacyNavShown); + expect(findSmallScreenDrawerNavigation().exists()).toBe(legacyNavShown); + }); + + it(`renders navigation correctly with sidebarNavShown ${sidebarNavShown}`, () => { + expect(findScopeSidebarNavigation().exists()).toBe(sidebarNavShown); + }); + }, + ); }); describe('when useSidebarNavigation=true', () => { diff --git a/spec/frontend/search/sidebar/components/notes_filters_spec.js b/spec/frontend/search/sidebar/components/notes_filters_spec.js new file mode 100644 index 00000000000..2fb8e731ef5 --- /dev/null +++ b/spec/frontend/search/sidebar/components/notes_filters_spec.js @@ -0,0 +1,28 @@ +import { shallowMount } from '@vue/test-utils'; +import NotesFilters from '~/search/sidebar/components/projects_filters.vue'; +import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue'; +import FiltersTemplate from '~/search/sidebar/components/filters_template.vue'; + +describe('GlobalSearch ProjectsFilters', () => { + let wrapper; + + const findArchivedFilter = () => wrapper.findComponent(ArchivedFilter); + const findFiltersTemplate = () => wrapper.findComponent(FiltersTemplate); + + const createComponent = () => { + wrapper = shallowMount(NotesFilters); + }; + + describe('Renders correctly', () => { + beforeEach(() => { + createComponent(); + }); + it('renders ArchivedFilter', () => { + expect(findArchivedFilter().exists()).toBe(true); + }); + + it('renders FiltersTemplate', () => { + expect(findFiltersTemplate().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/search/sidebar/components/projects_filters_spec.js b/spec/frontend/search/sidebar/components/projects_filters_spec.js new file mode 100644 index 00000000000..930b7263ea4 --- /dev/null +++ b/spec/frontend/search/sidebar/components/projects_filters_spec.js @@ -0,0 +1,28 @@ +import { shallowMount } from '@vue/test-utils'; +import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue'; +import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue'; +import FiltersTemplate from '~/search/sidebar/components/filters_template.vue'; + +describe('GlobalSearch ProjectsFilters', () => { + let wrapper; + + const findArchivedFilter = () => wrapper.findComponent(ArchivedFilter); + const findFiltersTemplate = () => wrapper.findComponent(FiltersTemplate); + + const createComponent = () => { + wrapper = shallowMount(ProjectsFilters); + }; + + describe('Renders correctly', () => { + beforeEach(() => { + createComponent(); + }); + it('renders ArchivedFilter', () => { + expect(findArchivedFilter().exists()).toBe(true); + }); + + it('renders FiltersTemplate', () => { + expect(findFiltersTemplate().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/search/sidebar/components/projects_filters_specs.js b/spec/frontend/search/sidebar/components/projects_filters_specs.js deleted file mode 100644 index 15e3254e289..00000000000 --- a/spec/frontend/search/sidebar/components/projects_filters_specs.js +++ /dev/null @@ -1,28 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue'; -import ArchivedFilter from '~/search/sidebar/components/language_filter/index.vue'; -import FiltersTemplate from '~/search/sidebar/components/filters_template.vue'; - -describe('GlobalSearch ProjectsFilters', () => { - let wrapper; - - const findArchivedFilter = () => wrapper.findComponent(ArchivedFilter); - const findFiltersTemplate = () => wrapper.findComponent(FiltersTemplate); - - const createComponent = () => { - wrapper = shallowMount(ProjectsFilters); - }; - - describe('Renders correctly', () => { - beforeEach(() => { - createComponent(); - }); - it('renders ArchivedFilter', () => { - expect(findArchivedFilter().exists()).toBe(true); - }); - - it('renders FiltersTemplate', () => { - expect(findFiltersTemplate().exists()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js b/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js new file mode 100644 index 00000000000..84a468e4dd8 --- /dev/null +++ b/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js @@ -0,0 +1,124 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlBadge, GlToggle } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import Vue from 'vue'; +import ProjectSetContinuousVulnerabilityScanning from '~/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql'; +import ContinuousVulnerabilityScan from '~/security_configuration/components/continuous_vulnerability_scan.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; + +Vue.use(VueApollo); + +const setCVSMockResponse = { + data: { + projectSetContinuousVulnerabilityScanning: { + continuousVulnerabilityScanningEnabled: true, + errors: [], + }, + }, +}; + +const defaultProvide = { + continuousVulnerabilityScansEnabled: true, + projectFullPath: 'project/full/path', +}; + +describe('ContinuousVulnerabilityScan', () => { + let wrapper; + let apolloProvider; + let requestHandlers; + + const createComponent = (options) => { + requestHandlers = { + setCVSMutationHandler: jest.fn().mockResolvedValue(setCVSMockResponse), + }; + + apolloProvider = createMockApollo([ + [ProjectSetContinuousVulnerabilityScanning, requestHandlers.setCVSMutationHandler], + ]); + + wrapper = shallowMount(ContinuousVulnerabilityScan, { + propsData: { + feature: { + available: true, + configured: true, + }, + }, + provide: { + glFeatures: { + dependencyScanningOnAdvisoryIngestion: true, + }, + ...defaultProvide, + }, + apolloProvider, + ...options, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + apolloProvider = null; + }); + + const findBadge = () => wrapper.findComponent(GlBadge); + const findToggle = () => wrapper.findComponent(GlToggle); + + it('renders the component', () => { + expect(wrapper.exists()).toBe(true); + }); + + it('renders the correct title', () => { + expect(wrapper.text()).toContain('Continuous Vulnerability Scan'); + }); + + it('renders the badge and toggle component with correct values', () => { + expect(findBadge().exists()).toBe(true); + expect(findBadge().text()).toBe('Experiment'); + + expect(findToggle().exists()).toBe(true); + expect(findToggle().props('value')).toBe(defaultProvide.continuousVulnerabilityScansEnabled); + }); + + it('should disable toggle when feature is not configured', () => { + createComponent({ + propsData: { + feature: { + available: true, + configured: false, + }, + }, + }); + expect(findToggle().props('disabled')).toBe(true); + }); + + it('calls mutation on toggle change with correct payload', () => { + findToggle().vm.$emit('change', true); + + expect(requestHandlers.setCVSMutationHandler).toHaveBeenCalledWith({ + input: { + projectPath: 'project/full/path', + enable: true, + }, + }); + }); + + describe('when feature flag is disabled', () => { + beforeEach(() => { + createComponent({ + provide: { + glFeatures: { + dependencyScanningOnAdvisoryIngestion: false, + }, + ...defaultProvide, + }, + }); + }); + + it('should not render toggle and badge', () => { + expect(findToggle().exists()).toBe(false); + expect(findBadge().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/security_configuration/components/feature_card_spec.js b/spec/frontend/security_configuration/components/feature_card_spec.js index 983a66a7fd3..c715d01dd58 100644 --- a/spec/frontend/security_configuration/components/feature_card_spec.js +++ b/spec/frontend/security_configuration/components/feature_card_spec.js @@ -1,5 +1,6 @@ import { GlIcon } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; +import Vue from 'vue'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { securityFeatures } from '~/security_configuration/components/constants'; import FeatureCard from '~/security_configuration/components/feature_card.vue'; @@ -13,6 +14,10 @@ import { import { manageViaMRErrorMessage } from '../constants'; import { makeFeature } from './utils'; +const MockComponent = Vue.component('MockComponent', { + render: (createElement) => createElement('span'), +}); + describe('FeatureCard component', () => { let feature; let wrapper; @@ -389,4 +394,17 @@ describe('FeatureCard component', () => { }); }); }); + + describe('when a slot component is passed', () => { + beforeEach(() => { + feature = makeFeature({ + slotComponent: MockComponent, + }); + createComponent({ feature }); + }); + + it('renders the component properly', () => { + expect(wrapper.findComponent(MockComponent).exists()).toBe(true); + }); + }); }); -- cgit v1.2.3