diff options
15 files changed, 125 insertions, 61 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 05aa5530ca2..e747e7a4858 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -1253,8 +1253,11 @@ lib/gitlab/checks/** @proglottis @toon /app/models/generic_commit_status.rb /app/models/namespace_ci_cd_setting.rb /app/models/project_ci_cd_setting.rb +/app/models/projects/build_artifacts_size_refresh.rb /app/presenters/commit_status_presenter.rb /app/presenters/generic_commit_status_presenter.rb +/app/services/projects/refresh_build_artifacts_size_statistics_service.rb +/app/uploaders/job_artifact_uploader.rb /app/validators/json_schemas/build_metadata_id_tokens.json /app/views/projects/artifacts/ /app/views/projects/generic_commit_statuses/ @@ -1273,6 +1276,7 @@ lib/gitlab/checks/** @proglottis @toon /app/workers/pipeline_notification_worker.rb /app/workers/pipeline_process_worker.rb /app/workers/pipeline_schedule_worker.rb +/app/workers/projects/refresh_build_artifacts_size_statistics_worker.rb /app/workers/run_pipeline_schedule_worker.rb /app/workers/stuck_ci_jobs_worker.rb /app/workers/update_external_pull_requests_worker.rb diff --git a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue index 6807b4b2c7e..302b6a9d4b0 100644 --- a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue +++ b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue @@ -2,6 +2,7 @@ import { GlButton, GlCollapse } from '@gitlab/ui'; import { __ } from '~/locale'; import { isCollapsed, toggleSuperSidebarCollapsed } from '../super_sidebar_collapsed_state_manager'; +import { SIDEBAR_VISIBILITY_CLASS } from '../constants'; import UserBar from './user_bar.vue'; import SidebarPortalTarget from './sidebar_portal_target.vue'; import ContextSwitcherToggle from './context_switcher_toggle.vue'; @@ -10,6 +11,7 @@ import HelpCenter from './help_center.vue'; import SidebarMenu from './sidebar_menu.vue'; export default { + SIDEBAR_VISIBILITY_CLASS, components: { GlButton, GlCollapse, @@ -32,7 +34,7 @@ export default { data() { return { contextSwitcherOpen: false, - isCollapased: isCollapsed(), + isCollapsed: isCollapsed(), }; }, computed: { @@ -57,10 +59,10 @@ export default { <aside id="super-sidebar" class="super-sidebar" - :class="{ 'gl-visibility-hidden': isCollapased }" + :class="{ [$options.SIDEBAR_VISIBILITY_CLASS]: isCollapsed }" data-testid="super-sidebar" data-qa-selector="navbar" - :inert="isCollapased" + :inert="isCollapsed" tabindex="-1" > <gl-button diff --git a/app/assets/javascripts/super_sidebar/constants.js b/app/assets/javascripts/super_sidebar/constants.js index 8290c4f533f..119c83e29bb 100644 --- a/app/assets/javascripts/super_sidebar/constants.js +++ b/app/assets/javascripts/super_sidebar/constants.js @@ -10,6 +10,8 @@ export const portalState = Vue.observable({ ready: false, }); +export const SIDEBAR_VISIBILITY_CLASS = 'gl-visibility-hidden'; + export const MAX_FREQUENT_PROJECTS_COUNT = 5; export const MAX_FREQUENT_GROUPS_COUNT = 3; diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js index d1d9b7c714c..3d798d278bf 100644 --- a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js +++ b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js @@ -1,6 +1,7 @@ import { GlBreakpointInstance as bp, breakpoints } from '@gitlab/ui/dist/utils'; import { debounce } from 'lodash'; import { setCookie, getCookie } from '~/lib/utils/common_utils'; +import { SIDEBAR_VISIBILITY_CLASS } from './constants'; export const SIDEBAR_COLLAPSED_CLASS = 'page-with-super-sidebar-collapsed'; export const SIDEBAR_COLLAPSED_COOKIE = 'super_sidebar_collapsed'; @@ -20,6 +21,22 @@ export const isDesktopBreakpoint = () => bp.windowWidth() >= breakpoints.xl; export const getCollapsedCookie = () => getCookie(SIDEBAR_COLLAPSED_COOKIE) === 'true'; +const show = (sidebar, isUserAction) => { + sidebar.classList.remove(SIDEBAR_VISIBILITY_CLASS); + if (isUserAction) { + sidebar.focus(); + } +}; + +const hide = (sidebar, toggle, isUserAction) => { + setTimeout(() => { + sidebar.classList.add(SIDEBAR_VISIBILITY_CLASS); + if (isUserAction) { + toggle.focus(); + } + }, SIDEBAR_TRANSITION_DURATION); +}; + export const toggleSuperSidebarCollapsed = (collapsed, saveCookie, isUserAction) => { const page = findPage(); const toggle = findToggle(); @@ -28,16 +45,10 @@ export const toggleSuperSidebarCollapsed = (collapsed, saveCookie, isUserAction) page.classList.toggle(SIDEBAR_COLLAPSED_CLASS, collapsed); sidebar.inert = collapsed; - if (isUserAction) { - if (collapsed) { - setTimeout(() => { - sidebar.classList.add('gl-visibility-hidden'); - toggle.focus(); - }, SIDEBAR_TRANSITION_DURATION); - } else { - sidebar.classList.remove('gl-visibility-hidden'); - sidebar.focus(); - } + if (collapsed) { + hide(sidebar, toggle, isUserAction); + } else { + show(sidebar, isUserAction); } if (saveCookie && isDesktopBreakpoint()) { diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue index 18eb529c40f..bdc8ffee90a 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue @@ -50,7 +50,7 @@ export default { @click="onClick" > <div - class="gl-display-flex gl-align-items-center gl-flex-wrap-wrap project-namespace-name-container" + class="gl-display-flex gl-align-items-center gl-flex-wrap project-namespace-name-container" > <gl-icon v-if="selected" data-testid="selected-icon" name="mobile-issue-close" /> <project-avatar diff --git a/app/assets/javascripts/vue_shared/components/registry/title_area.vue b/app/assets/javascripts/vue_shared/components/registry/title_area.vue index 0b9b5a0961e..ad979387596 100644 --- a/app/assets/javascripts/vue_shared/components/registry/title_area.vue +++ b/app/assets/javascripts/vue_shared/components/registry/title_area.vue @@ -89,7 +89,7 @@ export default { <div v-if="metadataSlots.length > 0" - class="gl-display-flex gl-flex-wrap-wrap gl-align-items-center gl-mt-3" + class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3" > <template v-if="!metadataLoading"> <div diff --git a/app/assets/javascripts/work_items/components/notes/work_item_notes_activity_header.vue b/app/assets/javascripts/work_items/components/notes/work_item_notes_activity_header.vue index a981742773e..0c1419e983f 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_notes_activity_header.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_notes_activity_header.vue @@ -56,7 +56,7 @@ export default { <template> <div - class="gl-display-flex gl-justify-content-space-between gl-flex-wrap-wrap gl-pb-3 gl-align-items-center" + class="gl-display-flex gl-justify-content-space-between gl-flex-wrap gl-pb-3 gl-align-items-center" > <h3 class="gl-font-base gl-m-0">{{ $options.i18n.activityLabel }}</h3> <div class="gl-display-flex gl-gap-3"> diff --git a/app/assets/javascripts/work_items/components/work_item_due_date.vue b/app/assets/javascripts/work_items/components/work_item_due_date.vue index 1bec5958b73..3e546598dc2 100644 --- a/app/assets/javascripts/work_items/components/work_item_due_date.vue +++ b/app/assets/javascripts/work_items/components/work_item_due_date.vue @@ -201,7 +201,7 @@ export default { <span v-if="isReadonlyWithNoDates" class="gl-text-secondary gl-ml-4"> {{ $options.i18n.none }} </span> - <div v-else class="gl-display-flex gl-flex-wrap-wrap gl-gap-5"> + <div v-else class="gl-display-flex gl-flex-wrap gl-gap-5"> <gl-form-group class="gl-display-flex gl-align-items-center gl-m-0" :class="{ 'gl-ml-n3': isReadonlyWithOnlyDueDate }" diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue index a0f2ec06120..d1866110fd4 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue @@ -279,9 +279,9 @@ export default { class="item-body work-item-link-child gl-relative gl-display-flex gl-flex-grow-1 gl-overflow-break-word gl-min-w-0 gl-pl-3 gl-pr-2 gl-py-2 gl-rounded-base" data-testid="links-child" > - <div class="item-contents gl-display-flex gl-flex-grow-1 gl-flex-wrap-wrap gl-min-w-0"> + <div class="item-contents gl-display-flex gl-flex-grow-1 gl-flex-wrap gl-min-w-0"> <div - class="gl-display-flex gl-flex-grow-1 gl-flex-wrap-wrap flex-xl-nowrap gl-align-items-center gl-justify-content-space-between gl-gap-3 gl-min-w-0" + class="gl-display-flex gl-flex-grow-1 gl-flex-wrap flex-xl-nowrap gl-align-items-center gl-justify-content-space-between gl-gap-3 gl-min-w-0" > <div class="item-title gl-display-flex gl-gap-3 gl-min-w-0"> <span @@ -328,10 +328,7 @@ export default { class="gl-ml-6 ml-xl-0" /> </div> - <div - v-if="labels.length" - class="gl-display-flex gl-flex-wrap-wrap gl-flex-basis-full gl-ml-6" - > + <div v-if="labels.length" class="gl-display-flex gl-flex-wrap gl-flex-basis-full gl-ml-6"> <gl-label v-for="label in labels" :key="label.id" diff --git a/jest.config.base.js b/jest.config.base.js index 08ee2d87a60..8c063e7173f 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -88,6 +88,9 @@ module.exports = (path, options = {}) => { } const TEST_FIXTURES_PATTERN = 'test_fixtures(/.*)$'; + const TEST_FIXTURES_HOME = '/tmp/tests/frontend/fixtures'; + const TEST_FIXTURES_HOME_EE = '/tmp/tests/frontend/fixtures-ee'; + const TEST_FIXTURES_RAW_LOADER_PATTERN = `${TEST_FIXTURES_HOME}.*\\.html$`; const moduleNameMapper = { '^~(/.*)\\?(worker|raw)$': '<rootDir>/app/assets/javascripts$1', @@ -103,7 +106,7 @@ module.exports = (path, options = {}) => { '^any_else_ce(/.*)$': '<rootDir>/app/assets/javascripts$1', '^helpers(/.*)$': '<rootDir>/spec/frontend/__helpers__$1', '^vendor(/.*)$': '<rootDir>/vendor/assets/javascripts$1', - [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures$1', + [TEST_FIXTURES_PATTERN]: `<rootDir>${TEST_FIXTURES_HOME}$1`, '^test_fixtures_static(/.*)$': '<rootDir>/spec/frontend/fixtures/static$1', '\\.(jpg|jpeg|png|svg|css)$': '<rootDir>/spec/frontend/__mocks__/file_mock.js', '\\.svg\\?url$': '<rootDir>/spec/frontend/__mocks__/file_mock.js', @@ -132,7 +135,7 @@ module.exports = (path, options = {}) => { '^ee_else_ce_jest/(.*)$': specDirEE, '^any_else_ce(/.*)$': rootDirEE, '^jh_else_ee(/.*)$': rootDirEE, - [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures-ee$1', + [TEST_FIXTURES_PATTERN]: `<rootDir>${TEST_FIXTURES_HOME_EE}$1`, ...extModuleNameMapperEE, }); @@ -216,7 +219,7 @@ module.exports = (path, options = {}) => { globals, clearMocks: true, testMatch, - moduleFileExtensions: ['js', 'json', 'vue', 'gql', 'graphql', 'yaml', 'yml'], + moduleFileExtensions: ['js', 'json', 'vue', 'gql', 'graphql', 'yaml', 'yml', 'html'], moduleNameMapper, collectCoverageFrom, coverageDirectory: coverageDirectory(), @@ -238,6 +241,7 @@ module.exports = (path, options = {}) => { 'spec/frontend/editor/schema/ci/yaml_tests/.+\\.(yml|yaml)$': './spec/frontend/__helpers__/yaml_transformer.js', '^.+\\.(md|zip|png|yml|yaml|sh|ps1)$': './spec/frontend/__helpers__/raw_transformer.js', + [TEST_FIXTURES_RAW_LOADER_PATTERN]: './spec/frontend/__helpers__/raw_transformer.js', }, transformIgnorePatterns: [`node_modules/(?!(${transformIgnoreNodeModules.join('|')}))`], fakeTimers: { diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb index 702db45554e..c8ee52019a4 100644 --- a/spec/factories/group_members.rb +++ b/spec/factories/group_members.rb @@ -30,6 +30,12 @@ FactoryBot.define do after(:build) { |group_member, _| group_member.user.block! } end + trait :banned do + after(:create) do |member| + create(:namespace_ban, namespace: member.member_namespace.root_ancestor, user: member.user) unless member.owner? + end + end + trait :minimal_access do to_create { |instance| instance.save!(validate: false) } diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb index 57f228650a1..fb62b2ed951 100644 --- a/spec/factories/project_members.rb +++ b/spec/factories/project_members.rb @@ -26,6 +26,12 @@ FactoryBot.define do after(:build) { |project_member, _| project_member.user.block! } end + trait :banned do + after(:create) do |member| + create(:namespace_ban, namespace: member.member_namespace.root_ancestor, user: member.user) unless member.owner? + end + end + trait :awaiting do after(:create) do |member| member.update!(state: ::Member::STATE_AWAITING) diff --git a/spec/frontend/ci/ci_variable_list/ci_variable_list/ci_variable_list_spec.js b/spec/frontend/ci/ci_variable_list/ci_variable_list/ci_variable_list_spec.js index e4abedb412f..8990a70d4ef 100644 --- a/spec/frontend/ci/ci_variable_list/ci_variable_list/ci_variable_list_spec.js +++ b/spec/frontend/ci/ci_variable_list/ci_variable_list/ci_variable_list_spec.js @@ -1,5 +1,7 @@ import $ from 'jquery'; -import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; +import htmlPipelineSchedulesEdit from 'test_fixtures/pipeline_schedules/edit.html'; +import htmlPipelineSchedulesEditWithVariables from 'test_fixtures/pipeline_schedules/edit_with_variables.html'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import VariableList from '~/ci/ci_variable_list/ci_variable_list'; const HIDE_CLASS = 'hide'; @@ -11,7 +13,7 @@ describe('VariableList', () => { describe('with only key/value inputs', () => { describe('with no variables', () => { beforeEach(() => { - loadHTMLFixture('pipeline_schedules/edit.html'); + setHTMLFixture(htmlPipelineSchedulesEdit); $wrapper = $('.js-ci-variable-list-section'); variableList = new VariableList({ @@ -69,7 +71,7 @@ describe('VariableList', () => { describe('with persisted variables', () => { beforeEach(() => { - loadHTMLFixture('pipeline_schedules/edit_with_variables.html'); + setHTMLFixture(htmlPipelineSchedulesEditWithVariables); $wrapper = $('.js-ci-variable-list-section'); variableList = new VariableList({ @@ -106,7 +108,7 @@ describe('VariableList', () => { describe('toggleEnableRow method', () => { beforeEach(() => { - loadHTMLFixture('pipeline_schedules/edit_with_variables.html'); + setHTMLFixture(htmlPipelineSchedulesEditWithVariables); $wrapper = $('.js-ci-variable-list-section'); variableList = new VariableList({ diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap index 54f5ad67d07..047fa04947c 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap +++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap @@ -53,7 +53,7 @@ exports[`PackageTitle renders with tags 1`] = ` </div> <div - class="gl-display-flex gl-flex-wrap-wrap gl-align-items-center gl-mt-3" + class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3" > <div class="gl-display-flex gl-align-items-center gl-mr-5" @@ -150,7 +150,7 @@ exports[`PackageTitle renders without tags 1`] = ` </div> <div - class="gl-display-flex gl-flex-wrap-wrap gl-align-items-center gl-mt-3" + class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3" > <div class="gl-display-flex gl-align-items-center gl-mr-5" diff --git a/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js b/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js index 59faa4484eb..36b14a6451f 100644 --- a/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js +++ b/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js @@ -73,37 +73,67 @@ describe('Super Sidebar Collapsed State Manager', () => { }, ); - describe('focus', () => { - it.each` - collapsed | isUserAction - ${false} | ${true} - ${false} | ${false} - ${true} | ${true} - ${true} | ${false} - `( - 'when collapsed is $collapsed, isUserAction is $isUserAction', - ({ collapsed, isUserAction }) => { - const sidebar = findSidebar(); - const toggle = findToggle(); - jest.spyOn(toggle, 'focus'); - jest.spyOn(sidebar, 'focus'); - toggleSuperSidebarCollapsed(collapsed, false, isUserAction); - - if (isUserAction) { - if (collapsed) { - jest.runAllTimers(); - expect(sidebar.classList).toContain('gl-visibility-hidden'); - expect(toggle.focus).toHaveBeenCalled(); - } else { - expect(sidebar.classList).not.toContain('gl-visibility-hidden'); - expect(sidebar.focus).toHaveBeenCalled(); - } - } else { + describe('toggling the super sidebar', () => { + let sidebar; + let toggle; + + beforeEach(() => { + sidebar = findSidebar(); + toggle = findToggle(); + jest.spyOn(toggle, 'focus'); + jest.spyOn(sidebar, 'focus'); + }); + + afterEach(() => { + sidebar = null; + toggle = null; + }); + + describe('collapsing the sidebar', () => { + const collapse = true; + + describe('on user action', () => { + it('hides the sidebar, then focuses the toggle', () => { + toggleSuperSidebarCollapsed(collapse, false, true); + jest.runAllTimers(); + + expect(sidebar.classList).toContain('gl-visibility-hidden'); + expect(toggle.focus).toHaveBeenCalled(); + }); + }); + + describe('on programmatic toggle', () => { + it('hides the sidebar, but does not focus the toggle', () => { + toggleSuperSidebarCollapsed(collapse, false, false); + jest.runAllTimers(); + + expect(sidebar.classList).toContain('gl-visibility-hidden'); expect(toggle.focus).not.toHaveBeenCalled(); + }); + }); + }); + + describe('expanding the sidebar', () => { + const collapse = false; + + describe('on user action', () => { + it('shows the sidebar, then focuses it', () => { + toggleSuperSidebarCollapsed(collapse, false, true); + + expect(sidebar.classList).not.toContain('gl-visibility-hidden'); + expect(sidebar.focus).toHaveBeenCalled(); + }); + }); + + describe('on programmatic toggle', () => { + it('shows the sidebar, but does not focus it', () => { + toggleSuperSidebarCollapsed(collapse, false, false); + + expect(sidebar.classList).not.toContain('gl-visibility-hidden'); expect(sidebar.focus).not.toHaveBeenCalled(); - } - }, - ); + }); + }); + }); }); }); |