summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS4
-rw-r--r--app/assets/javascripts/super_sidebar/components/super_sidebar.vue8
-rw-r--r--app/assets/javascripts/super_sidebar/constants.js2
-rw-r--r--app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js31
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/title_area.vue2
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_notes_activity_header.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_due_date.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue9
-rw-r--r--jest.config.base.js10
-rw-r--r--spec/factories/group_members.rb6
-rw-r--r--spec/factories/project_members.rb6
-rw-r--r--spec/frontend/ci/ci_variable_list/ci_variable_list/ci_variable_list_spec.js10
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap4
-rw-r--r--spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js88
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();
- }
- },
- );
+ });
+ });
+ });
});
});