diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-03 18:09:58 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-03 18:09:58 +0000 |
commit | 524639c7063131c40b848789ff541758b68c1cca (patch) | |
tree | eef8ce631ab44dfaefe71c54d2a4d248d8fd3c69 /spec | |
parent | 4f41b713eb264096903c168375815adec96ab8ac (diff) | |
download | gitlab-ce-524639c7063131c40b848789ff541758b68c1cca.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
20 files changed, 489 insertions, 169 deletions
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 7cccac11363..c289c18126d 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -8,9 +8,11 @@ RSpec.describe 'Admin updates settings' do include UsageDataHelpers let(:admin) { create(:admin) } + let(:dot_com?) { false } context 'application setting :admin_mode is enabled', :request_store do before do + allow(Gitlab).to receive(:com?).and_return(dot_com?) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) @@ -127,6 +129,37 @@ RSpec.describe 'Admin updates settings' do expect(user_internal_regex['placeholder']).to eq 'Regex pattern' end + context 'Dormant users' do + context 'when Gitlab.com' do + let(:dot_com?) { true } + + it 'does not expose the setting' do + expect(page).to have_no_selector('#application_setting_deactivate_dormant_users') + end + end + + context 'when not Gitlab.com' do + let(:dot_com?) { false } + + it 'change Dormant users' do + expect(page).to have_unchecked_field('Deactivate dormant users after 90 days of inactivity') + expect(current_settings.deactivate_dormant_users).to be_falsey + + page.within('.as-account-limit') do + check 'application_setting_deactivate_dormant_users' + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + + page.refresh + + expect(current_settings.deactivate_dormant_users).to be_truthy + expect(page).to have_checked_field('Deactivate dormant users after 90 days of inactivity') + end + end + end + context 'Change Sign-up restrictions' do context 'Require Admin approval for new signup setting' do it 'changes the setting', :js do diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb index ffdfbb9fe81..613033373e8 100644 --- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb +++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb @@ -12,6 +12,10 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do sign_in(user) end + def find_new_menu_toggle + find('#js-onboarding-new-project-link') + end + context 'with developer user' do before do project.add_developer(user) @@ -22,7 +26,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do # The navigation bar page.within('.header-new') do - find('.qa-new-menu-toggle').click + find_new_menu_toggle.click aggregate_failures 'dropdown links in the navigation bar' do expect(page).to have_link('New issue') @@ -30,7 +34,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do expect(page).to have_link('New snippet', href: new_project_snippet_path(project)) end - find('.qa-new-menu-toggle').click + find_new_menu_toggle.click end # The dropdown above the tree @@ -56,7 +60,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do visit project_path(project) page.within('.header-new') do - find('.qa-new-menu-toggle').click + find_new_menu_toggle.click aggregate_failures 'dropdown links' do expect(page).not_to have_link('New issue') @@ -64,7 +68,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do expect(page).not_to have_link('New snippet', href: new_project_snippet_path(project)) end - find('.qa-new-menu-toggle').click + find_new_menu_toggle.click end expect(page).not_to have_selector('.qa-add-to-tree') diff --git a/spec/frontend/fixtures/startup_css.rb b/spec/frontend/fixtures/startup_css.rb index 134d29d3106..003f7b768dd 100644 --- a/spec/frontend/fixtures/startup_css.rb +++ b/spec/frontend/fixtures/startup_css.rb @@ -11,12 +11,13 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do before(:all) do stub_feature_flags(combined_menu: true) + stub_feature_flags(sidebar_refactor: true) clean_frontend_fixtures('startup_css/') end shared_examples 'startup css project fixtures' do |type| let(:user) { create(:user, :admin) } - let(:project) { create(:project, :public, :repository, description: 'Code and stuff', avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png'), creator: user) } + let(:project) { create(:project, :public, :repository, description: 'Code and stuff', creator: user) } before do sign_in(user) @@ -42,6 +43,17 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do expect(response).to be_successful end + it "startup_css/project-#{type}-legacy-sidebar.html" do + stub_feature_flags(sidebar_refactor: false) + + get :show, params: { + namespace_id: project.namespace.to_param, + id: project + } + + expect(response).to be_successful + end + it "startup_css/project-#{type}-signed-out.html" do sign_out(user) diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js index 7a1026e8bfc..a94cb3e2fcc 100644 --- a/spec/frontend/frequent_items/components/app_spec.js +++ b/spec/frontend/frequent_items/components/app_spec.js @@ -21,13 +21,14 @@ const TEST_NAMESPACE = 'projects'; const TEST_VUEX_MODULE = 'frequentProjects'; const TEST_PROJECT = currentSession[TEST_NAMESPACE].project; const TEST_STORAGE_KEY = currentSession[TEST_NAMESPACE].storageKey; +const TEST_SEARCH_CLASS = 'test-search-class'; describe('Frequent Items App Component', () => { let wrapper; let mock; let store; - const createComponent = ({ currentItem = null } = {}) => { + const createComponent = (props = {}) => { const session = currentSession[TEST_NAMESPACE]; gon.api_version = session.apiVersion; @@ -36,7 +37,8 @@ describe('Frequent Items App Component', () => { propsData: { namespace: TEST_NAMESPACE, currentUserName: session.username, - currentItem: currentItem || session.project, + currentItem: session.project, + ...props, }, provide: { vuexModule: TEST_VUEX_MODULE, @@ -88,7 +90,7 @@ describe('Frequent Items App Component', () => { }); it('should render search input', () => { - expect(findSearchInput().exists()).toBe(true); + expect(findSearchInput().classes()).toEqual(['search-input-container']); }); it('should render loading animation', async () => { @@ -159,6 +161,16 @@ describe('Frequent Items App Component', () => { }); }); + describe('with searchClass', () => { + beforeEach(() => { + createComponent({ searchClass: TEST_SEARCH_CLASS }); + }); + + it('should render search input with searchClass', () => { + expect(findSearchInput().classes()).toEqual(['search-input-container', TEST_SEARCH_CLASS]); + }); + }); + describe('logging', () => { it('when created, it should create a project storage entry and adds a project', () => { createComponent(); diff --git a/spec/frontend/integrations/edit/components/active_checkbox_spec.js b/spec/frontend/integrations/edit/components/active_checkbox_spec.js index 0e56fb6454e..df7ffd19747 100644 --- a/spec/frontend/integrations/edit/components/active_checkbox_spec.js +++ b/spec/frontend/integrations/edit/components/active_checkbox_spec.js @@ -1,5 +1,6 @@ import { GlFormCheckbox } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; + import ActiveCheckbox from '~/integrations/edit/components/active_checkbox.vue'; import { createStore } from '~/integrations/edit/store'; diff --git a/spec/frontend/integrations/edit/components/confirmation_modal_spec.js b/spec/frontend/integrations/edit/components/confirmation_modal_spec.js index 1c126f60c37..805d3971994 100644 --- a/spec/frontend/integrations/edit/components/confirmation_modal_spec.js +++ b/spec/frontend/integrations/edit/components/confirmation_modal_spec.js @@ -1,5 +1,6 @@ import { GlModal } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; + import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue'; import { createStore } from '~/integrations/edit/store'; @@ -13,13 +14,10 @@ describe('ConfirmationModal', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); - const findGlModal = () => wrapper.find(GlModal); + const findGlModal = () => wrapper.findComponent(GlModal); describe('template', () => { beforeEach(() => { diff --git a/spec/frontend/integrations/edit/components/dynamic_field_spec.js b/spec/frontend/integrations/edit/components/dynamic_field_spec.js index 2ebb3333c0f..8784b3c2b00 100644 --- a/spec/frontend/integrations/edit/components/dynamic_field_spec.js +++ b/spec/frontend/integrations/edit/components/dynamic_field_spec.js @@ -1,5 +1,6 @@ import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; + import DynamicField from '~/integrations/edit/components/dynamic_field.vue'; describe('DynamicField', () => { @@ -24,17 +25,14 @@ describe('DynamicField', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); - const findGlFormGroup = () => wrapper.find(GlFormGroup); - const findGlFormCheckbox = () => wrapper.find(GlFormCheckbox); - const findGlFormInput = () => wrapper.find(GlFormInput); - const findGlFormSelect = () => wrapper.find(GlFormSelect); - const findGlFormTextarea = () => wrapper.find(GlFormTextarea); + const findGlFormGroup = () => wrapper.findComponent(GlFormGroup); + const findGlFormCheckbox = () => wrapper.findComponent(GlFormCheckbox); + const findGlFormInput = () => wrapper.findComponent(GlFormInput); + const findGlFormSelect = () => wrapper.findComponent(GlFormSelect); + const findGlFormTextarea = () => wrapper.findComponent(GlFormTextarea); describe('template', () => { describe.each([ diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index c015fd0b9e0..cbce26762b1 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,6 +1,6 @@ -import { shallowMount } from '@vue/test-utils'; import { setHTMLFixture } from 'helpers/fixtures'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + import { mockIntegrationProps } from 'jest/integrations/edit/mock_data'; import ActiveCheckbox from '~/integrations/edit/components/active_checkbox.vue'; import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue'; @@ -23,42 +23,37 @@ describe('IntegrationForm', () => { initialState = {}, props = {}, } = {}) => { - wrapper = extendedWrapper( - shallowMount(IntegrationForm, { - propsData: { ...props }, - store: createStore({ - customState: { ...mockIntegrationProps, ...customStateProps }, - ...initialState, - }), - stubs: { - OverrideDropdown, - ActiveCheckbox, - ConfirmationModal, - JiraTriggerFields, - TriggerFields, - }, - provide: { - glFeatures: featureFlags, - }, + wrapper = shallowMountExtended(IntegrationForm, { + propsData: { ...props }, + store: createStore({ + customState: { ...mockIntegrationProps, ...customStateProps }, + ...initialState, }), - ); + stubs: { + OverrideDropdown, + ActiveCheckbox, + ConfirmationModal, + JiraTriggerFields, + TriggerFields, + }, + provide: { + glFeatures: featureFlags, + }, + }); }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); - const findOverrideDropdown = () => wrapper.find(OverrideDropdown); - const findActiveCheckbox = () => wrapper.find(ActiveCheckbox); - const findConfirmationModal = () => wrapper.find(ConfirmationModal); - const findResetConfirmationModal = () => wrapper.find(ResetConfirmationModal); - const findResetButton = () => wrapper.find('[data-testid="reset-button"]'); - const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields); - const findJiraIssuesFields = () => wrapper.find(JiraIssuesFields); - const findTriggerFields = () => wrapper.find(TriggerFields); + const findOverrideDropdown = () => wrapper.findComponent(OverrideDropdown); + const findActiveCheckbox = () => wrapper.findComponent(ActiveCheckbox); + const findConfirmationModal = () => wrapper.findComponent(ConfirmationModal); + const findResetConfirmationModal = () => wrapper.findComponent(ResetConfirmationModal); + const findResetButton = () => wrapper.findByTestId('reset-button'); + const findJiraTriggerFields = () => wrapper.findComponent(JiraTriggerFields); + const findJiraIssuesFields = () => wrapper.findComponent(JiraIssuesFields); + const findTriggerFields = () => wrapper.findComponent(TriggerFields); describe('template', () => { describe('showActive is true', () => { @@ -286,7 +281,7 @@ describe('IntegrationForm', () => { </div> `); - it('renders `helpHtml`', async () => { + it('renders `helpHtml`', () => { const mockHelpHtml = document.querySelector(`[data-testid="${mockTestId}"]`); createComponent({ diff --git a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js index 9cc041490d0..eb5f7e9fe40 100644 --- a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js @@ -1,5 +1,6 @@ import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; + import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; import JiraUpgradeCta from '~/integrations/edit/components/jira_upgrade_cta.vue'; import eventHub from '~/integrations/edit/event_hub'; @@ -20,7 +21,7 @@ describe('JiraIssuesFields', () => { defaultState: isInheriting ? {} : undefined, }); - wrapper = mount(JiraIssuesFields, { + wrapper = mountExtended(JiraIssuesFields, { propsData: { ...defaultProps, ...props }, store, stubs: ['jira-issue-creation-vulnerabilities'], @@ -37,7 +38,7 @@ describe('JiraIssuesFields', () => { findEnableCheckbox().find('[type=checkbox]').attributes('disabled'); const findProjectKey = () => wrapper.findComponent(GlFormInput); const findJiraUpgradeCta = () => wrapper.findComponent(JiraUpgradeCta); - const findJiraForVulnerabilities = () => wrapper.find('[data-testid="jira-for-vulnerabilities"]'); + const findJiraForVulnerabilities = () => wrapper.findByTestId('jira-for-vulnerabilities'); const setEnableCheckbox = async (isEnabled = true) => findEnableCheckbox().vm.$emit('input', isEnabled); diff --git a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js index 5c04add61a1..9e01371f542 100644 --- a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js @@ -1,5 +1,6 @@ import { GlFormCheckbox } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; + import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; describe('JiraTriggerFields', () => { @@ -12,7 +13,7 @@ describe('JiraTriggerFields', () => { }; const createComponent = (props, isInheriting = false) => { - wrapper = mount(JiraTriggerFields, { + wrapper = mountExtended(JiraTriggerFields, { propsData: { ...defaultProps, ...props }, computed: { isInheriting: () => isInheriting, @@ -21,18 +22,15 @@ describe('JiraTriggerFields', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); - const findCommentSettings = () => wrapper.find('[data-testid="comment-settings"]'); - const findCommentDetail = () => wrapper.find('[data-testid="comment-detail"]'); - const findCommentSettingsCheckbox = () => findCommentSettings().find(GlFormCheckbox); + const findCommentSettings = () => wrapper.findByTestId('comment-settings'); + const findCommentDetail = () => wrapper.findByTestId('comment-detail'); + const findCommentSettingsCheckbox = () => findCommentSettings().findComponent(GlFormCheckbox); const findIssueTransitionEnabled = () => wrapper.find('[data-testid="issue-transition-enabled"] input[type="checkbox"]'); - const findIssueTransitionMode = () => wrapper.find('[data-testid="issue-transition-mode"]'); + const findIssueTransitionMode = () => wrapper.findByTestId('issue-transition-mode'); const findIssueTransitionModeRadios = () => findIssueTransitionMode().findAll('input[type="radio"]'); const findIssueTransitionIdsField = () => diff --git a/spec/frontend/integrations/edit/components/jira_upgrade_cta_spec.js b/spec/frontend/integrations/edit/components/jira_upgrade_cta_spec.js index e49a1619627..e90e9a5d2ac 100644 --- a/spec/frontend/integrations/edit/components/jira_upgrade_cta_spec.js +++ b/spec/frontend/integrations/edit/components/jira_upgrade_cta_spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; + import JiraUpgradeCta from '~/integrations/edit/components/jira_upgrade_cta.vue'; describe('JiraUpgradeCta', () => { @@ -18,13 +19,13 @@ describe('JiraUpgradeCta', () => { it('displays the correct message for premium and lower users', () => { createComponent({ showPremiumMessage: true }); - expect(wrapper.html()).toContain('This is a Premium feature'); - expect(wrapper.html()).toContain(contentMessage); + expect(wrapper.text()).toContain('This is a Premium feature'); + expect(wrapper.text()).toContain(contentMessage); }); it('displays the correct message for ultimate and lower users', () => { createComponent({ showUltimateMessage: true }); - expect(wrapper.html()).toContain('This is an Ultimate feature'); - expect(wrapper.html()).toContain(contentMessage); + expect(wrapper.text()).toContain('This is an Ultimate feature'); + expect(wrapper.text()).toContain(contentMessage); }); }); diff --git a/spec/frontend/integrations/edit/components/override_dropdown_spec.js b/spec/frontend/integrations/edit/components/override_dropdown_spec.js index 592f4514e45..eb43d940f5e 100644 --- a/spec/frontend/integrations/edit/components/override_dropdown_spec.js +++ b/spec/frontend/integrations/edit/components/override_dropdown_spec.js @@ -1,5 +1,6 @@ import { GlDropdown, GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; + import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue'; import { integrationLevels, overrideDropdownDescriptions } from '~/integrations/edit/constants'; import { createStore } from '~/integrations/edit/store'; @@ -26,14 +27,11 @@ describe('OverrideDropdown', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); - const findGlLink = () => wrapper.find(GlLink); - const findGlDropdown = () => wrapper.find(GlDropdown); + const findGlLink = () => wrapper.findComponent(GlLink); + const findGlDropdown = () => wrapper.findComponent(GlDropdown); describe('template', () => { describe('override prop is true', () => { diff --git a/spec/frontend/integrations/edit/components/trigger_fields_spec.js b/spec/frontend/integrations/edit/components/trigger_fields_spec.js index b9d16464e72..5f85c58da28 100644 --- a/spec/frontend/integrations/edit/components/trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/trigger_fields_spec.js @@ -1,5 +1,6 @@ import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; + import TriggerFields from '~/integrations/edit/components/trigger_fields.vue'; describe('TriggerFields', () => { @@ -10,7 +11,7 @@ describe('TriggerFields', () => { }; const createComponent = (props, isInheriting = false) => { - wrapper = mount(TriggerFields, { + wrapper = mountExtended(TriggerFields, { propsData: { ...defaultProps, ...props }, computed: { isInheriting: () => isInheriting, @@ -19,21 +20,19 @@ describe('TriggerFields', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); }); + const findTriggerLabel = () => wrapper.findByTestId('trigger-fields-group').find('label'); const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAll(GlFormGroup); - const findAllGlFormCheckboxes = () => wrapper.findAll(GlFormCheckbox); - const findAllGlFormInputs = () => wrapper.findAll(GlFormInput); + const findAllGlFormCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox); + const findAllGlFormInputs = () => wrapper.findAllComponents(GlFormInput); describe.each([true, false])('template, isInheriting = `%p`', (isInheriting) => { it('renders a label with text "Trigger"', () => { createComponent(); - const triggerLabel = wrapper.find('[data-testid="trigger-fields-group"]').find('label'); + const triggerLabel = findTriggerLabel(); expect(triggerLabel.exists()).toBe(true); expect(triggerLabel.text()).toBe('Trigger'); }); @@ -68,7 +67,7 @@ describe('TriggerFields', () => { }); it('renders GlFormInput with description for each event', () => { - const groups = wrapper.find('#trigger-fields').findAll(GlFormGroup); + const groups = findAllGlFormGroups(); expect(groups).toHaveLength(2); groups.wrappers.forEach((group, index) => { diff --git a/spec/frontend/integrations/index/components/integrations_list_spec.js b/spec/frontend/integrations/index/components/integrations_list_spec.js index 94fd7fc84ee..ee54a5fd359 100644 --- a/spec/frontend/integrations/index/components/integrations_list_spec.js +++ b/spec/frontend/integrations/index/components/integrations_list_spec.js @@ -1,5 +1,5 @@ -import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + import IntegrationsList from '~/integrations/index/components/integrations_list.vue'; import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data'; @@ -10,7 +10,7 @@ describe('IntegrationsList', () => { const findInactiveIntegrationsTable = () => wrapper.findByTestId('inactive-integrations-table'); const createComponent = (propsData = {}) => { - wrapper = extendedWrapper(shallowMount(IntegrationsList, { propsData })); + wrapper = shallowMountExtended(IntegrationsList, { propsData }); }; afterEach(() => { diff --git a/spec/frontend/nav/components/top_nav_container_view_spec.js b/spec/frontend/nav/components/top_nav_container_view_spec.js index 12bee7a515e..311331ce8f5 100644 --- a/spec/frontend/nav/components/top_nav_container_view_spec.js +++ b/spec/frontend/nav/components/top_nav_container_view_spec.js @@ -16,8 +16,8 @@ const DEFAULT_PROPS = { }; const TEST_OTHER_PROPS = { namespace: 'projects', - currentUserName: '', - currentItem: {}, + currentUserName: 'test-user', + currentItem: { id: 'test' }, }; describe('~/nav/components/top_nav_container_view.vue', () => { @@ -84,7 +84,7 @@ describe('~/nav/components/top_nav_container_view.vue', () => { it('renders frequent items app', () => { expect(findFrequentItemsApp()).toEqual({ vuexModule: DEFAULT_PROPS.frequentItemsVuexModule, - props: TEST_OTHER_PROPS, + props: expect.objectContaining(TEST_OTHER_PROPS), attributes: expect.objectContaining(EXTRA_ATTRS), }); }); diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb index c74ee3ce0ec..4c62b3e12c1 100644 --- a/spec/helpers/application_settings_helper_spec.rb +++ b/spec/helpers/application_settings_helper_spec.rb @@ -37,8 +37,24 @@ RSpec.describe ApplicationSettingsHelper do it_behaves_like 'when HTTP protocol is in use', 'https' it_behaves_like 'when HTTP protocol is in use', 'http' - context 'with tracking parameters' do - it { expect(visible_attributes).to include(*%i(snowplow_collector_hostname snowplow_cookie_domain snowplow_enabled snowplow_app_id)) } + describe '.visible_attributes' do + it 'contains tracking parameters' do + expect(helper.visible_attributes).to include(*%i(snowplow_collector_hostname snowplow_cookie_domain snowplow_enabled snowplow_app_id)) + end + + it 'contains :deactivate_dormant_users' do + expect(helper.visible_attributes).to include(:deactivate_dormant_users) + end + + context 'when GitLab.com' do + before do + allow(Gitlab).to receive(:com?).and_return(true) + end + + it 'does not contain :deactivate_dormant_users' do + expect(helper.visible_attributes).not_to include(:deactivate_dormant_users) + end + end end describe '.integration_expanded?' do diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb index 122f2339b28..3d2adaa5b5d 100644 --- a/spec/helpers/invite_members_helper_spec.rb +++ b/spec/helpers/invite_members_helper_spec.rb @@ -114,69 +114,4 @@ RSpec.describe InviteMembersHelper do end end end - - describe '#dropdown_invite_members_link' do - shared_examples_for 'dropdown invite members link' do - let(:link_regex) do - /data-track-event="click_link".*data-track-property="_track_property_".*Invite members/ - end - - before do - allow(helper).to receive(:experiment_tracking_category_and_group) { '_track_property_' } - allow(helper).to receive(:current_user) { owner } - end - - it 'records the experiment' do - allow(helper).to receive(:experiment_enabled?) - - helper.dropdown_invite_members_link(form_model) - - expect(helper).to have_received(:experiment_tracking_category_and_group).with(:invite_members_new_dropdown) - end - - context 'with experiment enabled' do - before do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_new_dropdown) { true } - end - - it 'returns link' do - link = helper.dropdown_invite_members_link(form_model) - - expect(link).to match(link_regex) - expect(link).to include(link_href) - expect(link).to include('gl-emoji') - end - end - - context 'with no experiment enabled' do - before do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_new_dropdown) { false } - end - - it 'returns link' do - link = helper.dropdown_invite_members_link(form_model) - - expect(link).to match(link_regex) - expect(link).to include(link_href) - expect(link).not_to include('gl-emoji') - end - end - end - - context 'with a project' do - let_it_be(:form_model) { project } - - let(:link_href) { "href=\"#{project_project_members_path(form_model)}\"" } - - it_behaves_like 'dropdown invite members link' - end - - context 'with a group' do - let_it_be(:form_model) { create(:group) } - - let(:link_href) { "href=\"#{group_group_members_path(form_model)}\"" } - - it_behaves_like 'dropdown invite members link' - end - end end diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb new file mode 100644 index 00000000000..dd860ce3180 --- /dev/null +++ b/spec/helpers/nav/new_dropdown_helper_spec.rb @@ -0,0 +1,320 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Nav::NewDropdownHelper do + describe '#new_dropdown_view_model' do + let_it_be(:user) { build_stubbed(:user) } + + let(:current_user) { user } + let(:current_project) { nil } + let(:current_group) { nil } + + let(:with_can_create_project) { false } + let(:with_can_create_group) { false } + let(:with_can_create_snippet) { false } + let(:with_new_repo_experiment) { :control } + let(:with_invite_members_experiment) { false } + let(:with_invite_members_experiment_enabled) { false } + + let(:subject) { helper.new_dropdown_view_model(project: current_project, group: current_group) } + + def expected_menu_section(title:, menu_item:) + [ + { + title: title, + menu_items: [menu_item] + } + ] + end + + before do + stub_experiments(new_repo: with_new_repo_experiment) + allow(::Gitlab::Experimentation).to receive(:active?).with(:invite_members_new_dropdown) { with_invite_members_experiment } + allow(helper).to receive(:experiment_enabled?).with(:invite_members_new_dropdown) { with_invite_members_experiment_enabled } + allow(helper).to receive(:tracking_label) { 'test_tracking_label' } + allow(helper).to receive(:experiment_tracking_category_and_group) { |x| x } + + allow(helper).to receive(:current_user) { current_user } + allow(helper).to receive(:can?) { false } + + allow(user).to receive(:can_create_project?) { with_can_create_project } + allow(user).to receive(:can_create_group?) { with_can_create_group } + allow(user).to receive(:can?).with(:create_snippet) { with_can_create_snippet } + end + + shared_examples 'new repo experiment shared example' do |title| + let(:with_new_repo_experiment) { :candidate } + + it 'has experiment project title' do + expect(subject[:menu_sections]).to match( + expected_menu_section( + title: title, + menu_item: a_hash_including(title: 'New project/repository') + ) + ) + end + end + + shared_examples 'invite member link shared example' do + it 'shows invite member link' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: expected_title, + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'invite', + title: 'Invite members', + href: expected_href, + data: { + track_event: 'click_link', + track_label: 'test_tracking_label', + track_property: :invite_members_new_dropdown + } + ) + ) + ) + end + + context 'with experiment enabled' do + let(:with_invite_members_experiment_enabled) { true } + + it 'shows emoji with invite member link' do + expect(subject[:menu_sections]).to match( + expected_menu_section( + title: expected_title, + menu_item: a_hash_including( + emoji: 'shaking_hands' + ) + ) + ) + end + end + end + + it 'has title' do + expect(subject[:title]).to eq('New...') + end + + context 'when current_user is nil (anonymous)' do + let(:current_user) { nil } + + it 'is nil' do + expect(subject).to be_nil + end + end + + context 'when group and project are nil' do + it 'has no menu sections' do + expect(subject[:menu_sections]).to eq([]) + end + + context 'when can create project' do + let(:with_can_create_project) { true } + + it 'has project menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'GitLab', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'general_new_project', + title: 'New project', + href: '/projects/new', + data: { track_experiment: 'new_repo', track_event: 'click_link_new_project', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_project_link' } + ) + ) + ) + end + + it_behaves_like 'new repo experiment shared example', 'GitLab' + end + + context 'when can create group' do + let(:with_can_create_group) { true } + + it 'has group menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'GitLab', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'general_new_group', + title: 'New group', + href: '/groups/new', + data: { track_event: 'click_link_new_group', track_label: 'plus_menu_dropdown' } + ) + ) + ) + end + end + + context 'when can create snippet' do + let(:with_can_create_snippet) { true } + + it 'has new snippet menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'GitLab', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'general_new_snippet', + title: 'New snippet', + href: '/-/snippets/new', + data: { track_event: 'click_link_new_snippet_parent', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_snippet_link' } + ) + ) + ) + end + end + end + + context 'with persisted group' do + let_it_be(:group) { build_stubbed(:group) } + + let(:current_group) { group } + let(:with_can_create_projects_in_group) { false } + let(:with_can_create_subgroup_in_group) { false } + let(:with_can_admin_in_group) { false } + + before do + allow(group).to receive(:persisted?) { true } + allow(helper).to receive(:can?).with(current_user, :create_projects, group) { with_can_create_projects_in_group } + allow(helper).to receive(:can?).with(current_user, :create_subgroup, group) { with_can_create_subgroup_in_group } + allow(helper).to receive(:can?).with(current_user, :admin_group_member, group) { with_can_admin_in_group } + end + + it 'has no menu sections' do + expect(subject[:menu_sections]).to eq([]) + end + + context 'when can create projects in group' do + let(:with_can_create_projects_in_group) { true } + + it 'has new project menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'This group', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'new_project', + title: 'New project', + href: "/projects/new?namespace_id=#{group.id}", + data: { track_experiment: 'new_repo', track_event: 'click_link_new_project_group', track_label: 'plus_menu_dropdown' } + ) + ) + ) + end + + it_behaves_like 'new repo experiment shared example', 'This group' + end + + context 'when can create subgroup' do + let(:with_can_create_subgroup_in_group) { true } + + it 'has new subgroup menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'This group', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'new_subgroup', + title: 'New subgroup', + href: "/groups/new?parent_id=#{group.id}", + data: { track_event: 'click_link_new_subgroup', track_label: 'plus_menu_dropdown' } + ) + ) + ) + end + end + + context 'when can invite members' do + let(:with_can_admin_in_group) { true } + let(:with_invite_members_experiment) { true } + let(:expected_title) { 'This group' } + let(:expected_href) { "/groups/#{group.full_path}/-/group_members" } + + it_behaves_like 'invite member link shared example' + end + end + + context 'with persisted project' do + let_it_be(:project) { build_stubbed(:project) } + let_it_be(:merge_project) { build_stubbed(:project) } + + let(:current_project) { project } + let(:with_show_new_issue_link) { false } + let(:with_merge_project) { nil } + let(:with_can_create_snippet_in_project) { false } + let(:with_can_import_members) { false } + + before do + allow(helper).to receive(:show_new_issue_link?).with(project) { with_show_new_issue_link } + allow(helper).to receive(:merge_request_source_project_for_project).with(project) { with_merge_project } + allow(helper).to receive(:can?).with(user, :create_snippet, project) { with_can_create_snippet_in_project } + allow(helper).to receive(:can_import_members?) { with_can_import_members } + end + + it 'has no menu sections' do + expect(subject[:menu_sections]).to eq([]) + end + + context 'with show_new_issue_link?' do + let(:with_show_new_issue_link) { true } + + it 'shows new issue menu item' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'This project', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'new_issue', + title: 'New issue', + href: "/#{project.path_with_namespace}/-/issues/new", + data: { track_event: 'click_link_new_issue', track_label: 'plus_menu_dropdown', qa_selector: 'new_issue_link' } + ) + ) + ) + end + end + + context 'with merge project' do + let(:with_merge_project) { merge_project } + + it 'shows merge project' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'This project', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'new_mr', + title: 'New merge request', + href: "/#{merge_project.path_with_namespace}/-/merge_requests/new", + data: { track_event: 'click_link_new_mr', track_label: 'plus_menu_dropdown' } + ) + ) + ) + end + end + + context 'when can create snippet' do + let(:with_can_create_snippet_in_project) { true } + + it 'shows new snippet' do + expect(subject[:menu_sections]).to eq( + expected_menu_section( + title: 'This project', + menu_item: ::Gitlab::Nav::TopNavMenuItem.build( + id: 'new_snippet', + title: 'New snippet', + href: "/#{project.path_with_namespace}/-/snippets/new", + data: { track_event: 'click_link_new_snippet_project', track_label: 'plus_menu_dropdown' } + ) + ) + ) + end + end + + context 'when invite members experiment' do + let(:with_invite_members_experiment) { true } + let(:with_can_import_members) { true } + let(:expected_title) { 'This project' } + let(:expected_href) { "/#{project.path_with_namespace}/-/project_members" } + + it_behaves_like 'invite member link shared example' + end + end + end +end diff --git a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb index c96bed2284b..966b23bf51a 100644 --- a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb +++ b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb @@ -13,7 +13,8 @@ RSpec.describe ::Gitlab::Nav::TopNavMenuItem do href: 'href', view: 'view', css_class: 'css_class', - data: {} + data: {}, + emoji: 'smile' } expect(described_class.build(**item)).to eq(item) diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb index bf81ab577f7..319e7b55fc3 100644 --- a/spec/views/layouts/header/_new_dropdown.haml_spec.rb +++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb @@ -52,7 +52,6 @@ RSpec.describe 'layouts/header/_new_dropdown' do end it 'has a "New project" link' do - render('layouts/header/new_repo_experiment') render expect(rendered).to have_link('New project', href: new_project_path(namespace_id: group.id)) @@ -164,7 +163,6 @@ RSpec.describe 'layouts/header/_new_dropdown' do end it 'has a "New project" link' do - render('layouts/header/new_repo_experiment') render expect(rendered).to have_link('New project', href: new_project_path) @@ -182,13 +180,13 @@ RSpec.describe 'layouts/header/_new_dropdown' do expect(rendered).to have_link('New snippet', href: new_snippet_path) end - context 'when the user is not allowed to create snippets' do + context 'when the user is not allowed to do anything' do let(:user) { create(:user, :external) } - it 'has no "New snippet" link' do - render - - expect(rendered).not_to have_link('New snippet', href: new_snippet_path) + it 'is nil' do + # We have to us `view.render` because `render` causes issues + # https://github.com/rails/rails/issues/41320 + expect(view.render("layouts/header/new_dropdown")).to be_nil end end end |