diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 0890fe473c6..a69085090c5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1275,5 +1275,3 @@ FactoryBot/InlineAssociation: - 'spec/factories/packages.rb' - 'spec/factories/packages/package_file.rb' - 'spec/factories/sent_notifications.rb' - - 'spec/factories/uploads.rb' - - 'spec/factories/wiki_pages.rb' diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index cfc730712d5..5e57fb89558 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.28.0 +1.29.0 diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue new file mode 100644 index 00000000000..1d3bd312b09 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue @@ -0,0 +1,211 @@ + + + diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 7a87a968489..00d32b75628 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -921,6 +921,25 @@ } } +/* + * Following overrides are done to prevent + * legacy dropdown styles from influencing + * GitLab UI components used within GlDropdown + */ +.issuable-move-dropdown { + .b-dropdown-form { + @include gl-p-0; + } + + .gl-search-box-by-type button.gl-clear-icon-button:hover { + @include gl-bg-transparent; + } + + .issuable-move-button:not(.disabled):hover { + @include gl-text-white; + } +} + .right-sidebar-collapsed { .sidebar-grouped-item { .sidebar-collapsed-icon { diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb index 9fc3ec0aafb..505ddaf50e3 100644 --- a/app/services/projects/container_repository/delete_tags_service.rb +++ b/app/services/projects/container_repository/delete_tags_service.rb @@ -36,11 +36,11 @@ module Projects def log_response(response) log_data = LOG_DATA_BASE.merge( container_repository_id: @container_repository.id, - message: 'deleted tags' - ) + message: 'deleted tags', + deleted_tags_count: response[:deleted]&.size + ).compact if response[:status] == :success - log_data[:deleted_tags_count] = response[:deleted].size log_info(log_data) else log_data[:message] = response[:message] diff --git a/app/services/projects/container_repository/gitlab/delete_tags_service.rb b/app/services/projects/container_repository/gitlab/delete_tags_service.rb index cee94b994a3..e4e22dd9543 100644 --- a/app/services/projects/container_repository/gitlab/delete_tags_service.rb +++ b/app/services/projects/container_repository/gitlab/delete_tags_service.rb @@ -14,6 +14,7 @@ module Projects def initialize(container_repository, tag_names) @container_repository = container_repository @tag_names = tag_names + @deleted_tags = [] end # Delete tags by name with a single DELETE request. This is only supported @@ -25,7 +26,7 @@ module Projects delete_tags rescue TimeoutError => e ::Gitlab::ErrorTracking.track_exception(e, tags_count: @tag_names&.size, container_repository_id: @container_repository&.id) - error('timeout while deleting tags') + error('timeout while deleting tags', nil, pass_back: { deleted: @deleted_tags }) end private @@ -33,13 +34,15 @@ module Projects def delete_tags start_time = Time.zone.now - deleted_tags = @tag_names.select do |name| + @tag_names.each do |name| raise TimeoutError if timeout?(start_time) - @container_repository.delete_tag_by_name(name) + if @container_repository.delete_tag_by_name(name) + @deleted_tags.append(name) + end end - deleted_tags.any? ? success(deleted: deleted_tags) : error('could not delete tags') + @deleted_tags.any? ? success(deleted: @deleted_tags) : error('could not delete tags') end def timeout?(start_time) diff --git a/changelogs/unreleased/delete_tags_service_logging_enhancement.yml b/changelogs/unreleased/delete_tags_service_logging_enhancement.yml new file mode 100644 index 00000000000..c248634ae42 --- /dev/null +++ b/changelogs/unreleased/delete_tags_service_logging_enhancement.yml @@ -0,0 +1,6 @@ +--- +title: Improving Container Registry Delete Tags Service to log number of successfully + deleted tags even if deletion process was interrupted by a timeout +merge_request: 46079 +author: Maksim Stankevic, @maksimstankevic +type: changed diff --git a/changelogs/unreleased/kerrizor-increase-default-diff_max_patch_bytes.yml b/changelogs/unreleased/kerrizor-increase-default-diff_max_patch_bytes.yml new file mode 100644 index 00000000000..0eeaf26ba1e --- /dev/null +++ b/changelogs/unreleased/kerrizor-increase-default-diff_max_patch_bytes.yml @@ -0,0 +1,5 @@ +--- +title: Update diff_max_patch_bytes from 100kb -> 200kb +merge_request: 46276 +author: +type: changed diff --git a/changelogs/unreleased/upgrade-pages-1-29-0.yml b/changelogs/unreleased/upgrade-pages-1-29-0.yml new file mode 100644 index 00000000000..06a630f87f7 --- /dev/null +++ b/changelogs/unreleased/upgrade-pages-1-29-0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade GitLab Pages to 1.29.0 +merge_request: 46665 +author: +type: added diff --git a/db/migrate/20201027211138_increase_default_diff_max_patch_bytes.rb b/db/migrate/20201027211138_increase_default_diff_max_patch_bytes.rb new file mode 100644 index 00000000000..92d9025706f --- /dev/null +++ b/db/migrate/20201027211138_increase_default_diff_max_patch_bytes.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class IncreaseDefaultDiffMaxPatchBytes < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + change_column_default(:application_settings, :diff_max_patch_bytes, from: 102400, to: 204800) + end +end diff --git a/db/migrate/20201028204306_migrate_default_diff_max_patch_bytes_to_minimum_200kb.rb b/db/migrate/20201028204306_migrate_default_diff_max_patch_bytes_to_minimum_200kb.rb new file mode 100644 index 00000000000..11d47904e67 --- /dev/null +++ b/db/migrate/20201028204306_migrate_default_diff_max_patch_bytes_to_minimum_200kb.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class MigrateDefaultDiffMaxPatchBytesToMinimum200kb < ActiveRecord::Migration[6.0] + DOWNTIME = false + MAX_SIZE = 200.kilobytes + + class ApplicationSetting < ActiveRecord::Base + self.table_name = 'application_settings' + end + + def up + table = ApplicationSetting.arel_table + ApplicationSetting.where(table[:diff_max_patch_bytes].lt(MAX_SIZE)).update_all(diff_max_patch_bytes: MAX_SIZE) + end + + def down + table = ApplicationSetting.arel_table + ApplicationSetting.where(table[:diff_max_patch_bytes].eq(MAX_SIZE)).update_all(diff_max_patch_bytes: 100.kilobytes) + end +end diff --git a/db/schema_migrations/20201027211138 b/db/schema_migrations/20201027211138 new file mode 100644 index 00000000000..5e36445702b --- /dev/null +++ b/db/schema_migrations/20201027211138 @@ -0,0 +1 @@ +402e9a6e92802888ba01ee216850ab5b0fe9997a84415c9ffe8d5d37a9823220 \ No newline at end of file diff --git a/db/schema_migrations/20201028204306 b/db/schema_migrations/20201028204306 new file mode 100644 index 00000000000..03b68c9a0d1 --- /dev/null +++ b/db/schema_migrations/20201028204306 @@ -0,0 +1 @@ +ec5bab20a1b591b77b48b85dc0b871e88a41891d256201b7d8eb86195ef1c4ad \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index dbb20221d96..71a66997b20 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9207,7 +9207,7 @@ CREATE TABLE application_settings ( custom_project_templates_group_id integer, usage_stats_set_by_user_id integer, receive_max_input_size integer, - diff_max_patch_bytes integer DEFAULT 102400 NOT NULL, + diff_max_patch_bytes integer DEFAULT 204800 NOT NULL, archive_builds_in_seconds integer, commit_email_hostname character varying, protected_ci_variables boolean DEFAULT true NOT NULL, @@ -12749,10 +12749,10 @@ CREATE TABLE group_wiki_repositories ( CREATE TABLE historical_data ( id integer NOT NULL, + date date, active_user_count integer, created_at timestamp without time zone, updated_at timestamp without time zone, - date date, recorded_at timestamp with time zone, CONSTRAINT check_640e8cf66c CHECK ((recorded_at IS NOT NULL)) ); diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index 78c47023c08..209917073c7 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -25,7 +25,7 @@ module Gitlab # # If this value ever changes, make sure to create a migration to update # current records, and default of `ApplicationSettings#diff_max_patch_bytes`. - DEFAULT_MAX_PATCH_BYTES = 100.kilobytes + DEFAULT_MAX_PATCH_BYTES = 200.kilobytes # This is a limitation applied on the source (Gitaly), therefore we don't allow # persisting limits over that. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5dae947d5e6..383500ebcd3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11139,6 +11139,9 @@ msgstr "" msgid "Failed to load milestones. Please try again." msgstr "" +msgid "Failed to load projects" +msgstr "" + msgid "Failed to load related branches" msgstr "" diff --git a/package.json b/package.json index a9cb06b6487..225b77b3241 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.174.0", - "@gitlab/ui": "21.41.0", + "@gitlab/ui": "21.42.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-3", "@rails/ujs": "^6.0.3-2", diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb index b19af277cc3..85237e2d791 100644 --- a/spec/factories/uploads.rb +++ b/spec/factories/uploads.rb @@ -2,7 +2,7 @@ FactoryBot.define do factory :upload do - model { create(:project) } + model { association(:project) } size { 100.kilobytes } uploader { "AvatarUploader" } mount_point { :avatar } @@ -20,7 +20,7 @@ FactoryBot.define do end trait :personal_snippet_upload do - model { create(:personal_snippet) } + model { association(:personal_snippet) } path { File.join(secret, filename) } uploader { "PersonalFileUploader" } secret { SecureRandom.hex } @@ -46,7 +46,7 @@ FactoryBot.define do end trait :namespace_upload do - model { create(:group) } + model { association(:group) } path { File.join(secret, filename) } uploader { "NamespaceFileUploader" } secret { SecureRandom.hex } @@ -54,7 +54,7 @@ FactoryBot.define do end trait :favicon_upload do - model { create(:appearance) } + model { association(:appearance) } uploader { "FaviconUploader" } secret { SecureRandom.hex } mount_point { :favicon } @@ -62,13 +62,13 @@ FactoryBot.define do trait :attachment_upload do mount_point { :attachment } - model { create(:note) } + model { association(:note) } uploader { "AttachmentUploader" } end trait :design_action_image_v432x230_upload do mount_point { :image_v432x230 } - model { create(:design_action) } + model { association(:design_action) } uploader { ::DesignManagement::DesignV432x230Uploader.name } end end diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb index 3397277839e..6f912a183e8 100644 --- a/spec/factories/wiki_pages.rb +++ b/spec/factories/wiki_pages.rb @@ -39,14 +39,14 @@ FactoryBot.define do factory :wiki_page_meta, class: 'WikiPage::Meta' do title { generate(:wiki_page_title) } - project { create(:project) } + project { association(:project) } trait :for_wiki_page do transient do - wiki_page { create(:wiki_page, container: project) } + wiki_page { association(:wiki_page, container: project) } end - project { @overrides[:wiki_page]&.container || create(:project) } + project { @overrides[:wiki_page]&.container || association(:project) } title { wiki_page.title } initialize_with do @@ -58,7 +58,7 @@ FactoryBot.define do end factory :wiki_page_slug, class: 'WikiPage::Slug' do - wiki_page_meta { create(:wiki_page_meta) } + wiki_page_meta { association(:wiki_page_meta) } slug { generate(:sluggified_title) } canonical { false } diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 49343cc7a57..0912df22924 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -8,6 +8,8 @@ RSpec.describe 'Expand and collapse diffs', :js do before do stub_feature_flags(increased_diff_limits: false) + allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.kilobytes) + sign_in(create(:admin)) # Ensure that undiffable.md is in .gitattributes diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb index 08a92d5bc21..09c5897f102 100644 --- a/spec/features/merge_request/user_expands_diff_spec.rb +++ b/spec/features/merge_request/user_expands_diff_spec.rb @@ -8,6 +8,8 @@ RSpec.describe 'User expands diff', :js do before do stub_feature_flags(increased_diff_limits: false) + allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.kilobytes) + visit(diffs_project_merge_request_path(project, merge_request)) wait_for_requests diff --git a/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap index eaa7460ae15..a91ce43cda1 100644 --- a/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap +++ b/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap @@ -44,7 +44,7 @@ exports[`Design management upload button component renders loading icon 1`] = ` diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap index 3d6c2561ff6..03ae77d4977 100644 --- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap +++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap @@ -136,7 +136,7 @@ exports[`Design management design index page sets loading state 1`] = ` > diff --git a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap b/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap index 42012841f0b..6640c0844e2 100644 --- a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap +++ b/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap @@ -27,7 +27,7 @@ exports[`SidebarTodo template renders component container element with proper da + shallowMount(IssuableMoveDropdown, { + propsData, + }); + +describe('IssuableMoveDropdown', () => { + let mock; + let wrapper; + + beforeEach(() => { + mock = new MockAdapter(axios); + wrapper = createComponent(); + wrapper.vm.$refs.dropdown.hide = jest.fn(); + wrapper.vm.$refs.searchInput.focusInput = jest.fn(); + }); + + afterEach(() => { + wrapper.destroy(); + mock.restore(); + }); + + describe('watch', () => { + describe('searchKey', () => { + it('calls `fetchProjects` with value of the prop', async () => { + jest.spyOn(wrapper.vm, 'fetchProjects'); + wrapper.setData({ + searchKey: 'foo', + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.fetchProjects).toHaveBeenCalledWith('foo'); + }); + }); + }); + + describe('methods', () => { + describe('fetchProjects', () => { + it('sets projectsListLoading to true and projectsListLoadFailed to false', () => { + wrapper.vm.fetchProjects(); + + expect(wrapper.vm.projectsListLoading).toBe(true); + expect(wrapper.vm.projectsListLoadFailed).toBe(false); + }); + + it('calls `axios.get` with `projectsFetchPath` and query param `search`', () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + wrapper.vm.fetchProjects('foo'); + + expect(axios.get).toHaveBeenCalledWith( + mockProps.projectsFetchPath, + expect.objectContaining({ + params: { + search: 'foo', + }, + }), + ); + }); + + it('sets response to `projects` and focuses on searchInput when request is successful', async () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projects).toBe(mockProjects); + expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled(); + }); + + it('sets projectsListLoadFailed to true when request fails', async () => { + jest.spyOn(axios, 'get').mockRejectedValue({}); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projectsListLoadFailed).toBe(true); + }); + + it('sets projectsListLoading to false when request completes', async () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projectsListLoading).toBe(false); + }); + }); + + describe('isSelectedProject', () => { + it.each` + project | selectedProject | title | returnValue + ${mockProjects[0]} | ${mockProjects[0]} | ${'are same projects'} | ${true} + ${mockProjects[0]} | ${mockProjects[1]} | ${'are different projects'} | ${false} + `( + 'returns $returnValue when selectedProject and provided project param $title', + async ({ project, selectedProject, returnValue }) => { + wrapper.setData({ + selectedProject, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isSelectedProject(project)).toBe(returnValue); + }, + ); + + it('returns false when selectedProject is null', async () => { + wrapper.setData({ + selectedProject: null, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isSelectedProject(mockProjects[0])).toBe(false); + }); + }); + }); + + describe('template', () => { + const findDropdownEl = () => wrapper.find(GlDropdown); + + it('renders collapsed state element with icon', () => { + const collapsedEl = wrapper.find('[data-testid="move-collapsed"]'); + + expect(collapsedEl.exists()).toBe(true); + expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle); + expect(collapsedEl.find(GlIcon).exists()).toBe(true); + expect(collapsedEl.find(GlIcon).props('name')).toBe('arrow-right'); + }); + + describe('gl-dropdown component', () => { + it('renders component container element', () => { + expect(findDropdownEl().exists()).toBe(true); + expect(findDropdownEl().props('block')).toBe(true); + }); + + it('renders gl-dropdown-form component', () => { + expect( + findDropdownEl() + .find(GlDropdownForm) + .exists(), + ).toBe(true); + }); + + it('renders header element', () => { + const headerEl = findDropdownEl().find('[data-testid="header"]'); + + expect(headerEl.exists()).toBe(true); + expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle); + expect(headerEl.find(GlButton).props('icon')).toBe('close'); + }); + + it('renders gl-search-box-by-type component', () => { + const searchEl = findDropdownEl().find(GlSearchBoxByType); + + expect(searchEl.exists()).toBe(true); + expect(searchEl.attributes()).toMatchObject({ + placeholder: 'Search project', + debounce: '300', + }); + }); + + it('renders gl-loading-icon component when projectsListLoading prop is true', async () => { + wrapper.setData({ + projectsListLoading: true, + }); + + await wrapper.vm.$nextTick(); + + expect( + findDropdownEl() + .find(GlLoadingIcon) + .exists(), + ).toBe(true); + }); + + it('renders gl-dropdown-item components for available projects', async () => { + wrapper.setData({ + projects: mockProjects, + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + const dropdownItems = wrapper.findAll(GlDropdownItem); + + expect(dropdownItems).toHaveLength(mockProjects.length); + expect(dropdownItems.at(0).props()).toMatchObject({ + isCheckItem: true, + isChecked: true, + }); + expect(dropdownItems.at(0).text()).toBe(mockProjects[0].name_with_namespace); + }); + + it('renders string "No matching results" when search does not yield any matches', async () => { + wrapper.setData({ + searchKey: 'foo', + }); + + // Wait for `searchKey` watcher to run. + await wrapper.vm.$nextTick(); + + wrapper.setData({ + projects: [], + projectsListLoading: false, + }); + + await wrapper.vm.$nextTick(); + + const dropdownContentEl = wrapper.find('[data-testid="content"]'); + + expect(dropdownContentEl.text()).toContain('No matching results'); + }); + + it('renders string "Failed to load projects" when loading projects list fails', async () => { + wrapper.setData({ + projects: [], + projectsListLoading: false, + projectsListLoadFailed: true, + }); + + await wrapper.vm.$nextTick(); + + const dropdownContentEl = wrapper.find('[data-testid="content"]'); + + expect(dropdownContentEl.text()).toContain('Failed to load projects'); + }); + + it('renders gl-button within footer', async () => { + const moveButtonEl = wrapper.find('[data-testid="footer"]').find(GlButton); + + expect(moveButtonEl.text()).toBe('Move'); + expect(moveButtonEl.attributes('disabled')).toBe('true'); + + wrapper.setData({ + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + expect( + wrapper + .find('[data-testid="footer"]') + .find(GlButton) + .attributes('disabled'), + ).not.toBeDefined(); + }); + }); + + describe('events', () => { + it('collapsed state element emits `toggle-collapse` event on component when clicked', () => { + wrapper.find('[data-testid="move-collapsed"]').trigger('click'); + + expect(wrapper.emitted('toggle-collapse')).toBeTruthy(); + }); + + it('gl-dropdown component calls `fetchProjects` on `shown` event', () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + findDropdownEl().vm.$emit('shown'); + + expect(axios.get).toHaveBeenCalled(); + }); + + it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => { + wrapper.setData({ + projectItemClick: true, + }); + + findDropdownEl().vm.$emit('hide', mockEvent); + + expect(mockEvent.preventDefault).toHaveBeenCalled(); + expect(wrapper.vm.projectItemClick).toBe(false); + }); + + it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', async () => { + findDropdownEl().vm.$emit('hide'); + + expect(wrapper.emitted('dropdown-close')).toBeTruthy(); + }); + + it('close icon in dropdown header closes the dropdown when clicked', () => { + wrapper + .find('[data-testid="header"]') + .find(GlButton) + .vm.$emit('click', mockEvent); + + expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); + }); + + it('sets project for clicked gl-dropdown-item to selectedProject', async () => { + wrapper.setData({ + projects: mockProjects, + }); + + await wrapper.vm.$nextTick(); + + wrapper + .findAll(GlDropdownItem) + .at(0) + .vm.$emit('click', mockEvent); + + expect(wrapper.vm.selectedProject).toBe(mockProjects[0]); + }); + + it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => { + wrapper.setData({ + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + wrapper + .find('[data-testid="footer"]') + .find(GlButton) + .vm.$emit('click'); + + expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); + expect(wrapper.emitted('move-issuable')).toBeTruthy(); + expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]); + }); + }); + }); +}); diff --git a/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap b/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap index 6c120898f01..b84f12df4f6 100644 --- a/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap +++ b/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap @@ -114,7 +114,7 @@ exports[`WebIDE runs 1`] = ` > diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index f977fe1638f..b09bd9dff1b 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -32,7 +32,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do safe_max_files: 100, safe_max_lines: 5000, safe_max_bytes: 512000, - max_patch_bytes: 102400 + max_patch_bytes: 204800 ) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) @@ -57,7 +57,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do safe_max_files: 100, safe_max_lines: 5000, safe_max_bytes: 512000, - max_patch_bytes: 102400 + max_patch_bytes: 204800 ) expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 8b5f74df8f8..e551db3552e 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -105,7 +105,7 @@ RSpec.describe API::Settings, 'Settings' do enforce_terms: true, terms: 'Hello world!', performance_bar_allowed_group_path: group.full_path, - diff_max_patch_bytes: 150_000, + diff_max_patch_bytes: 300_000, default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE, local_markdown_version: 3, allow_local_requests_from_web_hooks_and_services: true, @@ -148,7 +148,7 @@ RSpec.describe API::Settings, 'Settings' do expect(json_response['enforce_terms']).to be(true) expect(json_response['terms']).to eq('Hello world!') expect(json_response['performance_bar_allowed_group_id']).to eq(group.id) - expect(json_response['diff_max_patch_bytes']).to eq(150_000) + expect(json_response['diff_max_patch_bytes']).to eq(300_000) expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) expect(json_response['local_markdown_version']).to eq(3) expect(json_response['allow_local_requests_from_web_hooks_and_services']).to eq(true) diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index c3ae26b1f05..a012ec29be5 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -27,13 +27,17 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - RSpec.shared_examples 'logging an error response' do |message: 'could not delete tags'| + RSpec.shared_examples 'logging an error response' do |message: 'could not delete tags', extra_log: {}| it 'logs an error message' do - expect(service).to receive(:log_error).with( - service_class: 'Projects::ContainerRepository::DeleteTagsService', - message: message, - container_repository_id: repository.id - ) + log_data = { + service_class: 'Projects::ContainerRepository::DeleteTagsService', + message: message, + container_repository_id: repository.id + } + + log_data.merge!(extra_log) if extra_log.any? + + expect(service).to receive(:log_error).with(log_data) subject end @@ -115,7 +119,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do it { is_expected.to include(status: :error, message: 'timeout while deleting tags') } - it_behaves_like 'logging an error response', message: 'timeout while deleting tags' + it_behaves_like 'logging an error response', message: 'timeout while deleting tags', extra_log: { deleted_tags_count: 0 } end end end diff --git a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb index 3bbcec8775e..988971171fc 100644 --- a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do stub_delete_reference_requests('A' => 200) end - it { is_expected.to include(status: :error, message: 'timeout while deleting tags') } + it { is_expected.to eq(status: :error, message: 'timeout while deleting tags', deleted: ['A']) } it 'tracks the exception' do expect(::Gitlab::ErrorTracking) diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb index 1ae74979b7a..003705ca21c 100644 --- a/spec/support/shared_examples/services/alert_management_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb @@ -8,11 +8,11 @@ RSpec.shared_examples 'creates an alert management alert' do end it 'executes the alert service hooks' do - slack_service = create(:service, type: 'SlackService', project: project, alert_events: true, active: true) + expect_next_instance_of(AlertManagement::Alert) do |alert| + expect(alert).to receive(:execute_services) + end subject - - expect(ProjectServiceWorker).to have_received(:perform_async).with(slack_service.id, an_instance_of(Hash)) end end diff --git a/yarn.lock b/yarn.lock index d2939cb94c0..1eafecb9c7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -866,10 +866,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.174.0.tgz#954b4d908a6188a2fcc45f00f748beeb23f054b0" integrity sha512-CgnZvO2miZkWxANhFdaK+2S4qRgkrMRE3vh3Xxwc+hIV9ki9KavlAAez9MNIs0Um/SJ1UpfmqKoM/dMyZX7K/w== -"@gitlab/ui@21.41.0": - version "21.41.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.41.0.tgz#185f5a534d6cd038f48588f432a25576d08c5780" - integrity sha512-Jl0OcEMQ+GKB9wTnZH9rU6YmL3rVDMHkEZc7Sa5QvNvP6A1Se/UEKbzhLi9rdyAoKpvrm3++tYg3ZJklIRZIsg== +"@gitlab/ui@21.42.0": + version "21.42.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.42.0.tgz#9cba612a6b0c8ee533865fa0c1e12243ced0c6e2" + integrity sha512-1q55KuGozOZ3iQPzE+GD7ChX+BCCe9eYGtaLMrFP6mjtyGDI1v9AHfYqHIqgS3+chaTKWCr8YpJ0PECPaLLM6A== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0"