diff options
Diffstat (limited to 'spec')
17 files changed, 233 insertions, 328 deletions
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb index b5cd14154a3..4a6e745cd63 100644 --- a/spec/controllers/repositories/git_http_controller_spec.rb +++ b/spec/controllers/repositories/git_http_controller_spec.rb @@ -90,6 +90,14 @@ RSpec.describe Repositories::GitHttpController do end end end + + context 'when the user is a deploy token' do + it_behaves_like Repositories::GitHttpController do + let(:container) { project } + let(:user) { create(:deploy_token, :project, projects: [project]) } + let(:access_checker_class) { Gitlab::GitAccess } + end + end end context 'when repository container is a project wiki' do diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index 1cae7cdeb16..0ce50107e54 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -85,33 +85,6 @@ RSpec.describe 'Groups > Members > Manage members' do property: 'existing_user', user: user1 ) - expect_no_snowplow_event( - category: 'Members::CreateService', - action: 'area_of_focus' - ) - end - - it 'adds a user to group with area_of_focus', :js, :snowplow, :aggregate_failures do - stub_experiments(member_areas_of_focus: :candidate) - group.add_owner(user1) - - visit group_group_members_path(group) - - invite_member(user2.name, role: 'Reporter', area_of_focus: true) - wait_for_requests - - expect_snowplow_event( - category: 'Members::CreateService', - action: 'area_of_focus', - label: 'Contribute to the codebase', - property: group.members.last.id.to_s - ) - expect_snowplow_event( - category: 'Members::CreateService', - action: 'area_of_focus', - label: 'Collaborate on open issues and merge requests', - property: group.members.last.id.to_s - ) end it 'do not disclose email addresses', :js do @@ -221,36 +194,9 @@ RSpec.describe 'Groups > Members > Manage members' do property: 'net_new_user', user: user1 ) - expect_no_snowplow_event( - category: 'Members::CreateService', - action: 'area_of_focus' - ) end end - it 'invite user to group with area_of_focus', :js, :snowplow, :aggregate_failures do - stub_experiments(member_areas_of_focus: :candidate) - group.add_owner(user1) - - visit group_group_members_path(group) - - invite_member('test@example.com', role: 'Reporter', area_of_focus: true) - wait_for_requests - - expect_snowplow_event( - category: 'Members::InviteService', - action: 'area_of_focus', - label: 'Contribute to the codebase', - property: group.members.last.id.to_s - ) - expect_snowplow_event( - category: 'Members::InviteService', - action: 'area_of_focus', - label: 'Collaborate on open issues and merge requests', - property: group.members.last.id.to_s - ) - end - context 'when user is a guest' do before do group.add_guest(user1) diff --git a/spec/features/one_trust_spec.rb b/spec/features/one_trust_spec.rb new file mode 100644 index 00000000000..0ed08e8b99b --- /dev/null +++ b/spec/features/one_trust_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'OneTrust' do + context 'almost there page' do + context 'when OneTrust is enabled' do + let_it_be(:onetrust_url) { 'https://*.onetrust.com' } + let_it_be(:one_trust_id) { SecureRandom.uuid } + + before do + stub_config(extra: { one_trust_id: one_trust_id }) + stub_feature_flags(ecomm_instrumentation: true) + visit users_almost_there_path + end + + it 'has the OneTrust CSP settings', :aggregate_failures do + expect(response_headers['Content-Security-Policy']).to include("#{onetrust_url}") + expect(page.html).to include("https://cdn.cookielaw.org/consent/#{one_trust_id}/OtAutoBlock.js") + end + end + end +end diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js index 7ecbef8af4b..194846c410a 100644 --- a/spec/frontend/header_search/components/app_spec.js +++ b/spec/frontend/header_search/components/app_spec.js @@ -6,6 +6,7 @@ import HeaderSearchApp from '~/header_search/components/app.vue'; import HeaderSearchAutocompleteItems from '~/header_search/components/header_search_autocomplete_items.vue'; import HeaderSearchDefaultItems from '~/header_search/components/header_search_default_items.vue'; import HeaderSearchScopedItems from '~/header_search/components/header_search_scoped_items.vue'; +import { SEARCH_INPUT_DESCRIPTION, SEARCH_RESULTS_DESCRIPTION } from '~/header_search/constants'; import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue'; import { ENTER_KEY } from '~/lib/utils/keys'; import { visitUrl } from '~/lib/utils/url_utility'; @@ -14,6 +15,7 @@ import { MOCK_SEARCH_QUERY, MOCK_USERNAME, MOCK_DEFAULT_SEARCH_OPTIONS, + MOCK_SCOPED_SEARCH_OPTIONS, } from '../mock_data'; Vue.use(Vuex); @@ -59,11 +61,26 @@ describe('HeaderSearchApp', () => { const findHeaderSearchAutocompleteItems = () => wrapper.findComponent(HeaderSearchAutocompleteItems); const findDropdownKeyboardNavigation = () => wrapper.findComponent(DropdownKeyboardNavigation); + const findSearchInputDescription = () => wrapper.find(`#${SEARCH_INPUT_DESCRIPTION}`); + const findSearchResultsDescription = () => wrapper.findByTestId(SEARCH_RESULTS_DESCRIPTION); describe('template', () => { - it('always renders Header Search Input', () => { - createComponent(); - expect(findHeaderSearchInput().exists()).toBe(true); + describe('always renders', () => { + beforeEach(() => { + createComponent(); + }); + + it('Header Search Input', () => { + expect(findHeaderSearchInput().exists()).toBe(true); + }); + + it('Search Input Description', () => { + expect(findSearchInputDescription().exists()).toBe(true); + }); + + it('Search Results Description', () => { + expect(findSearchResultsDescription().exists()).toBe(true); + }); }); describe.each` @@ -77,7 +94,7 @@ describe('HeaderSearchApp', () => { beforeEach(() => { window.gon.current_username = username; createComponent(); - wrapper.setData({ showDropdown }); + findHeaderSearchInput().vm.$emit(showDropdown ? 'click' : ''); }); it(`should${showSearchDropdown ? '' : ' not'} render`, () => { @@ -123,6 +140,53 @@ describe('HeaderSearchApp', () => { }); }, ); + + describe.each` + username | showDropdown | expectedDesc + ${null} | ${false} | ${HeaderSearchApp.i18n.searchInputDescribeByNoDropdown} + ${null} | ${true} | ${HeaderSearchApp.i18n.searchInputDescribeByNoDropdown} + ${MOCK_USERNAME} | ${false} | ${HeaderSearchApp.i18n.searchInputDescribeByWithDropdown} + ${MOCK_USERNAME} | ${true} | ${HeaderSearchApp.i18n.searchInputDescribeByWithDropdown} + `('Search Input Description', ({ username, showDropdown, expectedDesc }) => { + describe(`current_username is ${username} and showDropdown is ${showDropdown}`, () => { + beforeEach(() => { + window.gon.current_username = username; + createComponent(); + findHeaderSearchInput().vm.$emit(showDropdown ? 'click' : ''); + }); + + it(`sets description to ${expectedDesc}`, () => { + expect(findSearchInputDescription().text()).toBe(expectedDesc); + }); + }); + }); + + describe.each` + username | showDropdown | search | loading | searchOptions | expectedDesc + ${null} | ${true} | ${''} | ${false} | ${[]} | ${''} + ${MOCK_USERNAME} | ${false} | ${''} | ${false} | ${[]} | ${''} + ${MOCK_USERNAME} | ${true} | ${''} | ${false} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${`${MOCK_DEFAULT_SEARCH_OPTIONS.length} default results provided. Use the up and down arrow keys to navigate search results list.`} + ${MOCK_USERNAME} | ${true} | ${''} | ${true} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${`${MOCK_DEFAULT_SEARCH_OPTIONS.length} default results provided. Use the up and down arrow keys to navigate search results list.`} + ${MOCK_USERNAME} | ${true} | ${MOCK_SEARCH} | ${false} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${`Results updated. ${MOCK_SCOPED_SEARCH_OPTIONS.length} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit.`} + ${MOCK_USERNAME} | ${true} | ${MOCK_SEARCH} | ${true} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${HeaderSearchApp.i18n.searchResultsLoading} + `( + 'Search Results Description', + ({ username, showDropdown, search, loading, searchOptions, expectedDesc }) => { + describe(`search is ${search}, loading is ${loading}, and showSearchDropdown is ${ + Boolean(username) && showDropdown + }`, () => { + beforeEach(() => { + window.gon.current_username = username; + createComponent({ search, loading }, { searchOptions: () => searchOptions }); + findHeaderSearchInput().vm.$emit(showDropdown ? 'click' : ''); + }); + + it(`sets description to ${expectedDesc}`, () => { + expect(findSearchResultsDescription().text()).toBe(expectedDesc); + }); + }); + }, + ); }); describe('events', () => { diff --git a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js index ecfaa8e9443..bec0cbc8a5c 100644 --- a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js +++ b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js @@ -110,11 +110,11 @@ describe('HeaderSearchAutocompleteItems', () => { }); describe.each` - currentFocusedOption | isFocused - ${null} | ${false} - ${{ html_id: 'not-a-match' }} | ${false} - ${MOCK_SORTED_AUTOCOMPLETE_OPTIONS[0]} | ${true} - `('isOptionFocused', ({ currentFocusedOption, isFocused }) => { + currentFocusedOption | isFocused | ariaSelected + ${null} | ${false} | ${undefined} + ${{ html_id: 'not-a-match' }} | ${false} | ${undefined} + ${MOCK_SORTED_AUTOCOMPLETE_OPTIONS[0]} | ${true} | ${'true'} + `('isOptionFocused', ({ currentFocusedOption, isFocused, ariaSelected }) => { describe(`when currentFocusedOption.html_id is ${currentFocusedOption?.html_id}`, () => { beforeEach(() => { createComponent({}, {}, { currentFocusedOption }); @@ -123,6 +123,10 @@ describe('HeaderSearchAutocompleteItems', () => { it(`should${isFocused ? '' : ' not'} have gl-bg-gray-50 applied`, () => { expect(findFirstDropdownItem().classes('gl-bg-gray-50')).toBe(isFocused); }); + + it(`sets "aria-selected to ${ariaSelected}`, () => { + expect(findFirstDropdownItem().attributes('aria-selected')).toBe(ariaSelected); + }); }); }); }); diff --git a/spec/frontend/header_search/components/header_search_default_items_spec.js b/spec/frontend/header_search/components/header_search_default_items_spec.js index afad9b5b138..abcacc487df 100644 --- a/spec/frontend/header_search/components/header_search_default_items_spec.js +++ b/spec/frontend/header_search/components/header_search_default_items_spec.js @@ -83,11 +83,11 @@ describe('HeaderSearchDefaultItems', () => { }); describe.each` - currentFocusedOption | isFocused - ${null} | ${false} - ${{ html_id: 'not-a-match' }} | ${false} - ${MOCK_DEFAULT_SEARCH_OPTIONS[0]} | ${true} - `('isOptionFocused', ({ currentFocusedOption, isFocused }) => { + currentFocusedOption | isFocused | ariaSelected + ${null} | ${false} | ${undefined} + ${{ html_id: 'not-a-match' }} | ${false} | ${undefined} + ${MOCK_DEFAULT_SEARCH_OPTIONS[0]} | ${true} | ${'true'} + `('isOptionFocused', ({ currentFocusedOption, isFocused, ariaSelected }) => { describe(`when currentFocusedOption.html_id is ${currentFocusedOption?.html_id}`, () => { beforeEach(() => { createComponent({}, { currentFocusedOption }); @@ -96,6 +96,10 @@ describe('HeaderSearchDefaultItems', () => { it(`should${isFocused ? '' : ' not'} have gl-bg-gray-50 applied`, () => { expect(findFirstDropdownItem().classes('gl-bg-gray-50')).toBe(isFocused); }); + + it(`sets "aria-selected to ${ariaSelected}`, () => { + expect(findFirstDropdownItem().attributes('aria-selected')).toBe(ariaSelected); + }); }); }); }); diff --git a/spec/frontend/header_search/components/header_search_scoped_items_spec.js b/spec/frontend/header_search/components/header_search_scoped_items_spec.js index 2fdd03fdae3..a65b4d8b813 100644 --- a/spec/frontend/header_search/components/header_search_scoped_items_spec.js +++ b/spec/frontend/header_search/components/header_search_scoped_items_spec.js @@ -37,6 +37,8 @@ describe('HeaderSearchScopedItems', () => { const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); const findFirstDropdownItem = () => findDropdownItems().at(0); const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => trimText(w.text())); + const findDropdownItemAriaLabels = () => + findDropdownItems().wrappers.map((w) => trimText(w.attributes('aria-label'))); const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href')); describe('template', () => { @@ -56,6 +58,13 @@ describe('HeaderSearchScopedItems', () => { expect(findDropdownItemTitles()).toStrictEqual(expectedTitles); }); + it('renders aria-labels correctly', () => { + const expectedLabels = MOCK_SCOPED_SEARCH_OPTIONS.map((o) => + trimText(`${MOCK_SEARCH} ${o.description} ${o.scope || ''}`), + ); + expect(findDropdownItemAriaLabels()).toStrictEqual(expectedLabels); + }); + it('renders links correctly', () => { const expectedLinks = MOCK_SCOPED_SEARCH_OPTIONS.map((o) => o.url); expect(findDropdownItemLinks()).toStrictEqual(expectedLinks); @@ -63,11 +72,11 @@ describe('HeaderSearchScopedItems', () => { }); describe.each` - currentFocusedOption | isFocused - ${null} | ${false} - ${{ html_id: 'not-a-match' }} | ${false} - ${MOCK_SCOPED_SEARCH_OPTIONS[0]} | ${true} - `('isOptionFocused', ({ currentFocusedOption, isFocused }) => { + currentFocusedOption | isFocused | ariaSelected + ${null} | ${false} | ${undefined} + ${{ html_id: 'not-a-match' }} | ${false} | ${undefined} + ${MOCK_SCOPED_SEARCH_OPTIONS[0]} | ${true} | ${'true'} + `('isOptionFocused', ({ currentFocusedOption, isFocused, ariaSelected }) => { describe(`when currentFocusedOption.html_id is ${currentFocusedOption?.html_id}`, () => { beforeEach(() => { createComponent({}, { currentFocusedOption }); @@ -76,6 +85,10 @@ describe('HeaderSearchScopedItems', () => { it(`should${isFocused ? '' : ' not'} have gl-bg-gray-50 applied`, () => { expect(findFirstDropdownItem().classes('gl-bg-gray-50')).toBe(isFocused); }); + + it(`sets "aria-selected to ${ariaSelected}`, () => { + expect(findFirstDropdownItem().attributes('aria-selected')).toBe(ariaSelected); + }); }); }); }); diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js index 24df424b40b..f3cad3fc066 100644 --- a/spec/frontend/invite_members/components/invite_members_modal_spec.js +++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js @@ -6,7 +6,6 @@ import { GlSprintf, GlLink, GlModal, - GlFormCheckboxGroup, } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import { stubComponent } from 'helpers/stub_component'; @@ -19,7 +18,6 @@ import ModalConfetti from '~/invite_members/components/confetti.vue'; import MembersTokenSelect from '~/invite_members/components/members_token_select.vue'; import { INVITE_MEMBERS_IN_COMMENT, - MEMBER_AREAS_OF_FOCUS, INVITE_MEMBERS_FOR_TASK, CANCEL_BUTTON_TEXT, INVITE_BUTTON_TEXT, @@ -52,12 +50,7 @@ const inviteeType = 'members'; const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 }; const defaultAccessLevel = 10; const inviteSource = 'unknown'; -const noSelectionAreasOfFocus = ['no_selection']; const helpLink = 'https://example.com'; -const areasOfFocusOptions = [ - { text: 'area1', value: 'area1' }, - { text: 'area2', value: 'area2' }, -]; const tasksToBeDoneOptions = [ { text: 'First task', value: 'first' }, { text: 'Second task', value: 'second' }, @@ -96,9 +89,7 @@ const createComponent = (data = {}, props = {}) => { isProject, inviteeType, accessLevels, - areasOfFocusOptions, defaultAccessLevel, - noSelectionAreasOfFocus, tasksToBeDoneOptions, projects, helpLink, @@ -164,7 +155,6 @@ describe('InviteMembersModal', () => { const membersFormGroupInvalidFeedback = () => findMembersFormGroup().props('invalidFeedback'); const membersFormGroupDescription = () => findMembersFormGroup().props('description'); const findMembersSelect = () => wrapper.findComponent(MembersTokenSelect); - const findAreaofFocusCheckBoxGroup = () => wrapper.findComponent(GlFormCheckboxGroup); const findTasksToBeDone = () => wrapper.findByTestId('invite-members-modal-tasks-to-be-done'); const findTasks = () => wrapper.findByTestId('invite-members-modal-tasks'); const findProjectSelect = () => wrapper.findByTestId('invite-members-modal-project-select'); @@ -215,21 +205,6 @@ describe('InviteMembersModal', () => { }); }); - describe('rendering the areas_of_focus', () => { - it('renders the areas_of_focus checkboxes', () => { - createComponent(); - - expect(findAreaofFocusCheckBoxGroup().props('options')).toBe(areasOfFocusOptions); - expect(findAreaofFocusCheckBoxGroup().exists()).toBe(true); - }); - - it('does not render the areas_of_focus checkboxes', () => { - createComponent({}, { areasOfFocusOptions: [] }); - - expect(findAreaofFocusCheckBoxGroup().exists()).toBe(false); - }); - }); - describe('rendering the tasks to be done', () => { const setupComponent = ( extraData = {}, @@ -442,20 +417,6 @@ describe('InviteMembersModal', () => { "The member's email address is not allowed for this project. Go to the Admin area > Sign-up restrictions, and check Allowed domains for sign-ups."; const expectedSyntaxError = 'email contains an invalid email address'; - it('calls the API with the expected focus data when an areas_of_focus checkbox is clicked', () => { - const spy = jest.spyOn(Api, 'addGroupMembersByUserId'); - const expectedFocus = [areasOfFocusOptions[0].value]; - createComponent({ newUsersToInvite: [user1] }); - - findAreaofFocusCheckBoxGroup().vm.$emit('input', expectedFocus); - clickInviteButton(); - - expect(spy).toHaveBeenCalledWith( - user1.id.toString(), - expect.objectContaining({ areas_of_focus: expectedFocus }), - ); - }); - describe('when inviting an existing user to group by user ID', () => { const postData = { user_id: '1,2', @@ -463,7 +424,6 @@ describe('InviteMembersModal', () => { expires_at: undefined, invite_source: inviteSource, format: 'json', - areas_of_focus: noSelectionAreasOfFocus, tasks_to_be_done: [], tasks_project_id: '', }; @@ -476,16 +436,6 @@ describe('InviteMembersModal', () => { jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData }); }); - it('includes the non-default selected areas of focus', () => { - const focus = ['abc']; - const updatedPostData = { ...postData, areas_of_focus: focus }; - wrapper.setData({ selectedAreasOfFocus: focus }); - - clickInviteButton(); - - expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, updatedPostData); - }); - describe('when triggered from regular mounting', () => { beforeEach(() => { clickInviteButton(); @@ -661,7 +611,6 @@ describe('InviteMembersModal', () => { expires_at: undefined, email: 'email@example.com', invite_source: inviteSource, - areas_of_focus: noSelectionAreasOfFocus, tasks_to_be_done: [], tasks_project_id: '', format: 'json', @@ -675,16 +624,6 @@ describe('InviteMembersModal', () => { jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData }); }); - it('includes the non-default selected areas of focus', () => { - const focus = ['abc']; - const updatedPostData = { ...postData, areas_of_focus: focus }; - wrapper.setData({ selectedAreasOfFocus: focus }); - - clickInviteButton(); - - expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, updatedPostData); - }); - describe('when triggered from regular mounting', () => { beforeEach(() => { clickInviteButton(); @@ -792,7 +731,6 @@ describe('InviteMembersModal', () => { access_level: defaultAccessLevel, expires_at: undefined, invite_source: inviteSource, - areas_of_focus: noSelectionAreasOfFocus, format: 'json', tasks_to_be_done: [], tasks_project_id: '', @@ -951,30 +889,12 @@ describe('InviteMembersModal', () => { expect(ExperimentTracking).not.toHaveBeenCalledWith(INVITE_MEMBERS_IN_COMMENT); }); - it('tracks the view for areas_of_focus', () => { - eventHub.$emit('openModal', { inviteeType: 'members' }); - - expect(ExperimentTracking).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.name); - expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.view); - }); - it('tracks the view for learn_gitlab source', () => { eventHub.$emit('openModal', { inviteeType: 'members', source: LEARN_GITLAB }); expect(ExperimentTracking).toHaveBeenCalledWith(INVITE_MEMBERS_FOR_TASK.name); expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(LEARN_GITLAB); }); - - it('tracks the invite for areas_of_focus', () => { - eventHub.$emit('openModal', { inviteeType: 'members' }); - - clickInviteButton(); - - expect(ExperimentTracking).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.name); - expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith( - MEMBER_AREAS_OF_FOCUS.submit, - ); - }); }); }); }); diff --git a/spec/frontend/security_configuration/components/app_spec.js b/spec/frontend/security_configuration/components/app_spec.js index 4e0ea6a9717..759844b941a 100644 --- a/spec/frontend/security_configuration/components/app_spec.js +++ b/spec/frontend/security_configuration/components/app_spec.js @@ -5,7 +5,10 @@ import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser'; import stubChildren from 'helpers/stub_children'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import SecurityConfigurationApp, { i18n } from '~/security_configuration/components/app.vue'; +import SecurityConfigurationApp, { + i18n, + TRAINING_PROVIDERS, +} from '~/security_configuration/components/app.vue'; import AutoDevopsAlert from '~/security_configuration/components/auto_dev_ops_alert.vue'; import AutoDevopsEnabledAlert from '~/security_configuration/components/auto_dev_ops_enabled_alert.vue'; import { @@ -20,6 +23,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 { @@ -78,6 +82,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}"]`; @@ -180,6 +185,10 @@ describe('App component', () => { expect(findComplianceViewHistoryLink().exists()).toBe(false); expect(findSecurityViewHistoryLink().exists()).toBe(false); }); + + it('renders training provider list with correct props', () => { + expect(findTrainingProviderList().props('providers')).toEqual(TRAINING_PROVIDERS); + }); }); describe('Manage via MR Error Alert', () => { 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..1169a977d44 --- /dev/null +++ b/spec/frontend/security_configuration/components/training_provider_list_spec.js @@ -0,0 +1,60 @@ +import { GlLink, GlToggle, GlCard } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; +import { TRAINING_PROVIDERS } from '~/security_configuration/components/app.vue'; + +const DEFAULT_PROPS = { + providers: TRAINING_PROVIDERS, +}; + +describe('TrainingProviderList component', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(TrainingProviderList, { + propsData: { + ...DEFAULT_PROPS, + ...props, + }, + }); + }; + + const findCards = () => wrapper.findAllComponents(GlCard); + const findLinks = () => wrapper.findAllComponents(GlLink); + const findToggles = () => wrapper.findAllComponents(GlToggle); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('basic structure', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders correct amount of cards', () => { + expect(findCards()).toHaveLength(DEFAULT_PROPS.providers.length); + }); + + DEFAULT_PROPS.providers.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); + }); + }); + }); +}); diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb index e3db2240318..d8a97b93bc9 100644 --- a/spec/helpers/invite_members_helper_spec.rb +++ b/spec/helpers/invite_members_helper_spec.rb @@ -16,52 +16,14 @@ RSpec.describe InviteMembersHelper do end describe '#common_invite_modal_dataset' do - context 'when member_areas_of_focus is enabled', :experiment do - context 'with control experience' do - before do - stub_experiments(member_areas_of_focus: :control) - end - - it 'has expected attributes' do - attributes = { - areas_of_focus_options: [], - no_selection_areas_of_focus: [] - } - - expect(helper.common_invite_modal_dataset(project)).to include(attributes) - end - end - - context 'with candidate experience' do - before do - stub_experiments(member_areas_of_focus: :candidate) - end - - it 'has expected attributes', :aggregate_failures do - output = helper.common_invite_modal_dataset(project) - - expect(output[:no_selection_areas_of_focus]).to eq ['no_selection'] - expect(Gitlab::Json.parse(output[:areas_of_focus_options]).first['value']).to eq 'Contribute to the codebase' - end - end - end - - context 'when member_areas_of_focus is disabled' do - before do - stub_feature_flags(member_areas_of_focus: false) - end - - it 'has expected attributes' do - attributes = { - id: project.id, - name: project.name, - default_access_level: Gitlab::Access::GUEST, - areas_of_focus_options: [], - no_selection_areas_of_focus: [] - } - - expect(helper.common_invite_modal_dataset(project)).to include(attributes) - end + it 'has expected common attributes' do + attributes = { + id: project.id, + name: project.name, + default_access_level: Gitlab::Access::GUEST + } + + expect(helper.common_invite_modal_dataset(project)).to include(attributes) end context 'tasks_to_be_done' do diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb index 7b17a5ed1cb..702e6ef0a2a 100644 --- a/spec/requests/api/invitations_spec.rb +++ b/spec/requests/api/invitations_spec.rb @@ -152,20 +152,6 @@ RSpec.describe API::Invitations do end end - context 'with areas_of_focus', :snowplow do - it 'tracks the areas_of_focus from params' do - post invitations_url(source, maintainer), - params: { email: email, access_level: Member::DEVELOPER, areas_of_focus: 'Other' } - - expect_snowplow_event( - category: 'Members::InviteService', - action: 'area_of_focus', - label: 'Other', - property: source.members.last.id.to_s - ) - end - end - context 'with tasks_to_be_done and tasks_project_id in the params' do let(:project_id) { source_type == 'project' ? source.id : create(:project, namespace: source).id } diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index bf5b89e72c6..02061bb8ab6 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -387,33 +387,6 @@ RSpec.describe API::Members do end end - context 'with areas_of_focus considerations', :snowplow do - let(:user_id) { stranger.id } - - context 'when areas_of_focus is present in params' do - it 'tracks the areas_of_focus' do - post api("/#{source_type.pluralize}/#{source.id}/members", maintainer), - params: { user_id: user_id, access_level: Member::DEVELOPER, areas_of_focus: 'Other' } - - expect_snowplow_event( - category: 'Members::CreateService', - action: 'area_of_focus', - label: 'Other', - property: source.members.last.id.to_s - ) - end - end - - context 'when areas_of_focus is not present in params' do - it 'does not track the areas_of_focus' do - post api("/#{source_type.pluralize}/#{source.id}/members", maintainer), - params: { user_id: user_id, access_level: Member::DEVELOPER } - - expect_no_snowplow_event(category: 'Members::CreateService', action: 'area_of_focus') - end - end - end - context 'with tasks_to_be_done and tasks_project_id in the params' do let(:project_id) { source_type == 'project' ? source.id : create(:project, namespace: source).id } diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index babd601e0cf..34f77260334 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -79,12 +79,22 @@ RSpec.describe Ci::PlayBuildService, '#execute' do { key: 'second', secret_value: 'second' }] end + subject { service.execute(build, job_variables) } + it 'assigns the variables to the build' do - service.execute(build, job_variables) + subject expect(build.reload.job_variables.map(&:key)).to contain_exactly('first', 'second') end + context 'when variables are invalid' do + let(:job_variables) { [{}] } + + it 'raises an error' do + expect { subject }.to raise_error(ActiveRecord::RecordInvalid) + end + end + context 'when user defined variables are restricted' do before do project.update!(restrict_user_defined_variables: true) @@ -96,7 +106,7 @@ RSpec.describe Ci::PlayBuildService, '#execute' do end it 'assigns the variables to the build' do - service.execute(build, job_variables) + subject expect(build.reload.job_variables.map(&:key)).to contain_exactly('first', 'second') end @@ -104,8 +114,7 @@ RSpec.describe Ci::PlayBuildService, '#execute' do context 'when user is developer' do it 'raises an error' do - expect { service.execute(build, job_variables) } - .to raise_error Gitlab::Access::AccessDeniedError + expect { subject }.to raise_error Gitlab::Access::AccessDeniedError end end end diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb index 43493deb621..13f56fe7458 100644 --- a/spec/services/members/create_service_spec.rb +++ b/spec/services/members/create_service_spec.rb @@ -127,76 +127,6 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_ end end - context 'when tracking the areas of focus', :snowplow do - context 'when areas_of_focus is not passed' do - it 'does not track' do - execute_service - - expect_no_snowplow_event(category: described_class.name, action: 'area_of_focus') - end - end - - context 'when 1 areas_of_focus is passed' do - let(:additional_params) { { invite_source: '_invite_source_', areas_of_focus: ['no_selection'] } } - - it 'tracks the areas_of_focus from params' do - execute_service - - expect_snowplow_event( - category: described_class.name, - action: 'area_of_focus', - label: 'no_selection', - property: source.members.last.id.to_s - ) - end - - context 'when passing many user ids' do - let(:another_user) { create(:user) } - let(:user_ids) { [member.id, another_user.id].join(',') } - - it 'tracks the areas_of_focus from params' do - execute_service - - members = source.members.last(2) - - expect_snowplow_event( - category: described_class.name, - action: 'area_of_focus', - label: 'no_selection', - property: members.first.id.to_s - ) - expect_snowplow_event( - category: described_class.name, - action: 'area_of_focus', - label: 'no_selection', - property: members.last.id.to_s - ) - end - end - end - - context 'when multiple areas_of_focus are passed' do - let(:additional_params) { { invite_source: '_invite_source_', areas_of_focus: %w[no_selection Other] } } - - it 'tracks the areas_of_focus from params' do - execute_service - - expect_snowplow_event( - category: described_class.name, - action: 'area_of_focus', - label: 'no_selection', - property: source.members.last.id.to_s - ) - expect_snowplow_event( - category: described_class.name, - action: 'area_of_focus', - label: 'Other', - property: source.members.last.id.to_s - ) - end - end - end - context 'when assigning tasks to be done' do let(:additional_params) do { invite_source: '_invite_source_', tasks_to_be_done: %w(ci code), tasks_project_id: source.id } diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb index 3502558b2c2..11040562b49 100644 --- a/spec/support/helpers/features/invite_members_modal_helper.rb +++ b/spec/support/helpers/features/invite_members_modal_helper.rb @@ -5,7 +5,7 @@ module Spec module Helpers module Features module InviteMembersModalHelper - def invite_member(name, role: 'Guest', expires_at: nil, area_of_focus: false) + def invite_member(name, role: 'Guest', expires_at: nil) click_on 'Invite members' page.within '[data-testid="invite-members-modal"]' do @@ -14,7 +14,6 @@ module Spec wait_for_requests click_button name choose_options(role, expires_at) - choose_area_of_focus if area_of_focus click_button 'Invite' @@ -44,13 +43,6 @@ module Spec fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at end - - def choose_area_of_focus - page.within '[data-testid="area-of-focus-checks"]' do - check 'Contribute to the codebase' - check 'Collaborate on open issues and merge requests' - end - end end end end diff --git a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb index 00a0fb7e4c5..3a7588a5cc9 100644 --- a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb +++ b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb @@ -50,7 +50,8 @@ RSpec.shared_examples Repositories::GitHttpController do context 'with authorized user' do before do - request.headers.merge! auth_env(user.username, user.password, nil) + password = user.try(:password) || user.try(:token) + request.headers.merge! auth_env(user.username, password, nil) end it 'returns 200' do @@ -71,9 +72,10 @@ RSpec.shared_examples Repositories::GitHttpController do it 'adds user info to the logs' do get :info_refs, params: params - expect(log_data).to include('username' => user.username, - 'user_id' => user.id, - 'meta.user' => user.username) + user_log_data = { 'username' => user.username, 'user_id' => user.id } + user_log_data['meta.user'] = user.username if user.is_a?(User) + + expect(log_data).to include(user_log_data) end end end |