diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-16 18:07:37 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-16 18:07:37 +0300 |
commit | f0718f2099663226531b8916e14d5582eb28e453 (patch) | |
tree | bd8f8e6ef6353b1ce066c43c0e19655ca3be62e6 /spec | |
parent | 6d4ce68742e8decbfca5a1b382ce260790e8c396 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
12 files changed, 300 insertions, 232 deletions
diff --git a/spec/commands/sidekiq_cluster/cli_spec.rb b/spec/commands/sidekiq_cluster/cli_spec.rb index 75361686f14..0c32fa2571a 100644 --- a/spec/commands/sidekiq_cluster/cli_spec.rb +++ b/spec/commands/sidekiq_cluster/cli_spec.rb @@ -245,9 +245,15 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo it 'expands multiple queue groups correctly' do expected_workers = if Gitlab.ee? - [%w[], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export project_template_export]] + [ + %w[cronjob:clusters_integrations_check_prometheus_health incident_management_close_incident status_page_publish], + %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export project_template_export] + ] else - [%w[], %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export]] + [ + %w[cronjob:clusters_integrations_check_prometheus_health incident_management_close_incident], + %w[project_export projects_import_export_parallel_project_export projects_import_export_relation_export] + ] end expect(Gitlab::SidekiqCluster) @@ -255,7 +261,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo .with(expected_workers, default_options) .and_return([]) - cli.run(%w(--queue-selector feature_category=continuous_integration&has_external_dependencies=true resource_boundary=memory&feature_category=importers)) + cli.run(%w(--queue-selector feature_category=incident_management&has_external_dependencies=true resource_boundary=memory&feature_category=importers)) end it 'allows the special * selector' do diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb index a497be4cbc3..b723bd5690a 100644 --- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb +++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb @@ -24,12 +24,6 @@ RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled expect(file_name.value).to have_content('.gitlab-ci.yml') end - it 'chooses the .gitlab-ci.yml Template Type' do - template_type = page.find(:css, '.template-type-selector .dropdown-toggle-text') - - expect(template_type.text).to have_content('.gitlab-ci.yml') - end - it 'displays suggest_gitlab_ci_yml popover' do page.find(:css, '.gitlab-ci-yml-selector').click @@ -60,12 +54,6 @@ RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled expect(file_name.value).not_to have_content('.gitlab-ci.yml') end - it 'does not choose the .gitlab-ci.yml Template Type' do - template_type = page.find(:css, '.template-type-selector .dropdown-toggle-text') - - expect(template_type.text).to have_content('Select a template type') - end - it 'does not display suggest_gitlab_ci_yml popover' do popover_selector = '.b-popover.suggest-gitlab-ci-yml' diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb deleted file mode 100644 index 990b118d172..00000000000 --- a/spec/features/projects/files/template_type_dropdown_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > Template type dropdown selector', :js, feature_category: :projects do - let(:project) { create(:project, :repository) } - let(:user) { project.first_owner } - - before do - sign_in user - end - - context 'editing a non-matching file' do - before do - create_and_edit_file('.random-file.js') - end - - it 'not displayed' do - check_type_selector_display(false) - end - - it 'selects every template type correctly' do - fill_in 'file_path', with: '.gitignore' - try_selecting_all_types - end - - it 'updates template type toggle value when template is chosen' do - fill_in 'file_path', with: '.gitignore' - select_template('gitignore', 'Actionscript') - check_type_selector_toggle_text('.gitignore') - end - end - - context 'editing a matching file' do - before do - visit project_edit_blob_path(project, File.join(project.default_branch, 'LICENSE')) - end - - it 'displayed' do - check_type_selector_display(true) - end - - it 'selects every template type correctly' do - try_selecting_all_types - end - - context 'user previews changes' do - before do - click_link 'Preview changes' - end - - it 'type selector is hidden and shown correctly' do - check_type_selector_display(false) - click_link 'Write' - check_type_selector_display(true) - end - end - end - - context 'creating a matching file' do - before do - visit project_new_blob_path(project, 'master', file_name: '.gitignore') - end - - it 'is displayed' do - check_type_selector_display(true) - end - - it 'toggle is set to the correct value' do - select_template('gitignore', 'Actionscript') - check_type_selector_toggle_text('.gitignore') - end - - it 'sets the toggle text when selecting the template type' do - select_template_type('.gitignore') - check_type_selector_toggle_text('.gitignore') - end - - it 'selects every template type correctly' do - try_selecting_all_types - end - end - - context 'creating a file' do - before do - visit project_new_blob_path(project, project.default_branch) - end - - it 'type selector is shown' do - check_type_selector_display(true) - end - - it 'toggle is set to the proper value' do - check_type_selector_toggle_text('Select a template type') - end - - it 'selects every template type correctly' do - try_selecting_all_types - end - end -end - -def check_type_selector_display(is_visible) - count = is_visible ? 1 : 0 - expect(page).to have_css('.js-template-type-selector', count: count) -end - -def try_selecting_all_types - try_selecting_template_type('LICENSE', 'Apply a template') - try_selecting_template_type('Dockerfile', 'Apply a template') - try_selecting_template_type('.gitlab-ci.yml', 'Apply a template') - try_selecting_template_type('.gitignore', 'Apply a template') -end - -def try_selecting_template_type(template_type, selector_label) - select_template_type(template_type) - check_template_selector_display(selector_label) -end - -def select_template_type(template_type) - find('.js-template-type-selector').click - find('.dropdown-content li', text: template_type).click -end - -def select_template(type, template) - find(".js-#{type}-selector-wrap").click - find('.dropdown-content li', text: template).click -end - -def check_template_selector_display(content) - expect(page).to have_content(content) -end - -def check_type_selector_toggle_text(template_type) - dropdown_toggle_button = find('.template-type-selector .dropdown-toggle-text') - expect(dropdown_toggle_button).to have_content(template_type) -end - -def create_and_edit_file(file_name) - visit project_new_blob_path(project, 'master', file_name: file_name) - click_button "Commit changes" - visit project_edit_blob_path(project, File.join(project.default_branch, file_name)) -end diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb index afc9a5fd232..0dfed209ce9 100644 --- a/spec/features/projects/files/undo_template_spec.rb +++ b/spec/features/projects/files/undo_template_spec.rb @@ -13,19 +13,6 @@ RSpec.describe 'Projects > Files > Template Undo Button', :js, feature_category: context 'editing a matching file and applying a template' do before do visit project_edit_blob_path(project, File.join(project.default_branch, "LICENSE")) - select_file_template_type('LICENSE') - select_file_template('.js-license-selector', 'Apache License 2.0') - end - - it 'reverts template application' do - try_template_undo('http://www.apache.org/licenses/', 'Apply a template') - end - end - - context 'creating a non-matching file' do - before do - visit project_new_blob_path(project, 'master') - select_file_template_type('LICENSE') select_file_template('.js-license-selector', 'Apache License 2.0') end @@ -53,7 +40,6 @@ end def check_content_reverted(template_content) find('.b-toaster a', text: 'Undo').click expect(page).not_to have_content(template_content) - expect(page).to have_css('.template-type-selector .dropdown-toggle-text') end def select_file_template(template_selector_selector, template_name) @@ -61,8 +47,3 @@ def select_file_template(template_selector_selector, template_name) find('.dropdown-content li', text: template_name).click wait_for_requests end - -def select_file_template_type(template_type) - find('.js-template-type-selector').click - find('.dropdown-content li', text: template_type).click -end diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb index 2870f729765..dc2fcdd7305 100644 --- a/spec/features/snippets/show_spec.rb +++ b/spec/features/snippets/show_spec.rb @@ -43,11 +43,6 @@ RSpec.describe 'Snippet', :js, feature_category: :source_code_management do sign_in(different_user) end - it 'does not have the sidebar' do - visit snippet_path(snippet) - - expect(page).to have_title _('Snippets') - expect(page).not_to have_css('aside.nav-sidebar') - end + it_behaves_like 'a dashboard page with sidebar', :dashboard_snippets_path, :snippets end end diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js index c201bbf4af2..b1e87aca63d 100644 --- a/spec/frontend/frequent_items/components/app_spec.js +++ b/spec/frontend/frequent_items/components/app_spec.js @@ -1,3 +1,4 @@ +import { GlButton, GlIcon } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; @@ -103,6 +104,7 @@ describe('Frequent Items App Component', () => { expect(loading.exists()).toBe(true); expect(loading.find('[aria-label="Loading projects"]').exists()).toBe(true); + expect(findSectionHeader().exists()).toBe(false); }); it('should render frequent projects list header', () => { @@ -112,25 +114,6 @@ describe('Frequent Items App Component', () => { expect(sectionHeader.text()).toBe('Frequently visited'); }); - it('should render frequent projects list', async () => { - const expectedResult = getTopFrequentItems(mockFrequentProjects); - localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(mockFrequentProjects)); - - expect(findFrequentItems().length).toBe(1); - - triggerDropdownOpen(); - await nextTick(); - - expect(findFrequentItems().length).toBe(expectedResult.length); - expect(findFrequentItemsList().props()).toEqual({ - items: expectedResult, - namespace: TEST_NAMESPACE, - hasSearchQuery: false, - isFetchFailed: false, - matcher: '', - }); - }); - it('should render searched projects list', async () => { mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects.data); @@ -164,6 +147,47 @@ describe('Frequent Items App Component', () => { }), ); }); + + describe('with frequent items list', () => { + const expectedResult = getTopFrequentItems(mockFrequentProjects); + + beforeEach(async () => { + localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(mockFrequentProjects)); + triggerDropdownOpen(); + await nextTick(); + }); + + it('should render edit button within header', () => { + const itemEditButton = findSectionHeader().findComponent(GlButton); + + expect(itemEditButton.exists()).toBe(true); + expect(itemEditButton.attributes('title')).toBe('Toggle edit mode'); + expect(itemEditButton.findComponent(GlIcon).props('name')).toBe('pencil'); + }); + + it('should render frequent projects list', () => { + expect(findFrequentItems().length).toBe(expectedResult.length); + expect(findFrequentItemsList().props()).toEqual({ + items: expectedResult, + namespace: TEST_NAMESPACE, + hasSearchQuery: false, + isFetchFailed: false, + isItemRemovalFailed: false, + matcher: '', + }); + }); + + it('dispatches action `toggleItemsListEditablity` when edit button is clicked', async () => { + const itemEditButton = findSectionHeader().findComponent(GlButton); + itemEditButton.vm.$emit('click'); + + await nextTick(); + + expect(store.dispatch).toHaveBeenCalledWith( + `${TEST_VUEX_MODULE}/toggleItemsListEditablity`, + ); + }); + }); }); describe('with searchClass', () => { diff --git a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js index e6673fa78ec..4f2badf869d 100644 --- a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js +++ b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js @@ -1,5 +1,5 @@ -import { GlButton } from '@gitlab/ui'; -import Vue from 'vue'; +import { GlIcon } from '@gitlab/ui'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { trimText } from 'helpers/text_helper'; @@ -12,6 +12,7 @@ import { mockProject } from '../mock_data'; Vue.use(Vuex); describe('FrequentItemsListItemComponent', () => { + const TEST_VUEX_MODULE = 'frequentProjects'; let wrapper; let trackingSpy; let store; @@ -20,11 +21,18 @@ describe('FrequentItemsListItemComponent', () => { const findAvatar = () => wrapper.findComponent(ProjectAvatar); const findAllTitles = () => wrapper.findAllByTestId('frequent-items-item-title'); const findNamespace = () => wrapper.findByTestId('frequent-items-item-namespace'); - const findAllButtons = () => wrapper.findAllComponents(GlButton); + const findAllFrequentItems = () => wrapper.findAllByTestId('frequent-item-link'); const findAllNamespace = () => wrapper.findAllByTestId('frequent-items-item-namespace'); const findAllAvatars = () => wrapper.findAllComponents(ProjectAvatar); const findAllMetadataContainers = () => wrapper.findAllByTestId('frequent-items-item-metadata-container'); + const findRemoveButton = () => wrapper.findByTestId('item-remove'); + + const toggleItemsListEditablity = async () => { + store.dispatch(`${TEST_VUEX_MODULE}/toggleItemsListEditablity`); + + await nextTick(); + }; const createComponent = (props = {}) => { wrapper = shallowMountExtended(frequentItemsListItemComponent, { @@ -38,7 +46,7 @@ describe('FrequentItemsListItemComponent', () => { ...props, }, provide: { - vuexModule: 'frequentProjects', + vuexModule: TEST_VUEX_MODULE, }, }); }; @@ -102,7 +110,7 @@ describe('FrequentItemsListItemComponent', () => { it.each` name | selector | expected - ${'button'} | ${findAllButtons} | ${1} + ${'list item'} | ${findAllFrequentItems} | ${1} ${'avatar container'} | ${findAllAvatars} | ${1} ${'metadata container'} | ${findAllMetadataContainers} | ${1} ${'title'} | ${findAllTitles} | ${1} @@ -111,8 +119,37 @@ describe('FrequentItemsListItemComponent', () => { expect(selector()).toHaveLength(expected); }); + it('renders remove button within item when `isItemsListEditable` is true', async () => { + await toggleItemsListEditablity(); + + const removeButton = findRemoveButton(); + expect(removeButton.exists()).toBe(true); + expect(removeButton.attributes('title')).toBe('Remove'); + expect(removeButton.findComponent(GlIcon).props('name')).toBe('close'); + }); + + it('dispatches action `removeFrequentItem` when remove button is clicked', async () => { + await toggleItemsListEditablity(); + + jest.spyOn(store, 'dispatch'); + + const removeButton = findRemoveButton(); + removeButton.vm.$emit( + 'click', + { stopPropagation: jest.fn(), preventDefault: jest.fn() }, + mockProject.id, + ); + + await nextTick(); + + expect(store.dispatch).toHaveBeenCalledWith( + `${TEST_VUEX_MODULE}/removeFrequentItem`, + mockProject.id, + ); + }); + it('tracks when item link is clicked', () => { - const link = wrapper.findComponent(GlButton); + const link = wrapper.findByTestId('frequent-item-link'); link.vm.$emit('click'); diff --git a/spec/frontend/frequent_items/components/frequent_items_list_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_spec.js index 9f08a432a3d..d024925f62b 100644 --- a/spec/frontend/frequent_items/components/frequent_items_list_spec.js +++ b/spec/frontend/frequent_items/components/frequent_items_list_spec.js @@ -18,6 +18,7 @@ describe('FrequentItemsListComponent', () => { namespace: 'projects', items: mockFrequentProjects, isFetchFailed: false, + isItemRemovalFailed: false, hasSearchQuery: false, matcher: 'lab', ...props, @@ -51,22 +52,34 @@ describe('FrequentItemsListComponent', () => { }); describe('fetched item messages', () => { - it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', async () => { + it('should show default empty list message', async () => { createComponent({ - isFetchFailed: true, + items: [], }); - expect(wrapper.vm.listEmptyMessage).toBe( - 'This feature requires browser localStorage support', + expect(wrapper.findByTestId('frequent-items-list-empty').text()).toContain( + 'Projects you visit often will appear here', ); - - wrapper.setProps({ - isFetchFailed: false, - }); - await nextTick(); - - expect(wrapper.vm.listEmptyMessage).toBe('Projects you visit often will appear here'); }); + + it.each` + isFetchFailed | isItemRemovalFailed + ${true} | ${false} + ${false} | ${true} + `( + 'should show failure message when `isFetchFailed` is $isFetchFailed or `isItemRemovalFailed` is $isItemRemovalFailed', + ({ isFetchFailed, isItemRemovalFailed }) => { + createComponent({ + items: [], + isFetchFailed, + isItemRemovalFailed, + }); + + expect(wrapper.findByTestId('frequent-items-list-empty').text()).toContain( + 'This feature requires browser localStorage support', + ); + }, + ); }); describe('searched item messages', () => { diff --git a/spec/frontend/frequent_items/store/actions_spec.js b/spec/frontend/frequent_items/store/actions_spec.js index 3fc3eaf52a2..4f998cc26da 100644 --- a/spec/frontend/frequent_items/store/actions_spec.js +++ b/spec/frontend/frequent_items/store/actions_spec.js @@ -5,6 +5,7 @@ import * as types from '~/frequent_items/store/mutation_types'; import state from '~/frequent_items/store/state'; import AccessorUtilities from '~/lib/utils/accessor'; import axios from '~/lib/utils/axios_utils'; +import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { mockNamespace, mockStorageKey, @@ -13,6 +14,7 @@ import { } from '../mock_data'; describe('Frequent Items Dropdown Store Actions', () => { + useLocalStorageSpy(); let mockedState; let mock; @@ -52,6 +54,18 @@ describe('Frequent Items Dropdown Store Actions', () => { }); }); + describe('toggleItemsListEditablity', () => { + it('should toggle items list editablity', () => { + return testAction( + actions.toggleItemsListEditablity, + null, + mockedState, + [{ type: types.TOGGLE_ITEMS_LIST_EDITABILITY }], + [], + ); + }); + }); + describe('requestFrequentItems', () => { it('should request frequent items', () => { return testAction( @@ -211,4 +225,77 @@ describe('Frequent Items Dropdown Store Actions', () => { ); }); }); + + describe('removeFrequentItemSuccess', () => { + it('should remove frequent item on success', () => { + return testAction( + actions.removeFrequentItemSuccess, + { itemId: 1 }, + mockedState, + [ + { + type: types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS, + payload: { itemId: 1 }, + }, + ], + [], + ); + }); + }); + + describe('removeFrequentItemError', () => { + it('should should not remove frequent item on failure', () => { + return testAction( + actions.removeFrequentItemError, + null, + mockedState, + [{ type: types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR }], + [], + ); + }); + }); + + describe('removeFrequentItem', () => { + beforeEach(() => { + mockedState.items = [...mockFrequentProjects]; + window.localStorage.setItem(mockStorageKey, JSON.stringify(mockFrequentProjects)); + }); + + it('should remove provided itemId from localStorage', () => { + jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true); + + actions.removeFrequentItem( + { commit: jest.fn(), dispatch: jest.fn(), state: mockedState }, + mockFrequentProjects[0].id, + ); + + expect(window.localStorage.getItem(mockStorageKey)).toBe( + JSON.stringify(mockFrequentProjects.slice(1)), // First item was removed + ); + }); + + it('should dispatch `removeFrequentItemSuccess` on localStorage update success', () => { + jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true); + + return testAction( + actions.removeFrequentItem, + mockFrequentProjects[0].id, + mockedState, + [], + [{ type: 'removeFrequentItemSuccess', payload: mockFrequentProjects[0].id }], + ); + }); + + it('should dispatch `removeFrequentItemError` on localStorage update failure', () => { + jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(false); + + return testAction( + actions.removeFrequentItem, + mockFrequentProjects[0].id, + mockedState, + [], + [{ type: 'removeFrequentItemError' }], + ); + }); + }); }); diff --git a/spec/frontend/frequent_items/store/mutations_spec.js b/spec/frontend/frequent_items/store/mutations_spec.js index e593c9fae58..1e1878c3377 100644 --- a/spec/frontend/frequent_items/store/mutations_spec.js +++ b/spec/frontend/frequent_items/store/mutations_spec.js @@ -44,6 +44,18 @@ describe('Frequent Items dropdown mutations', () => { }); }); + describe('TOGGLE_ITEMS_LIST_EDITABILITY', () => { + it('should toggle items list editablity', () => { + mutations[types.TOGGLE_ITEMS_LIST_EDITABILITY](stateCopy); + + expect(stateCopy.isItemsListEditable).toEqual(true); + + mutations[types.TOGGLE_ITEMS_LIST_EDITABILITY](stateCopy); + + expect(stateCopy.isItemsListEditable).toEqual(false); + }); + }); + describe('REQUEST_FREQUENT_ITEMS', () => { it('should set view states when requesting frequent items', () => { mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy); @@ -114,4 +126,27 @@ describe('Frequent Items dropdown mutations', () => { expect(stateCopy.isFetchFailed).toEqual(true); }); }); + + describe('RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS', () => { + it('should remove item with provided itemId from the items', () => { + stateCopy.isItemRemovalFailed = true; + stateCopy.items = mockFrequentProjects; + + mutations[types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS](stateCopy, mockFrequentProjects[0].id); + + expect(stateCopy.items).toHaveLength(mockFrequentProjects.length - 1); + expect(stateCopy.items).toEqual([...mockFrequentProjects.slice(1)]); + expect(stateCopy.isItemRemovalFailed).toBe(false); + }); + }); + + describe('RECEIVE_REMOVE_FREQUENT_ITEM_ERROR', () => { + it('should remove item with provided itemId from the items', () => { + stateCopy.isItemRemovalFailed = false; + + mutations[types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR](stateCopy); + + expect(stateCopy.isItemRemovalFailed).toBe(true); + }); + }); }); diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb index dc719679449..9af0b964221 100644 --- a/spec/lib/gitlab/database/tables_truncate_spec.rb +++ b/spec/lib/gitlab/database/tables_truncate_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba let(:main_db_shared_item_model) { table("_test_gitlab_shared_items", database: "main") } let(:main_db_partitioned_item) { table("_test_gitlab_hook_logs", database: "main") } let(:main_db_partitioned_item_detached) do - table("gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101", database: "main") + table("gitlab_partitions_dynamic._test_gitlab_hook_logs_202201", database: "main") end # CI Database @@ -29,7 +29,7 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba let(:ci_db_shared_item_model) { table("_test_gitlab_shared_items", database: "ci") } let(:ci_db_partitioned_item) { table("_test_gitlab_hook_logs", database: "ci") } let(:ci_db_partitioned_item_detached) do - table("gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101", database: "ci") + table("gitlab_partitions_dynamic._test_gitlab_hook_logs_202201", database: "ci") end shared_examples 'truncating legacy tables on a database' do @@ -64,19 +64,19 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba id bigserial not null, created_at timestamptz not null, item_id BIGINT NOT NULL, - primary key (id, created_at), + PRIMARY KEY (id, created_at), CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id) ) PARTITION BY RANGE(created_at); - CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101 + CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202201 PARTITION OF _test_gitlab_hook_logs FOR VALUES FROM ('20220101') TO ('20220131'); - CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_20220201 + CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202202 PARTITION OF _test_gitlab_hook_logs FOR VALUES FROM ('20220201') TO ('20220228'); - ALTER TABLE _test_gitlab_hook_logs DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_hook_logs_20220101; + ALTER TABLE _test_gitlab_hook_logs DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_hook_logs_202201; SQL main_connection.execute(main_tables_sql) @@ -124,14 +124,14 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba Gitlab::Database::SharedModel.using_connection(main_connection) do Postgresql::DetachedPartition.create!( - table_name: '_test_gitlab_hook_logs_20220101', + table_name: '_test_gitlab_hook_logs_202201', drop_after: Time.current ) end Gitlab::Database::SharedModel.using_connection(ci_connection) do Postgresql::DetachedPartition.create!( - table_name: '_test_gitlab_hook_logs_20220101', + table_name: '_test_gitlab_hook_logs_202201', drop_after: Time.current ) end @@ -237,6 +237,25 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba end end + context 'when one of the attached partitions happened to be locked for writes' do + before do + skip if connection.pool.db_config.name != 'ci' + + Gitlab::Database::LockWritesManager.new( + table_name: "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}._test_gitlab_hook_logs_202202", + connection: connection, + database_name: connection.pool.db_config.name, + with_retries: false + ).lock_writes + end + + it 'truncates the locked partition successfully' do + expect do + truncate_legacy_tables + end.to change { ci_db_partitioned_item.count }.from(5).to(0) + end + end + context 'with geo configured' do let(:geo_connection) { Gitlab::Database.database_base_models[:geo].connection } diff --git a/spec/views/layouts/snippets.html.haml_spec.rb b/spec/views/layouts/snippets.html.haml_spec.rb index 581b7a898fa..69378906bcd 100644 --- a/spec/views/layouts/snippets.html.haml_spec.rb +++ b/spec/views/layouts/snippets.html.haml_spec.rb @@ -3,19 +3,45 @@ require 'spec_helper' RSpec.describe 'layouts/snippets', feature_category: :snippets do - let(:user) { build_stubbed(:user) } - before do allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user)) end - describe "sidebar" do - context "when @snippet is not set" do + describe 'sidebar' do + context 'when feature flag is on' do + context 'when signed in' do + let(:user) { build_stubbed(:user) } + + it 'renders the "Your work" sidebar' do + render + + expect(rendered).to have_css('aside.nav-sidebar[aria-label="Your work"]') + end + end + + context 'when not signed in' do + let(:user) { nil } + + it 'renders no sidebar' do + render + + expect(rendered).not_to have_css('aside.nav-sidebar') + end + end + end + + context 'when feature flag is off' do + before do + stub_feature_flags(your_work_sidebar: false) + end + + let(:user) { build_stubbed(:user) } + it 'renders no sidebar' do render - expect(rendered).not_to have_css("aside.nav-sidebar") + expect(rendered).not_to have_css('aside.nav-sidebar') end end end |