Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ad3b511ba3
commit
31522c5182
|
@ -29,27 +29,6 @@ Layout/HashAlignment:
|
||||||
- 'ee/lib/gitlab/ci/parsers/security/formatters/dependency_list.rb'
|
- 'ee/lib/gitlab/ci/parsers/security/formatters/dependency_list.rb'
|
||||||
- 'ee/lib/gitlab/elastic/helper.rb'
|
- 'ee/lib/gitlab/elastic/helper.rb'
|
||||||
- 'ee/lib/gitlab/elastic/indexer.rb'
|
- 'ee/lib/gitlab/elastic/indexer.rb'
|
||||||
- 'ee/spec/controllers/ee/projects/variables_controller_spec.rb'
|
|
||||||
- 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
|
|
||||||
- 'ee/spec/controllers/groups/issues_controller_spec.rb'
|
|
||||||
- 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
|
|
||||||
- 'ee/spec/controllers/trials_controller_spec.rb'
|
|
||||||
- 'ee/spec/factories/dependencies.rb'
|
|
||||||
- 'ee/spec/factories/projects.rb'
|
|
||||||
- 'ee/spec/features/billings/billing_plans_spec.rb'
|
|
||||||
- 'ee/spec/features/groups/settings/protected_environments_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/environments/environments_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
|
|
||||||
- 'ee/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
|
|
||||||
- 'ee/spec/finders/epics_finder_spec.rb'
|
|
||||||
- 'ee/spec/finders/merge_requests_finder_spec.rb'
|
|
||||||
- 'ee/spec/frontend/fixtures/dast_profiles.rb'
|
|
||||||
- 'ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
|
|
||||||
- 'ee/spec/graphql/ee/resolvers/namespace_projects_resolver_spec.rb'
|
|
||||||
- 'ee/spec/graphql/resolvers/path_locks_resolver_spec.rb'
|
|
||||||
- 'ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb'
|
|
||||||
- 'ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb'
|
|
||||||
- 'ee/spec/graphql/resolvers/vulnerabilities/issue_links_resolver_spec.rb'
|
|
||||||
- 'ee/spec/helpers/billing_plans_helper_spec.rb'
|
- 'ee/spec/helpers/billing_plans_helper_spec.rb'
|
||||||
- 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
|
- 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
|
||||||
- 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
|
- 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
import JSZipUtils from 'jszip-utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
export default class SketchLoader {
|
export default class SketchLoader {
|
||||||
|
@ -7,37 +7,30 @@ export default class SketchLoader {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.loadingIcon = this.container.querySelector('.js-loading-icon');
|
this.loadingIcon = this.container.querySelector('.js-loading-icon');
|
||||||
|
|
||||||
this.load();
|
this.load().catch(() => {
|
||||||
}
|
this.error();
|
||||||
|
|
||||||
load() {
|
|
||||||
return this.getZipFile()
|
|
||||||
.then((data) => JSZip.loadAsync(data))
|
|
||||||
.then((asyncResult) => asyncResult.files['previews/preview.png'].async('uint8array'))
|
|
||||||
.then((content) => {
|
|
||||||
const url = window.URL || window.webkitURL;
|
|
||||||
const blob = new Blob([new Uint8Array(content)], {
|
|
||||||
type: 'image/png',
|
|
||||||
});
|
|
||||||
const previewUrl = url.createObjectURL(blob);
|
|
||||||
|
|
||||||
this.render(previewUrl);
|
|
||||||
})
|
|
||||||
.catch(this.error.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
getZipFile() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
JSZipUtils.getBinaryContent(this.container.dataset.endpoint, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
const zipContents = await this.getZipContents();
|
||||||
|
const previewContents = await zipContents.files['previews/preview.png'].async('uint8array');
|
||||||
|
|
||||||
|
const blob = new Blob([previewContents], {
|
||||||
|
type: 'image/png',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render(window.URL.createObjectURL(blob));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getZipContents() {
|
||||||
|
const { data } = await axios.get(this.container.dataset.endpoint, {
|
||||||
|
responseType: 'arraybuffer',
|
||||||
|
});
|
||||||
|
|
||||||
|
return JSZip.loadAsync(data);
|
||||||
|
}
|
||||||
|
|
||||||
render(previewUrl) {
|
render(previewUrl) {
|
||||||
const previewLink = document.createElement('a');
|
const previewLink = document.createElement('a');
|
||||||
const previewImage = document.createElement('img');
|
const previewImage = document.createElement('img');
|
||||||
|
|
|
@ -102,7 +102,7 @@ export default {
|
||||||
data-qa-selector="board_add_new_list"
|
data-qa-selector="board_add_new_list"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
|
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"
|
||||||
>
|
>
|
||||||
<h3 class="gl-font-size-h2 gl-px-5 gl-py-5 gl-m-0" data-testid="board-add-column-form-title">
|
<h3 class="gl-font-size-h2 gl-px-5 gl-py-5 gl-m-0" data-testid="board-add-column-form-title">
|
||||||
{{ $options.i18n.newList }}
|
{{ $options.i18n.newList }}
|
||||||
|
|
|
@ -154,7 +154,7 @@ export default {
|
||||||
:id="glIconId"
|
:id="glIconId"
|
||||||
ref="icon"
|
ref="icon"
|
||||||
name="issue-block"
|
name="issue-block"
|
||||||
class="issue-blocked-icon gl-mr-2 gl-cursor-pointer"
|
class="issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-text-red-500"
|
||||||
data-testid="issue-blocked-icon"
|
data-testid="issue-blocked-icon"
|
||||||
@mouseenter="handleMouseEnter"
|
@mouseenter="handleMouseEnter"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -81,10 +81,10 @@ export default {
|
||||||
data-qa-selector="board_card"
|
data-qa-selector="board_card"
|
||||||
:class="[
|
:class="[
|
||||||
{
|
{
|
||||||
'multi-select': multiSelectVisible,
|
'multi-select gl-bg-blue-50 gl-border-blue-200': multiSelectVisible,
|
||||||
'gl-cursor-grab': isDraggable,
|
'gl-cursor-grab': isDraggable,
|
||||||
'is-disabled': isDisabled,
|
'is-disabled': isDisabled,
|
||||||
'is-active': isActive,
|
'is-active gl-bg-blue-50': isActive,
|
||||||
'gl-cursor-not-allowed gl-bg-gray-10': item.isLoading,
|
'gl-cursor-not-allowed gl-bg-gray-10': item.isLoading,
|
||||||
},
|
},
|
||||||
colorClass,
|
colorClass,
|
||||||
|
@ -95,7 +95,7 @@ export default {
|
||||||
:data-item-path="item.referencePath"
|
:data-item-path="item.referencePath"
|
||||||
:style="cardStyle"
|
:style="cardStyle"
|
||||||
data-testid="board_card"
|
data-testid="board_card"
|
||||||
class="board-card gl-p-5 gl-rounded-base"
|
class="board-card gl-p-5 gl-rounded-base gl-line-height-normal gl-relative gl-mb-3"
|
||||||
@click="toggleIssue($event)"
|
@click="toggleIssue($event)"
|
||||||
>
|
>
|
||||||
<board-card-inner :list="list" :item="item" :update-filters="true" :index="index" />
|
<board-card-inner :list="list" :item="item" :update-filters="true" :index="index" />
|
||||||
|
|
|
@ -208,7 +208,7 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="gl-display-flex" dir="auto">
|
<div class="gl-display-flex" dir="auto">
|
||||||
<h4 class="board-card-title gl-mb-0 gl-mt-0 gl-mr-3">
|
<h4 class="board-card-title gl-mb-0 gl-mt-0 gl-mr-3 gl-font-base gl-overflow-break-word">
|
||||||
<board-blocked-icon
|
<board-blocked-icon
|
||||||
v-if="item.blocked"
|
v-if="item.blocked"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
@ -221,7 +221,7 @@ export default {
|
||||||
v-gl-tooltip
|
v-gl-tooltip
|
||||||
name="eye-slash"
|
name="eye-slash"
|
||||||
:title="__('Confidential')"
|
:title="__('Confidential')"
|
||||||
class="confidential-icon gl-mr-2"
|
class="confidential-icon gl-mr-2 gl-text-orange-500 gl-cursor-help"
|
||||||
:aria-label="__('Confidential')"
|
:aria-label="__('Confidential')"
|
||||||
/>
|
/>
|
||||||
<gl-icon
|
<gl-icon
|
||||||
|
@ -229,14 +229,14 @@ export default {
|
||||||
v-gl-tooltip
|
v-gl-tooltip
|
||||||
name="spam"
|
name="spam"
|
||||||
:title="__('This issue is hidden because its author has been banned')"
|
:title="__('This issue is hidden because its author has been banned')"
|
||||||
class="gl-mr-2 hidden-icon"
|
class="gl-mr-2 hidden-icon gl-text-orange-500 gl-cursor-help"
|
||||||
data-testid="hidden-icon"
|
data-testid="hidden-icon"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
:href="item.path || item.webUrl || ''"
|
:href="item.path || item.webUrl || ''"
|
||||||
:title="item.title"
|
:title="item.title"
|
||||||
:class="{ 'gl-text-gray-400!': item.isLoading }"
|
:class="{ 'gl-text-gray-400!': item.isLoading }"
|
||||||
class="js-no-trigger"
|
class="js-no-trigger gl-text-body gl-hover-text-gray-900"
|
||||||
@mousemove.stop
|
@mousemove.stop
|
||||||
>{{ item.title }}</a
|
>{{ item.title }}</a
|
||||||
>
|
>
|
||||||
|
@ -247,7 +247,7 @@ export default {
|
||||||
<template v-for="label in orderedLabels">
|
<template v-for="label in orderedLabels">
|
||||||
<gl-label
|
<gl-label
|
||||||
:key="label.id"
|
:key="label.id"
|
||||||
class="js-no-trigger"
|
class="js-no-trigger gl-mt-2 gl-mr-2"
|
||||||
:background-color="label.color"
|
:background-color="label.color"
|
||||||
:title="label.title"
|
:title="label.title"
|
||||||
:description="label.description"
|
:description="label.description"
|
||||||
|
@ -267,7 +267,7 @@ export default {
|
||||||
<gl-loading-icon v-if="item.isLoading" size="lg" class="gl-mt-5" />
|
<gl-loading-icon v-if="item.isLoading" size="lg" class="gl-mt-5" />
|
||||||
<span
|
<span
|
||||||
v-if="item.referencePath"
|
v-if="item.referencePath"
|
||||||
class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3"
|
class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3 gl-text-secondary"
|
||||||
:class="{ 'gl-font-base': isEpicBoard }"
|
:class="{ 'gl-font-base': isEpicBoard }"
|
||||||
>
|
>
|
||||||
<tooltip-on-truncate
|
<tooltip-on-truncate
|
||||||
|
@ -328,7 +328,10 @@ export default {
|
||||||
</p>
|
</p>
|
||||||
</gl-tooltip>
|
</gl-tooltip>
|
||||||
|
|
||||||
<span ref="countBadge" class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3">
|
<span
|
||||||
|
ref="countBadge"
|
||||||
|
class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3 gl-text-secondary gl-cursor-help"
|
||||||
|
>
|
||||||
<span v-if="allowSubEpics" class="gl-mr-3">
|
<span v-if="allowSubEpics" class="gl-mr-3">
|
||||||
<gl-icon name="epic" />
|
<gl-icon name="epic" />
|
||||||
{{ totalEpicsCount }}
|
{{ totalEpicsCount }}
|
||||||
|
@ -346,7 +349,7 @@ export default {
|
||||||
<span
|
<span
|
||||||
v-if="shouldRenderEpicProgress"
|
v-if="shouldRenderEpicProgress"
|
||||||
ref="progressBadge"
|
ref="progressBadge"
|
||||||
class="board-card-info gl-pl-0"
|
class="board-card-info gl-pl-0 gl-text-secondary gl-cursor-help"
|
||||||
>
|
>
|
||||||
<span class="gl-mr-3" data-testid="epic-progress">
|
<span class="gl-mr-3" data-testid="epic-progress">
|
||||||
<gl-icon name="progress" />
|
<gl-icon name="progress" />
|
||||||
|
@ -369,7 +372,7 @@ export default {
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="board-card-assignee gl-display-flex gl-gap-3">
|
<div class="board-card-assignee gl-display-flex gl-gap-3 gl-mb-n2">
|
||||||
<user-avatar-link
|
<user-avatar-link
|
||||||
v-for="assignee in cappedAssignees"
|
v-for="assignee in cappedAssignees"
|
||||||
:key="assignee.id"
|
:key="assignee.id"
|
||||||
|
@ -377,7 +380,7 @@ export default {
|
||||||
:img-alt="avatarUrlTitle(assignee)"
|
:img-alt="avatarUrlTitle(assignee)"
|
||||||
:img-src="avatarUrl(assignee)"
|
:img-src="avatarUrl(assignee)"
|
||||||
:img-size="avatarSize"
|
:img-size="avatarSize"
|
||||||
class="js-no-trigger"
|
class="js-no-trigger user-avatar-link"
|
||||||
tooltip-placement="bottom"
|
tooltip-placement="bottom"
|
||||||
:enforce-gl-avatar="true"
|
:enforce-gl-avatar="true"
|
||||||
>
|
>
|
||||||
|
@ -391,7 +394,7 @@ export default {
|
||||||
v-if="shouldRenderCounter"
|
v-if="shouldRenderCounter"
|
||||||
v-gl-tooltip
|
v-gl-tooltip
|
||||||
:title="assigneeCounterTooltip"
|
:title="assigneeCounterTooltip"
|
||||||
class="avatar-counter"
|
class="avatar-counter gl-bg-gray-400 gl-cursor-help gl-font-weight-bold gl-ml-n4 gl-border-0 gl-line-height-24"
|
||||||
data-placement="bottom"
|
data-placement="bottom"
|
||||||
>{{ assigneeCounterLabel }}</span
|
>{{ assigneeCounterLabel }}</span
|
||||||
>
|
>
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default {
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'is-draggable': isListDraggable,
|
'is-draggable': isListDraggable,
|
||||||
'is-collapsed': list.collapsed,
|
'is-collapsed gl-w-10': list.collapsed,
|
||||||
'board-type-assignee': list.listType === 'assignee',
|
'board-type-assignee': list.listType === 'assignee',
|
||||||
}"
|
}"
|
||||||
:data-list-id="list.id"
|
:data-list-id="list.id"
|
||||||
|
@ -84,7 +84,7 @@ export default {
|
||||||
data-qa-selector="board_list"
|
data-qa-selector="board_list"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
|
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"
|
||||||
:class="{ 'board-column-highlighted': highlighted }"
|
:class="{ 'board-column-highlighted': highlighted }"
|
||||||
>
|
>
|
||||||
<board-list-header :list="list" :disabled="disabled" />
|
<board-list-header :list="list" :disabled="disabled" />
|
||||||
|
|
|
@ -75,7 +75,7 @@ export default {
|
||||||
v-if="!isSwimlanesOn"
|
v-if="!isSwimlanesOn"
|
||||||
ref="list"
|
ref="list"
|
||||||
v-bind="draggableOptions"
|
v-bind="draggableOptions"
|
||||||
class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap"
|
class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap gl-overflow-x-scroll"
|
||||||
@end="moveList"
|
@end="moveList"
|
||||||
>
|
>
|
||||||
<board-column
|
<board-column
|
||||||
|
|
|
@ -265,7 +265,7 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-show="!list.collapsed"
|
v-show="!list.collapsed"
|
||||||
class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column"
|
class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column gl-min-h-0"
|
||||||
data-qa-selector="board_list_cards_area"
|
data-qa-selector="board_list_cards_area"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -287,7 +287,7 @@ export default {
|
||||||
:data-board-type="list.listType"
|
:data-board-type="list.listType"
|
||||||
:class="{ 'bg-danger-100': boardItemsSizeExceedsMax }"
|
:class="{ 'bg-danger-100': boardItemsSizeExceedsMax }"
|
||||||
draggable=".board-card"
|
draggable=".board-card"
|
||||||
class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0"
|
class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-y-auto gl-overflow-x-hidden"
|
||||||
data-testid="tree-root-wrapper"
|
data-testid="tree-root-wrapper"
|
||||||
@start="handleDragOnStart"
|
@start="handleDragOnStart"
|
||||||
@end="handleDragOnEnd"
|
@end="handleDragOnEnd"
|
||||||
|
@ -303,7 +303,11 @@ export default {
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
/>
|
/>
|
||||||
<gl-intersection-observer @appear="onReachingListBottom">
|
<gl-intersection-observer @appear="onReachingListBottom">
|
||||||
<li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
|
<li
|
||||||
|
v-if="showCount"
|
||||||
|
class="board-list-count gl-text-center gl-text-secondary gl-py-4"
|
||||||
|
data-issue-id="-1"
|
||||||
|
>
|
||||||
<gl-loading-icon
|
<gl-loading-icon
|
||||||
v-if="loadingMore"
|
v-if="loadingMore"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
@ -252,7 +252,7 @@ export default {
|
||||||
<header
|
<header
|
||||||
:class="{
|
:class="{
|
||||||
'gl-h-full': list.collapsed,
|
'gl-h-full': list.collapsed,
|
||||||
'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader,
|
'board-inner gl-rounded-top-left-base gl-rounded-top-right-base gl-bg-gray-50': isSwimlanesHeader,
|
||||||
}"
|
}"
|
||||||
:style="headerStyle"
|
:style="headerStyle"
|
||||||
class="board-header gl-relative"
|
class="board-header gl-relative"
|
||||||
|
@ -267,14 +267,15 @@ export default {
|
||||||
'gl-py-2': list.collapsed && isSwimlanesHeader,
|
'gl-py-2': list.collapsed && isSwimlanesHeader,
|
||||||
'gl-flex-direction-column': list.collapsed,
|
'gl-flex-direction-column': list.collapsed,
|
||||||
}"
|
}"
|
||||||
class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3"
|
class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 gl-h-9"
|
||||||
>
|
>
|
||||||
<gl-button
|
<gl-button
|
||||||
v-gl-tooltip.hover
|
v-gl-tooltip.hover
|
||||||
:aria-label="chevronTooltip"
|
:aria-label="chevronTooltip"
|
||||||
:title="chevronTooltip"
|
:title="chevronTooltip"
|
||||||
:icon="chevronIcon"
|
:icon="chevronIcon"
|
||||||
class="board-title-caret no-drag gl-cursor-pointer"
|
class="board-title-caret no-drag gl-cursor-pointer gl-hover-bg-gray-50"
|
||||||
|
:class="{ 'gl-mt-1': list.collapsed, 'gl-mr-2': !list.collapsed }"
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
size="small"
|
size="small"
|
||||||
data-testid="board-title-caret"
|
data-testid="board-title-caret"
|
||||||
|
@ -307,6 +308,7 @@ export default {
|
||||||
'gl-display-none': list.collapsed && isSwimlanesHeader,
|
'gl-display-none': list.collapsed && isSwimlanesHeader,
|
||||||
'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed,
|
'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed,
|
||||||
'gl-flex-grow-1': !list.collapsed,
|
'gl-flex-grow-1': !list.collapsed,
|
||||||
|
'gl-rotate-90': list.collapsed,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- EE start -->
|
<!-- EE start -->
|
||||||
|
@ -324,7 +326,7 @@ export default {
|
||||||
<span
|
<span
|
||||||
v-if="listType === 'assignee'"
|
v-if="listType === 'assignee'"
|
||||||
v-show="!list.collapsed"
|
v-show="!list.collapsed"
|
||||||
class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
|
class="gl-ml-2 gl-font-weight-normal gl-text-secondary"
|
||||||
>
|
>
|
||||||
@{{ listAssignee }}
|
@{{ listAssignee }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -345,7 +347,7 @@ export default {
|
||||||
v-if="isSwimlanesHeader && list.collapsed"
|
v-if="isSwimlanesHeader && list.collapsed"
|
||||||
ref="collapsedInfo"
|
ref="collapsedInfo"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500"
|
class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-secondary gl-hover-text-gray-900"
|
||||||
>
|
>
|
||||||
<gl-icon name="information" />
|
<gl-icon name="information" />
|
||||||
</span>
|
</span>
|
||||||
|
@ -369,14 +371,14 @@ export default {
|
||||||
<!-- EE end -->
|
<!-- EE end -->
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="issue-count-badge gl-display-inline-flex gl-pr-2 no-drag gl-text-gray-500"
|
class="issue-count-badge gl-display-inline-flex gl-pr-2 no-drag gl-text-secondary"
|
||||||
data-testid="issue-count-badge"
|
data-testid="issue-count-badge"
|
||||||
:class="{
|
:class="{
|
||||||
'gl-display-none!': list.collapsed && isSwimlanesHeader,
|
'gl-display-none!': list.collapsed && isSwimlanesHeader,
|
||||||
'gl-p-0': list.collapsed,
|
'gl-p-0': list.collapsed,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="gl-display-inline-flex">
|
<span class="gl-display-inline-flex" :class="{ 'gl-rotate-90': list.collapsed }">
|
||||||
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
|
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
|
||||||
<span ref="itemCount" class="gl-display-inline-flex gl-align-items-center">
|
<span ref="itemCount" class="gl-display-inline-flex gl-align-items-center">
|
||||||
<gl-icon class="gl-mr-2" :name="countIcon" :size="16" />
|
<gl-icon class="gl-mr-2" :name="countIcon" :size="16" />
|
||||||
|
|
|
@ -69,7 +69,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="board-new-issue-form">
|
<div class="board-new-issue-form gl-z-index-3 gl-m-3">
|
||||||
<div class="board-card position-relative gl-p-5 rounded">
|
<div class="board-card position-relative gl-p-5 rounded">
|
||||||
<gl-form @submit.prevent="handleFormSubmit" @reset="handleFormCancel">
|
<gl-form @submit.prevent="handleFormSubmit" @reset="handleFormCancel">
|
||||||
<label :for="inputFieldId" class="gl-font-weight-bold">{{ __('Title') }}</label>
|
<label :for="inputFieldId" class="gl-font-weight-bold">{{ __('Title') }}</label>
|
||||||
|
|
|
@ -85,7 +85,11 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
|
<span
|
||||||
|
ref="issueDueDate"
|
||||||
|
:class="cssClass"
|
||||||
|
class="board-card-info gl-mr-3 gl-text-secondary gl-cursor-help"
|
||||||
|
>
|
||||||
<gl-icon
|
<gl-icon
|
||||||
:class="{ 'text-danger': isPastDue }"
|
:class="{ 'text-danger': isPastDue }"
|
||||||
class="board-card-info-icon gl-mr-2"
|
class="board-card-info-icon gl-mr-2"
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span ref="issueTimeEstimate" class="board-card-info card-number">
|
<span ref="issueTimeEstimate" class="board-card-info gl-mr-3 gl-text-secondary gl-cursor-help">
|
||||||
<gl-icon name="hourglass" class="board-card-info-icon gl-mr-2" />
|
<gl-icon name="hourglass" class="board-card-info-icon gl-mr-2" />
|
||||||
<time class="board-card-info-text">{{ timeEstimate }}</time>
|
<time class="board-card-info-text">{{ timeEstimate }}</time>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -30,7 +30,9 @@ export default {
|
||||||
{{ itemsSize }}
|
{{ itemsSize }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="isMaxLimitSet" class="max-issue-size">
|
<span v-if="isMaxLimitSet" class="max-issue-size">
|
||||||
{{ maxIssueCount }}
|
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
|
||||||
|
{{ `/ ${maxIssueCount}` }}
|
||||||
|
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -63,6 +63,12 @@ export default {
|
||||||
|
|
||||||
return btn.tooltipText;
|
return btn.tooltipText;
|
||||||
},
|
},
|
||||||
|
actionButtonQaSelector(btn) {
|
||||||
|
if (btn.dataQaSelector) {
|
||||||
|
return btn.dataQaSelector;
|
||||||
|
}
|
||||||
|
return 'mr_widget_extension_actions_button';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -105,7 +111,7 @@ export default {
|
||||||
:target="btn.target"
|
:target="btn.target"
|
||||||
:class="[{ 'gl-mr-3': index !== tertiaryButtons.length - 1 }, btn.class]"
|
:class="[{ 'gl-mr-3': index !== tertiaryButtons.length - 1 }, btn.class]"
|
||||||
:data-clipboard-text="btn.dataClipboardText"
|
:data-clipboard-text="btn.dataClipboardText"
|
||||||
:data-qa-selector="btn.dataQaSelector"
|
:data-qa-selector="actionButtonQaSelector(btn)"
|
||||||
:data-method="btn.dataMethod"
|
:data-method="btn.dataMethod"
|
||||||
:icon="btn.icon"
|
:icon="btn.icon"
|
||||||
:data-testid="btn.testId || 'extension-actions-button'"
|
:data-testid="btn.testId || 'extension-actions-button'"
|
||||||
|
|
|
@ -307,7 +307,11 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="media-section" data-testid="widget-extension">
|
<section
|
||||||
|
class="media-section"
|
||||||
|
data-testid="widget-extension"
|
||||||
|
data-qa-selector="mr_widget_extension"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
:class="{ 'gl-cursor-pointer': isCollapsible }"
|
:class="{ 'gl-cursor-pointer': isCollapsible }"
|
||||||
class="media gl-p-5"
|
class="media gl-p-5"
|
||||||
|
@ -352,6 +356,7 @@ export default {
|
||||||
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
|
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
data-testid="toggle-button"
|
data-testid="toggle-button"
|
||||||
|
data-qa-selector="toggle_button"
|
||||||
size="small"
|
size="small"
|
||||||
@click="toggleCollapsed"
|
@click="toggleCollapsed"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -109,6 +109,7 @@ export default {
|
||||||
:modal-id="modalId"
|
:modal-id="modalId"
|
||||||
:level="3"
|
:level="3"
|
||||||
data-testid="child-content"
|
data-testid="child-content"
|
||||||
|
data-qa-selector="child_content"
|
||||||
@clickedAction="onClickedAction"
|
@clickedAction="onClickedAction"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -60,6 +60,7 @@ export default {
|
||||||
:name="$options.EXTENSION_ICON_NAMES[iconName]"
|
:name="$options.EXTENSION_ICON_NAMES[iconName]"
|
||||||
:size="size"
|
:size="size"
|
||||||
:aria-label="iconAriaLabel"
|
:aria-label="iconAriaLabel"
|
||||||
|
:data-qa-selector="`status_${iconName}_icon`"
|
||||||
class="gl-display-block"
|
class="gl-display-block"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
||||||
import { __ } from '~/locale';
|
import { sprintf, __ } from '~/locale';
|
||||||
import Poll from '~/lib/utils/poll';
|
import Poll from '~/lib/utils/poll';
|
||||||
import StatusIcon from '../extensions/status_icon.vue';
|
import StatusIcon from '../extensions/status_icon.vue';
|
||||||
import { EXTENSION_ICON_NAMES } from '../../constants';
|
import { EXTENSION_ICON_NAMES } from '../../constants';
|
||||||
|
@ -11,6 +12,10 @@ const FETCH_TYPE_COLLAPSED = 'collapsed';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
StatusIcon,
|
StatusIcon,
|
||||||
|
GlButton,
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
GlTooltip: GlTooltipDirective,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +68,10 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1,
|
validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1,
|
||||||
},
|
},
|
||||||
|
isCollapsible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
widgetName: {
|
widgetName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -70,6 +79,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
isCollapsed: true,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
@ -91,6 +101,12 @@ export default {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
collapseButtonLabel() {
|
||||||
|
return sprintf(this.isCollapsed ? __('Show details') : __('Hide details'));
|
||||||
|
},
|
||||||
|
toggleCollapsed() {
|
||||||
|
this.isCollapsed = !this.isCollapsed;
|
||||||
|
},
|
||||||
fetch(handler, dataType) {
|
fetch(handler, dataType) {
|
||||||
const requests = this.multiPolling ? handler() : [handler];
|
const requests = this.multiPolling ? handler() : [handler];
|
||||||
|
|
||||||
|
@ -145,10 +161,26 @@ export default {
|
||||||
<slot name="summary">{{ isLoading ? loadingText : summary }}</slot>
|
<slot name="summary">{{ isLoading ? loadingText : summary }}</slot>
|
||||||
</div>
|
</div>
|
||||||
<!-- actions will go here -->
|
<!-- actions will go here -->
|
||||||
<!-- toggle button will go here -->
|
<div
|
||||||
|
v-if="isCollapsible"
|
||||||
|
class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6"
|
||||||
|
>
|
||||||
|
<gl-button
|
||||||
|
v-gl-tooltip
|
||||||
|
:title="collapseButtonLabel"
|
||||||
|
:aria-expanded="`${!isCollapsed}`"
|
||||||
|
:aria-label="collapseButtonLabel"
|
||||||
|
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
|
||||||
|
category="tertiary"
|
||||||
|
data-testid="toggle-button"
|
||||||
|
size="small"
|
||||||
|
@click="toggleCollapsed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-if="!isCollapsed"
|
||||||
class="mr-widget-grouped-section gl-relative"
|
class="mr-widget-grouped-section gl-relative"
|
||||||
data-testid="widget-extension-collapsed-section"
|
data-testid="widget-extension-collapsed-section"
|
||||||
>
|
>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return fileNames.join(' ');
|
return fileNames.join(' ').trim();
|
||||||
},
|
},
|
||||||
summary(data) {
|
summary(data) {
|
||||||
if (data.parsingInProgress) {
|
if (data.parsingInProgress) {
|
||||||
|
|
|
@ -5,34 +5,6 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-projects {
|
|
||||||
.dropdown-content {
|
|
||||||
max-height: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-board-dropdown-content {
|
|
||||||
margin: 0;
|
|
||||||
padding: $gl-padding-4 $gl-padding $gl-padding;
|
|
||||||
border-bottom: 0;
|
|
||||||
color: var(--gray-500, $gray-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-page$='epic_boards:index'],
|
|
||||||
[data-page$='epic_boards:show'],
|
|
||||||
.issue-boards-page {
|
|
||||||
.content-wrapper {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-page$='epic_boards:index'],
|
|
||||||
[data-page$='epic_boards:show'] {
|
|
||||||
.filtered-search-wrapper {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.boards-app {
|
.boards-app {
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
transition: width $gl-transition-duration-medium;
|
transition: width $gl-transition-duration-medium;
|
||||||
|
@ -87,33 +59,7 @@
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-title-caret {
|
|
||||||
border-radius: $border-radius-default;
|
|
||||||
line-height: $gl-spacing-scale-5;
|
|
||||||
|
|
||||||
&.btn svg {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--gray-50, $gray-50);
|
|
||||||
transition: background-color 0.1s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.is-collapsed) {
|
|
||||||
.board-title-caret {
|
|
||||||
margin-right: $gl-padding-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-collapsed {
|
&.is-collapsed {
|
||||||
width: 50px;
|
|
||||||
|
|
||||||
.board-title-caret {
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-title-text > span,
|
.board-title-text > span,
|
||||||
.issue-count-badge > span {
|
.issue-count-badge > span {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@ -124,17 +70,11 @@
|
||||||
// rotated element has square dimensions so it won't overlap with its siblings.
|
// rotated element has square dimensions so it won't overlap with its siblings.
|
||||||
margin: calc(50% - 8px) 0;
|
margin: calc(50% - 8px) 0;
|
||||||
|
|
||||||
transform: rotate(90deg);
|
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-inner {
|
|
||||||
font-size: $issue-boards-font-size;
|
|
||||||
background: var(--gray-50, $gray-50);
|
|
||||||
}
|
|
||||||
|
|
||||||
// to highlight columns we have animated pulse of box-shadow
|
// to highlight columns we have animated pulse of box-shadow
|
||||||
// we don't want to actually animate the box-shadow property
|
// we don't want to actually animate the box-shadow property
|
||||||
// because that causes costly repaints. Instead we can add a
|
// because that causes costly repaints. Instead we can add a
|
||||||
|
@ -169,42 +109,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-title {
|
|
||||||
height: 3rem;
|
|
||||||
|
|
||||||
.max-issue-size::before {
|
|
||||||
content: '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-list-component {
|
|
||||||
min-height: 0; // firefox fix
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-list {
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-list-loading {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: (26px / $issue-boards-font-size) * 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-card {
|
.board-card {
|
||||||
background: var(--gray-10, $white);
|
background: var(--gray-10, $white);
|
||||||
box-shadow: 0 1px 2px rgba(var(--black, $black), 0.1);
|
box-shadow: 0 1px 2px rgba(var(--black, $black), 0.1);
|
||||||
line-height: $gl-padding;
|
|
||||||
list-style: none;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:last-child {
|
||||||
margin-bottom: $gl-padding-8;
|
@include gl-mb-0;
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active,
|
|
||||||
&.is-active .board-card-assignee:hover a {
|
|
||||||
background-color: var(--blue-50, $blue-50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.move-to-position {
|
.move-to-position {
|
||||||
|
@ -215,34 +125,6 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.multi-select {
|
|
||||||
border-color: var(--blue-200, $blue-200);
|
|
||||||
background-color: var(--blue-50, $blue-50);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.sortable-chosen {
|
|
||||||
box-shadow: 0 2px 4px 0 rgba($black, 0.16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gl-label {
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.confidential-icon,
|
|
||||||
.hidden-icon {
|
|
||||||
color: var(--orange-500, $orange-500);
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-blocked-icon {
|
|
||||||
color: var(--red-500, $red-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-down(md) {
|
|
||||||
padding: $gl-padding-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-down(sm) {
|
@include media-breakpoint-down(sm) {
|
||||||
.move-to-position {
|
.move-to-position {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
@ -251,36 +133,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-card-title {
|
.board-card-title {
|
||||||
@include overflow-break-word();
|
|
||||||
font-size: 1em;
|
|
||||||
width: 95%;
|
width: 95%;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--gray-900, $gray-900);
|
@include media-breakpoint-down(md) {
|
||||||
}
|
font-size: $gl-font-size-sm;
|
||||||
|
}
|
||||||
@include media-breakpoint-down(md) {
|
|
||||||
font-size: $label-font-size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-card-assignee {
|
.board-card-assignee {
|
||||||
margin-top: -$gl-padding-4;
|
|
||||||
margin-bottom: -$gl-padding-4;
|
|
||||||
|
|
||||||
.avatar-counter {
|
.avatar-counter {
|
||||||
vertical-align: middle;
|
|
||||||
line-height: $gl-padding-24;
|
|
||||||
min-width: $gl-padding-24;
|
min-width: $gl-padding-24;
|
||||||
height: $gl-padding-24;
|
height: $gl-padding-24;
|
||||||
border-radius: $gl-padding-24;
|
border-radius: $gl-padding-24;
|
||||||
background-color: var(--gray-400, $gray-400);
|
|
||||||
font-size: $gl-font-size-xs;
|
font-size: $gl-font-size-xs;
|
||||||
cursor: help;
|
|
||||||
font-weight: $gl-font-weight-bold;
|
|
||||||
margin-left: -$gl-padding-4;
|
|
||||||
border: 0;
|
|
||||||
padding: 0 $gl-padding-4;
|
|
||||||
|
|
||||||
@include media-breakpoint-down(md) {
|
@include media-breakpoint-down(md) {
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
|
@ -290,12 +157,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar-link:not(:only-child) {
|
.user-avatar-link:not(:only-child) {
|
||||||
margin-left: -$gl-padding-4;
|
margin-left: -$gl-padding;
|
||||||
|
|
||||||
&:nth-of-type(1) {
|
&:nth-of-type(1) {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -314,14 +177,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-down(md) {
|
@include media-breakpoint-down(md) {
|
||||||
margin-top: 0;
|
margin-bottom: 0 !important;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-card-number {
|
.board-card-number {
|
||||||
font-size: $gl-font-size-xs;
|
font-size: $gl-font-size-xs;
|
||||||
color: var(--gray-500, $gray-500);
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
font-size: $label-font-size;
|
font-size: $label-font-size;
|
||||||
|
@ -329,74 +190,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-list-count {
|
.board-list-count {
|
||||||
padding: 10px 0;
|
|
||||||
color: var(--gray-500, $gray-500);
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-new-issue-form {
|
|
||||||
z-index: 4;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-sidebar.boards-sidebar {
|
|
||||||
.gutter-toggle {
|
|
||||||
bottom: 15px;
|
|
||||||
width: 22px;
|
|
||||||
padding-left: $gl-padding-32;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 0;
|
|
||||||
margin-top: (-11px / 2);
|
|
||||||
height: $gl-font-size-12;
|
|
||||||
width: $gl-font-size-12;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.issuable-header-text {
|
|
||||||
@include overflow-break-word();
|
|
||||||
padding-right: 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-sidebar.right-sidebar-expanded {
|
|
||||||
&.boards-sidebar-slide-enter-active,
|
|
||||||
&.boards-sidebar-slide-leave-active {
|
|
||||||
transition: width $gl-transition-duration-medium, padding $gl-transition-duration-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.boards-sidebar-slide-enter,
|
|
||||||
&.boards-sidebar-slide-leave-active {
|
|
||||||
width: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-card-info {
|
.board-card-info {
|
||||||
color: var(--gray-500, $gray-500);
|
&.board-card-weight:hover {
|
||||||
white-space: nowrap;
|
color: initial;
|
||||||
margin-right: $gl-padding-8;
|
|
||||||
|
|
||||||
&:not(.board-card-weight) {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.board-card-weight {
|
|
||||||
color: var(--gray-500, $gray-500);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: initial;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-card-info-icon {
|
.board-card-info-icon {
|
||||||
color: var(--gray-500, $gray-500);
|
|
||||||
margin-right: $gl-padding-4;
|
|
||||||
vertical-align: text-top;
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,15 +211,6 @@
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-labels-toggle-wrapper,
|
|
||||||
.board-swimlanes-toggle-wrapper {
|
|
||||||
/**
|
|
||||||
* Make the wrapper the same height as a button so it aligns properly when the
|
|
||||||
* filtered-search-box input element increases in size on Linux smaller breakpoints
|
|
||||||
*/
|
|
||||||
height: $input-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-boards-content:not(.breadcrumbs) {
|
.issue-boards-content:not(.breadcrumbs) {
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
@ -437,7 +230,6 @@
|
||||||
|
|
||||||
.boards-list {
|
.boards-list {
|
||||||
height: calc(100vh - #{$issue-boards-filter-height});
|
height: calc(100vh - #{$issue-boards-filter-height});
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.boards-sidebar {
|
.boards-sidebar {
|
||||||
|
@ -448,15 +240,7 @@
|
||||||
|
|
||||||
.boards-sidebar {
|
.boards-sidebar {
|
||||||
.sidebar-collapsed-icon {
|
.sidebar-collapsed-icon {
|
||||||
display: none;
|
@include gl-display-none;
|
||||||
}
|
|
||||||
|
|
||||||
.gl-drawer-header {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.labels-select-wrapper.is-embedded .labels-select-wrapper.is-embedded {
|
|
||||||
width: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.show.dropdown .dropdown-menu {
|
.show.dropdown .dropdown-menu {
|
||||||
|
@ -464,10 +248,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-header-collapsed-info-icon:hover {
|
|
||||||
color: var(--gray-900, $gray-900);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-card-skeleton {
|
.board-card-skeleton {
|
||||||
height: 110px;
|
height: 110px;
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
|
|
@ -7,9 +7,6 @@ module VerifiesWithEmail
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
include ActionView::Helpers::DateHelper
|
include ActionView::Helpers::DateHelper
|
||||||
|
|
||||||
TOKEN_LENGTH = 6
|
|
||||||
TOKEN_VALID_FOR_MINUTES = 60
|
|
||||||
|
|
||||||
included do
|
included do
|
||||||
prepend_before_action :verify_with_email, only: :create, unless: -> { two_factor_enabled? }
|
prepend_before_action :verify_with_email, only: :create, unless: -> { two_factor_enabled? }
|
||||||
end
|
end
|
||||||
|
@ -76,7 +73,8 @@ module VerifiesWithEmail
|
||||||
def send_verification_instructions(user)
|
def send_verification_instructions(user)
|
||||||
return if send_rate_limited?(user)
|
return if send_rate_limited?(user)
|
||||||
|
|
||||||
raw_token, encrypted_token = generate_token
|
service = Users::EmailVerification::GenerateTokenService.new(attr: :unlock_token)
|
||||||
|
raw_token, encrypted_token = service.execute
|
||||||
user.unlock_token = encrypted_token
|
user.unlock_token = encrypted_token
|
||||||
user.lock_access!({ send_instructions: false })
|
user.lock_access!({ send_instructions: false })
|
||||||
send_verification_instructions_email(user, raw_token)
|
send_verification_instructions_email(user, raw_token)
|
||||||
|
@ -88,27 +86,20 @@ module VerifiesWithEmail
|
||||||
Notify.verification_instructions_email(
|
Notify.verification_instructions_email(
|
||||||
user.id,
|
user.id,
|
||||||
token: token,
|
token: token,
|
||||||
expires_in: TOKEN_VALID_FOR_MINUTES).deliver_later
|
expires_in: Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES).deliver_later
|
||||||
|
|
||||||
log_verification(user, :instructions_sent)
|
log_verification(user, :instructions_sent)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_token(user, token)
|
def verify_token(user, token)
|
||||||
return handle_verification_failure(user, :rate_limited) if verification_rate_limited?(user)
|
service = Users::EmailVerification::ValidateTokenService.new(attr: :unlock_token, user: user, token: token)
|
||||||
return handle_verification_failure(user, :invalid) unless valid_token?(user, token)
|
result = service.execute
|
||||||
return handle_verification_failure(user, :expired) if expired_token?(user)
|
|
||||||
|
|
||||||
handle_verification_success(user)
|
if result[:status] == :success
|
||||||
end
|
handle_verification_success(user)
|
||||||
|
else
|
||||||
def generate_token
|
handle_verification_failure(user, result[:reason], result[:message])
|
||||||
raw_token = SecureRandom.random_number(10**TOKEN_LENGTH).to_s.rjust(TOKEN_LENGTH, '0')
|
end
|
||||||
encrypted_token = digest_token(raw_token)
|
|
||||||
[raw_token, encrypted_token]
|
|
||||||
end
|
|
||||||
|
|
||||||
def digest_token(token)
|
|
||||||
Devise.token_generator.digest(User, :unlock_token, token)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_sign_in_rate_limited
|
def render_sign_in_rate_limited
|
||||||
|
@ -122,44 +113,17 @@ module VerifiesWithEmail
|
||||||
distance_of_time_in_words(interval_in_seconds)
|
distance_of_time_in_words(interval_in_seconds)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verification_rate_limited?(user)
|
|
||||||
Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: user.unlock_token)
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_rate_limited?(user)
|
def send_rate_limited?(user)
|
||||||
Gitlab::ApplicationRateLimiter.throttled?(:email_verification_code_send, scope: user)
|
Gitlab::ApplicationRateLimiter.throttled?(:email_verification_code_send, scope: user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def expired_token?(user)
|
def handle_verification_failure(user, reason, message)
|
||||||
user.locked_at < (Time.current - TOKEN_VALID_FOR_MINUTES.minutes)
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_token?(user, token)
|
|
||||||
user.unlock_token == digest_token(token)
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_verification_failure(user, reason)
|
|
||||||
message = case reason
|
|
||||||
when :rate_limited
|
|
||||||
s_("IdentityVerification|You've reached the maximum amount of tries. "\
|
|
||||||
'Wait %{interval} or resend a new code and try again.') % { interval: email_verification_interval }
|
|
||||||
when :expired
|
|
||||||
s_('IdentityVerification|The code has expired. Resend a new code and try again.')
|
|
||||||
when :invalid
|
|
||||||
s_('IdentityVerification|The code is incorrect. Enter it again, or resend a new code.')
|
|
||||||
end
|
|
||||||
|
|
||||||
user.errors.add(:base, message)
|
user.errors.add(:base, message)
|
||||||
log_verification(user, :failed_attempt, reason)
|
log_verification(user, :failed_attempt, reason)
|
||||||
|
|
||||||
prompt_for_email_verification(user)
|
prompt_for_email_verification(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def email_verification_interval
|
|
||||||
interval_in_seconds = Gitlab::ApplicationRateLimiter.rate_limits[:email_verification][:interval]
|
|
||||||
distance_of_time_in_words(interval_in_seconds)
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_verification_success(user)
|
def handle_verification_success(user)
|
||||||
user.unlock_access!
|
user.unlock_access!
|
||||||
log_verification(user, :successful)
|
log_verification(user, :successful)
|
||||||
|
|
|
@ -5,8 +5,9 @@ module Groups
|
||||||
class RepositoryController < Groups::ApplicationController
|
class RepositoryController < Groups::ApplicationController
|
||||||
layout 'group_settings'
|
layout 'group_settings'
|
||||||
skip_cross_project_access_check :show
|
skip_cross_project_access_check :show
|
||||||
before_action :authorize_create_deploy_token!
|
before_action :authorize_create_deploy_token!, only: :create_deploy_token
|
||||||
before_action :define_deploy_token_variables
|
before_action :authorize_access!, only: :show
|
||||||
|
before_action :define_deploy_token_variables, if: -> { can?(current_user, :create_deploy_token, @group) }
|
||||||
before_action do
|
before_action do
|
||||||
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
|
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
|
||||||
end
|
end
|
||||||
|
@ -43,6 +44,10 @@ module Groups
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def authorize_access!
|
||||||
|
authorize_admin_group!
|
||||||
|
end
|
||||||
|
|
||||||
def define_deploy_token_variables
|
def define_deploy_token_variables
|
||||||
@deploy_tokens = @group.deploy_tokens.active
|
@deploy_tokens = @group.deploy_tokens.active
|
||||||
|
|
||||||
|
@ -55,3 +60,5 @@ module Groups
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Groups::Settings::RepositoryController.prepend_mod
|
||||||
|
|
|
@ -22,8 +22,7 @@ module Ci
|
||||||
accessibility: %w[accessibility],
|
accessibility: %w[accessibility],
|
||||||
coverage: %w[cobertura],
|
coverage: %w[cobertura],
|
||||||
codequality: %w[codequality],
|
codequality: %w[codequality],
|
||||||
terraform: %w[terraform],
|
terraform: %w[terraform]
|
||||||
sbom: %w[cyclonedx]
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
DEFAULT_FILE_NAMES = {
|
DEFAULT_FILE_NAMES = {
|
||||||
|
|
|
@ -34,7 +34,8 @@ class ResourceTimeboxEvent < ResourceEvent
|
||||||
|
|
||||||
case self
|
case self
|
||||||
when ResourceMilestoneEvent
|
when ResourceMilestoneEvent
|
||||||
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user)
|
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user,
|
||||||
|
project: issue.project)
|
||||||
else
|
else
|
||||||
# no-op
|
# no-op
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class UsersStarProject < ApplicationRecord
|
class UsersStarProject < ApplicationRecord
|
||||||
include Sortable
|
include Sortable
|
||||||
|
|
||||||
belongs_to :project, counter_cache: :star_count, touch: true
|
belongs_to :project, counter_cache: :star_count
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
|
|
|
@ -29,7 +29,10 @@ module ResourceEvents
|
||||||
|
|
||||||
resource.expire_note_etag_cache
|
resource.expire_note_etag_cache
|
||||||
|
|
||||||
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user) if resource.is_a?(Issue)
|
return unless resource.is_a?(Issue)
|
||||||
|
|
||||||
|
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user,
|
||||||
|
project: resource.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -495,7 +495,9 @@ module SystemNotes
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_cross_reference_action
|
def track_cross_reference_action
|
||||||
issue_activity_counter.track_issue_cross_referenced_action(author: author) if noteable.is_a?(Issue)
|
return unless noteable.is_a?(Issue)
|
||||||
|
|
||||||
|
issue_activity_counter.track_issue_cross_referenced_action(author: author, project: project || noteable.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def hierarchy_note_params(action, parent, child)
|
def hierarchy_note_params(action, parent, child)
|
||||||
|
|
|
@ -21,7 +21,7 @@ module SystemNotes
|
||||||
|
|
||||||
# Using instance_of because WorkItem < Issue. We don't want to track work item updates as issue updates
|
# Using instance_of because WorkItem < Issue. We don't want to track work item updates as issue updates
|
||||||
if noteable.instance_of?(Issue) && changed_dates.key?('due_date')
|
if noteable.instance_of?(Issue) && changed_dates.key?('due_date')
|
||||||
issue_activity_counter.track_issue_due_date_changed_action(author: author)
|
issue_activity_counter.track_issue_due_date_changed_action(author: author, project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
work_item_activity_counter.track_work_item_date_changed_action(author: author) if noteable.is_a?(WorkItem)
|
work_item_activity_counter.track_work_item_date_changed_action(author: author) if noteable.is_a?(WorkItem)
|
||||||
|
@ -50,7 +50,9 @@ module SystemNotes
|
||||||
"changed time estimate to #{parsed_time}"
|
"changed time estimate to #{parsed_time}"
|
||||||
end
|
end
|
||||||
|
|
||||||
issue_activity_counter.track_issue_time_estimate_changed_action(author: author) if noteable.is_a?(Issue)
|
if noteable.is_a?(Issue)
|
||||||
|
issue_activity_counter.track_issue_time_estimate_changed_action(author: author, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
||||||
end
|
end
|
||||||
|
@ -81,7 +83,9 @@ module SystemNotes
|
||||||
body = text_parts.join(' ')
|
body = text_parts.join(' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
issue_activity_counter.track_issue_time_spent_changed_action(author: author) if noteable.is_a?(Issue)
|
if noteable.is_a?(Issue)
|
||||||
|
issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
||||||
end
|
end
|
||||||
|
@ -107,7 +111,9 @@ module SystemNotes
|
||||||
text_parts << "at #{spent_at}" if spent_at && spent_at != DateTime.current.to_date
|
text_parts << "at #{spent_at}" if spent_at && spent_at != DateTime.current.to_date
|
||||||
body = text_parts.join(' ')
|
body = text_parts.join(' ')
|
||||||
|
|
||||||
issue_activity_counter.track_issue_time_spent_changed_action(author: author) if noteable.is_a?(Issue)
|
if noteable.is_a?(Issue)
|
||||||
|
issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Users
|
||||||
|
module EmailVerification
|
||||||
|
class BaseService
|
||||||
|
VALID_ATTRS = %i[unlock_token confirmation_token].freeze
|
||||||
|
|
||||||
|
def initialize(attr:)
|
||||||
|
@attr = attr
|
||||||
|
|
||||||
|
validate_attr!
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
attr_reader :attr, :token
|
||||||
|
|
||||||
|
def validate_attr!
|
||||||
|
raise ArgumentError, 'Invalid attribute' unless attr.in?(VALID_ATTRS)
|
||||||
|
end
|
||||||
|
|
||||||
|
def digest
|
||||||
|
Devise.token_generator.digest(User, attr, token)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Users
|
||||||
|
module EmailVerification
|
||||||
|
class GenerateTokenService < EmailVerification::BaseService
|
||||||
|
TOKEN_LENGTH = 6
|
||||||
|
|
||||||
|
def execute
|
||||||
|
@token = generate_token
|
||||||
|
|
||||||
|
[token, digest]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def generate_token
|
||||||
|
SecureRandom.random_number(10**TOKEN_LENGTH).to_s.rjust(TOKEN_LENGTH, '0')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,76 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Users
|
||||||
|
module EmailVerification
|
||||||
|
class ValidateTokenService < EmailVerification::BaseService
|
||||||
|
include ActionView::Helpers::DateHelper
|
||||||
|
|
||||||
|
TOKEN_VALID_FOR_MINUTES = 60
|
||||||
|
|
||||||
|
def initialize(attr:, user:, token:)
|
||||||
|
super(attr: attr)
|
||||||
|
|
||||||
|
@user = user
|
||||||
|
@token = token
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
return failure(:rate_limited) if verification_rate_limited?
|
||||||
|
return failure(:invalid) unless valid?
|
||||||
|
return failure(:expired) if expired_token?
|
||||||
|
|
||||||
|
success
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :user
|
||||||
|
|
||||||
|
def verification_rate_limited?
|
||||||
|
Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
Devise.secure_compare(user[attr], digest)
|
||||||
|
end
|
||||||
|
|
||||||
|
def expired_token?
|
||||||
|
generated_at = case attr
|
||||||
|
when :unlock_token then user.locked_at
|
||||||
|
when :confirmation_token then user.confirmation_sent_at
|
||||||
|
end
|
||||||
|
|
||||||
|
generated_at < TOKEN_VALID_FOR_MINUTES.minutes.ago
|
||||||
|
end
|
||||||
|
|
||||||
|
def success
|
||||||
|
{ status: :success }
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure(reason)
|
||||||
|
{
|
||||||
|
status: :failure,
|
||||||
|
reason: reason,
|
||||||
|
message: failure_message(reason)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure_message(reason)
|
||||||
|
case reason
|
||||||
|
when :rate_limited
|
||||||
|
format(s_("IdentityVerification|You've reached the maximum amount of tries. "\
|
||||||
|
'Wait %{interval} or resend a new code and try again.'), interval: email_verification_interval)
|
||||||
|
when :expired
|
||||||
|
s_('IdentityVerification|The code has expired. Resend a new code and try again.')
|
||||||
|
when :invalid
|
||||||
|
s_('IdentityVerification|The code is incorrect. Enter it again, or resend a new code.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_verification_interval
|
||||||
|
interval_in_seconds = Gitlab::ApplicationRateLimiter.rate_limits[:email_verification][:interval]
|
||||||
|
distance_of_time_in_words(interval_in_seconds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,7 +2,11 @@
|
||||||
- page_title _('Repository')
|
- page_title _('Repository')
|
||||||
- @content_class = "limit-container-width" unless fluid_layout
|
- @content_class = "limit-container-width" unless fluid_layout
|
||||||
|
|
||||||
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.')
|
- if can?(current_user, :admin_group, @group)
|
||||||
|
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.')
|
||||||
|
|
||||||
= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
|
= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
|
||||||
= render "default_branch", group: @group
|
= render "default_branch", group: @group
|
||||||
|
|
||||||
|
- if can?(current_user, :change_push_rules, @group)
|
||||||
|
= render "push_rules"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- board = local_assigns.fetch(:board, nil)
|
- board = local_assigns.fetch(:board, nil)
|
||||||
- @no_breadcrumb_container = true
|
- @no_breadcrumb_container = true
|
||||||
- @no_container = true
|
- @no_container = true
|
||||||
- @content_wrapper_class = "#{@content_wrapper_class} gl-relative"
|
- @content_wrapper_class = "#{@content_wrapper_class} gl-relative gl-pb-0"
|
||||||
- @content_class = "issue-boards-content js-focus-mode-board"
|
- @content_class = "issue-boards-content js-focus-mode-board"
|
||||||
- is_epic_board = board.to_type == "EpicBoard"
|
- is_epic_board = board.to_type == "EpicBoard"
|
||||||
- if is_epic_board
|
- if is_epic_board
|
||||||
|
|
|
@ -144,12 +144,6 @@
|
||||||
:why: https://github.com/Stuk/jszip/blob/master/LICENSE.markdown
|
:why: https://github.com/Stuk/jszip/blob/master/LICENSE.markdown
|
||||||
:versions: []
|
:versions: []
|
||||||
:when: 2017-04-05 10:38:46.275721000 Z
|
:when: 2017-04-05 10:38:46.275721000 Z
|
||||||
- - :approve
|
|
||||||
- jszip-utils
|
|
||||||
- :who: Phil Hughes
|
|
||||||
:why: https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown
|
|
||||||
:versions: []
|
|
||||||
:when: 2017-04-05 10:39:32.676232000 Z
|
|
||||||
- - :approve
|
- - :approve
|
||||||
- pako
|
- pako
|
||||||
- :who: Phil Hughes
|
- :who: Phil Hughes
|
||||||
|
|
|
@ -8,10 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
|
|
||||||
From October 19, 2022, namespaces in GitLab.com on the Free tier
|
From October 19, 2022, namespaces in GitLab.com on the Free tier
|
||||||
will be limited to five (5) members per [namespace](namespace/index.md).
|
will be limited to five (5) members per [namespace](namespace/index.md).
|
||||||
This limit applies to top-level groups and personal namespaces.
|
This limit applies to top-level private groups.
|
||||||
|
|
||||||
In a personal namespace, the limit applies across all projects in your personal
|
|
||||||
namespace.
|
|
||||||
|
|
||||||
On the transition date, if your namespace has six or more unique members:
|
On the transition date, if your namespace has six or more unique members:
|
||||||
|
|
||||||
|
@ -45,12 +42,6 @@ Prerequisite:
|
||||||
1. To view all members, select the **Seats** tab.
|
1. To view all members, select the **Seats** tab.
|
||||||
1. To remove a member, select **Remove user**.
|
1. To remove a member, select **Remove user**.
|
||||||
|
|
||||||
NOTE:
|
|
||||||
The **Usage Quotas** page is not available for personal namespaces. You can
|
|
||||||
view and [remove members](project/members/index.md#remove-a-member-from-a-project)
|
|
||||||
in each project instead. The five user limit includes all
|
|
||||||
unique members across all projects in your personal namespace.
|
|
||||||
|
|
||||||
If you need more time to manage your members, or to try GitLab features
|
If you need more time to manage your members, or to try GitLab features
|
||||||
with a team of more than five members, you can [start a trial](https://about.gitlab.com/free-trial/).
|
with a team of more than five members, you can [start a trial](https://about.gitlab.com/free-trial/).
|
||||||
A trial lasts for 30 days and includes an unlimited number of members.
|
A trial lasts for 30 days and includes an unlimited number of members.
|
||||||
|
|
|
@ -74,7 +74,6 @@ Slack user on GitLab.com.
|
||||||
|
|
||||||
The only difference with the [manually configurable Slack slash commands](slack_slash_commands.md)
|
The only difference with the [manually configurable Slack slash commands](slack_slash_commands.md)
|
||||||
is that all the commands should be prefixed with the `/gitlab` keyword.
|
is that all the commands should be prefixed with the `/gitlab` keyword.
|
||||||
We are working on making this configurable in the future.
|
|
||||||
|
|
||||||
For example, to show the issue number `1001` under the `gitlab-org/gitlab`
|
For example, to show the issue number `1001` under the `gitlab-org/gitlab`
|
||||||
project, you would do:
|
project, you would do:
|
||||||
|
|
|
@ -42,7 +42,9 @@ After the Harbor integration is activated:
|
||||||
- The global variables `$HARBOR_USERNAME`, `$HARBOR_HOST`, `$HARBOR_OCI`, `$HARBOR_PASSWORD`, `$HARBOR_URL`, and `$HARBOR_PROJECT` are created for CI/CD use.
|
- The global variables `$HARBOR_USERNAME`, `$HARBOR_HOST`, `$HARBOR_OCI`, `$HARBOR_PASSWORD`, `$HARBOR_URL`, and `$HARBOR_PROJECT` are created for CI/CD use.
|
||||||
- The project-level integration settings override the group-level integration settings.
|
- The project-level integration settings override the group-level integration settings.
|
||||||
|
|
||||||
## Secure your requests to the Harbor APIs
|
## Security considerations
|
||||||
|
|
||||||
|
### Secure your requests to the Harbor APIs
|
||||||
|
|
||||||
For each API request through the Harbor integration, the credentials for your connection to the Harbor API use
|
For each API request through the Harbor integration, the credentials for your connection to the Harbor API use
|
||||||
the `username:password` combination. The following are suggestions for safe use:
|
the `username:password` combination. The following are suggestions for safe use:
|
||||||
|
@ -51,6 +53,12 @@ the `username:password` combination. The following are suggestions for safe use:
|
||||||
- Follow the principle of least privilege (for access on Harbor) with your credentials.
|
- Follow the principle of least privilege (for access on Harbor) with your credentials.
|
||||||
- Have a rotation policy on your credentials.
|
- Have a rotation policy on your credentials.
|
||||||
|
|
||||||
|
### CI/CD variable security
|
||||||
|
|
||||||
|
Malicious code pushed to your `.gitlab-ci.yml` file could compromise your variables, including
|
||||||
|
`$HARBOR_PASSWORD`, and send them to a third-party server. For more details, see
|
||||||
|
[CI/CD variable security](../../../ci/variables/index.md#cicd-variable-security).
|
||||||
|
|
||||||
## Examples of Harbor variables in CI/CD
|
## Examples of Harbor variables in CI/CD
|
||||||
|
|
||||||
### Push a Docker image with kaniko
|
### Push a Docker image with kaniko
|
||||||
|
|
|
@ -27,6 +27,29 @@ included_attributes:
|
||||||
- :name
|
- :name
|
||||||
namespace_settings:
|
namespace_settings:
|
||||||
- :prevent_sharing_groups_outside_hierarchy
|
- :prevent_sharing_groups_outside_hierarchy
|
||||||
|
iterations_cadence: &iterations_cadence_definition
|
||||||
|
- :group_id
|
||||||
|
- :created_at
|
||||||
|
- :updated_at
|
||||||
|
- :start_date
|
||||||
|
- :last_run_date
|
||||||
|
- :duration_in_weeks
|
||||||
|
- :iterations_in_advance
|
||||||
|
- :active
|
||||||
|
- :automatic
|
||||||
|
- :roll_over
|
||||||
|
- :title
|
||||||
|
- :description
|
||||||
|
iterations_cadences: *iterations_cadence_definition
|
||||||
|
iteration: &iteration_definition
|
||||||
|
- :iid
|
||||||
|
- :created_at
|
||||||
|
- :updated_at
|
||||||
|
- :start_date
|
||||||
|
- :due_date
|
||||||
|
- :group_id
|
||||||
|
- :description
|
||||||
|
iterations: *iteration_definition
|
||||||
|
|
||||||
excluded_attributes:
|
excluded_attributes:
|
||||||
group:
|
group:
|
||||||
|
@ -44,6 +67,19 @@ excluded_attributes:
|
||||||
- :max_pages_size
|
- :max_pages_size
|
||||||
epics:
|
epics:
|
||||||
- :state_id
|
- :state_id
|
||||||
|
iterations_cadence: &iterations_cadence_definition
|
||||||
|
- :id
|
||||||
|
iterations_cadences: *iterations_cadence_definition
|
||||||
|
iteration: &iteration_excluded_definition
|
||||||
|
- :id
|
||||||
|
- :title
|
||||||
|
- :title_html
|
||||||
|
- :project_id
|
||||||
|
- :description_html
|
||||||
|
- :cached_markdown_version
|
||||||
|
- :iterations_cadence_id
|
||||||
|
- :sequence
|
||||||
|
iterations: *iteration_excluded_definition
|
||||||
|
|
||||||
methods:
|
methods:
|
||||||
labels:
|
labels:
|
||||||
|
@ -92,3 +128,5 @@ ee:
|
||||||
- milestone:
|
- milestone:
|
||||||
- events:
|
- events:
|
||||||
- :push_event_payload
|
- :push_event_payload
|
||||||
|
- iterations_cadences:
|
||||||
|
- :iterations
|
||||||
|
|
|
@ -24,6 +24,29 @@ included_attributes:
|
||||||
- :username
|
- :username
|
||||||
author:
|
author:
|
||||||
- :name
|
- :name
|
||||||
|
iterations_cadence: &iterations_cadence_definition
|
||||||
|
- :group_id
|
||||||
|
- :created_at
|
||||||
|
- :updated_at
|
||||||
|
- :start_date
|
||||||
|
- :last_run_date
|
||||||
|
- :duration_in_weeks
|
||||||
|
- :iterations_in_advance
|
||||||
|
- :active
|
||||||
|
- :automatic
|
||||||
|
- :roll_over
|
||||||
|
- :title
|
||||||
|
- :description
|
||||||
|
iterations_cadences: *iterations_cadence_definition
|
||||||
|
iteration: &iteration_definition
|
||||||
|
- :iid
|
||||||
|
- :created_at
|
||||||
|
- :updated_at
|
||||||
|
- :start_date
|
||||||
|
- :due_date
|
||||||
|
- :group_id
|
||||||
|
- :description
|
||||||
|
iterations: *iteration_definition
|
||||||
|
|
||||||
excluded_attributes:
|
excluded_attributes:
|
||||||
group:
|
group:
|
||||||
|
@ -41,6 +64,18 @@ excluded_attributes:
|
||||||
- :extra_shared_runners_minutes_limit
|
- :extra_shared_runners_minutes_limit
|
||||||
epics:
|
epics:
|
||||||
- :state_id
|
- :state_id
|
||||||
|
iterations_cadence: &iterations_cadence_definition
|
||||||
|
- :id
|
||||||
|
iterations_cadences: *iterations_cadence_definition
|
||||||
|
iteration: &iteration_excluded_definition
|
||||||
|
- :id
|
||||||
|
- :title
|
||||||
|
- :title_html
|
||||||
|
- :project_id
|
||||||
|
- :description_html
|
||||||
|
- :cached_markdown_version
|
||||||
|
- :iterations_cadence_id
|
||||||
|
iterations: *iteration_excluded_definition
|
||||||
|
|
||||||
methods:
|
methods:
|
||||||
labels:
|
labels:
|
||||||
|
@ -88,3 +123,5 @@ ee:
|
||||||
- milestone:
|
- milestone:
|
||||||
- events:
|
- events:
|
||||||
- :push_event_payload
|
- :push_event_payload
|
||||||
|
- iterations_cadences:
|
||||||
|
- :iterations
|
||||||
|
|
|
@ -8,7 +8,8 @@ module Gitlab
|
||||||
labels: :group_labels,
|
labels: :group_labels,
|
||||||
priorities: :label_priorities,
|
priorities: :label_priorities,
|
||||||
label: :group_label,
|
label: :group_label,
|
||||||
parent: :epic
|
parent: :epic,
|
||||||
|
iterations_cadences: 'Iterations::Cadence'
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
EXISTING_OBJECT_RELATIONS = %i[
|
EXISTING_OBJECT_RELATIONS = %i[
|
||||||
|
|
|
@ -68,15 +68,18 @@ module Gitlab
|
||||||
track_unique_action(ISSUE_REOPENED, author)
|
track_unique_action(ISSUE_REOPENED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_label_changed_action(author:)
|
def track_issue_label_changed_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_LABEL_CHANGED, author, project)
|
||||||
track_unique_action(ISSUE_LABEL_CHANGED, author)
|
track_unique_action(ISSUE_LABEL_CHANGED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_milestone_changed_action(author:)
|
def track_issue_milestone_changed_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_MILESTONE_CHANGED, author, project)
|
||||||
track_unique_action(ISSUE_MILESTONE_CHANGED, author)
|
track_unique_action(ISSUE_MILESTONE_CHANGED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_cross_referenced_action(author:)
|
def track_issue_cross_referenced_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_CROSS_REFERENCED, author, project)
|
||||||
track_unique_action(ISSUE_CROSS_REFERENCED, author)
|
track_unique_action(ISSUE_CROSS_REFERENCED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -116,15 +119,18 @@ module Gitlab
|
||||||
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
|
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_due_date_changed_action(author:)
|
def track_issue_due_date_changed_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_DUE_DATE_CHANGED, author, project)
|
||||||
track_unique_action(ISSUE_DUE_DATE_CHANGED, author)
|
track_unique_action(ISSUE_DUE_DATE_CHANGED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_time_estimate_changed_action(author:)
|
def track_issue_time_estimate_changed_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_TIME_ESTIMATE_CHANGED, author, project)
|
||||||
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author)
|
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_issue_time_spent_changed_action(author:)
|
def track_issue_time_spent_changed_action(author:, project:)
|
||||||
|
track_snowplow_action(ISSUE_TIME_SPENT_CHANGED, author, project)
|
||||||
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author)
|
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,23 @@ module Sidebars
|
||||||
class SettingsMenu < ::Sidebars::Menu
|
class SettingsMenu < ::Sidebars::Menu
|
||||||
override :configure_menu_items
|
override :configure_menu_items
|
||||||
def configure_menu_items
|
def configure_menu_items
|
||||||
return false unless can?(context.current_user, :admin_group, context.group)
|
if can?(context.current_user, :admin_group, context.group)
|
||||||
|
add_item(general_menu_item)
|
||||||
|
add_item(integrations_menu_item)
|
||||||
|
add_item(access_tokens_menu_item)
|
||||||
|
add_item(group_projects_menu_item)
|
||||||
|
add_item(repository_menu_item)
|
||||||
|
add_item(ci_cd_menu_item)
|
||||||
|
add_item(applications_menu_item)
|
||||||
|
add_item(packages_and_registries_menu_item)
|
||||||
|
return true
|
||||||
|
elsif Gitlab.ee? && can?(context.current_user, :change_push_rules, context.group)
|
||||||
|
# Push Rules are the only group setting that can also be edited by maintainers.
|
||||||
|
# Create an empty sub-menu here and EE adds Repository menu item (with only Push Rules).
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
add_item(general_menu_item)
|
false
|
||||||
add_item(integrations_menu_item)
|
|
||||||
add_item(access_tokens_menu_item)
|
|
||||||
add_item(group_projects_menu_item)
|
|
||||||
add_item(repository_menu_item)
|
|
||||||
add_item(ci_cd_menu_item)
|
|
||||||
add_item(applications_menu_item)
|
|
||||||
add_item(packages_and_registries_menu_item)
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
override :title
|
override :title
|
||||||
|
|
|
@ -31980,9 +31980,6 @@ msgstr ""
|
||||||
msgid "Push project from command line"
|
msgid "Push project from command line"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Push rules"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Push the target branch up to GitLab."
|
msgid "Push the target branch up to GitLab."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,6 @@
|
||||||
"js-cookie": "^3.0.0",
|
"js-cookie": "^3.0.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"jszip": "^3.1.3",
|
"jszip": "^3.1.3",
|
||||||
"jszip-utils": "^0.0.2",
|
|
||||||
"katex": "^0.13.2",
|
"katex": "^0.13.2",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"lowlight": "^2.6.1",
|
"lowlight": "^2.6.1",
|
||||||
|
|
|
@ -14,6 +14,7 @@ RSpec.describe 'Group navbar' do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
insert_package_nav(_('Kubernetes'))
|
insert_package_nav(_('Kubernetes'))
|
||||||
|
insert_after_nav_item(_('Analytics'), new_nav_item: settings_for_maintainer_nav_item) if Gitlab.ee?
|
||||||
|
|
||||||
stub_config(dependency_proxy: { enabled: false })
|
stub_config(dependency_proxy: { enabled: false })
|
||||||
stub_config(registry: { enabled: false })
|
stub_config(registry: { enabled: false })
|
||||||
|
|
|
@ -150,7 +150,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
||||||
code = expect_instructions_email_and_extract_code
|
code = expect_instructions_email_and_extract_code
|
||||||
|
|
||||||
# Wait for the code to expire before verifying
|
# Wait for the code to expire before verifying
|
||||||
travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
|
travel Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
|
||||||
verify_code(code)
|
verify_code(code)
|
||||||
|
|
||||||
# Expect an error message
|
# Expect an error message
|
||||||
|
@ -268,7 +268,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
||||||
verify_code(code)
|
verify_code(code)
|
||||||
expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
|
expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
|
||||||
|
|
||||||
travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
|
travel Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
|
||||||
|
|
||||||
verify_code(new_code)
|
verify_code(new_code)
|
||||||
expect(page).to have_content('The code has expired. Resend a new code and try again.')
|
expect(page).to have_content('The code has expired. Resend a new code and try again.')
|
||||||
|
@ -335,7 +335,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
||||||
mail = find_email_for(user)
|
mail = find_email_for(user)
|
||||||
expect(mail.to).to match_array([user.email])
|
expect(mail.to).to match_array([user.email])
|
||||||
expect(mail.subject).to eq('Verify your identity')
|
expect(mail.subject).to eq('Verify your identity')
|
||||||
code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/o]
|
code = mail.body.parts.first.to_s[/\d{#{Users::EmailVerification::GenerateTokenService::TOKEN_LENGTH}}/o]
|
||||||
reset_delivered_emails!
|
reset_delivered_emails!
|
||||||
code
|
code
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,18 +2,6 @@ import SketchLoader from '~/blob/sketch';
|
||||||
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||||
import waitForPromises from 'helpers/wait_for_promises';
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
|
|
||||||
jest.mock('jszip', () => {
|
|
||||||
return {
|
|
||||||
loadAsync: jest.fn().mockResolvedValue({
|
|
||||||
files: {
|
|
||||||
'previews/preview.png': {
|
|
||||||
async: jest.fn().mockResolvedValue('foo'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Sketch viewer', () => {
|
describe('Sketch viewer', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loadHTMLFixture('static/sketch_viewer.html');
|
loadHTMLFixture('static/sketch_viewer.html');
|
||||||
|
@ -25,7 +13,7 @@ describe('Sketch viewer', () => {
|
||||||
|
|
||||||
describe('with error message', () => {
|
describe('with error message', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(SketchLoader.prototype, 'getZipFile').mockImplementation(
|
jest.spyOn(SketchLoader.prototype, 'getZipContents').mockImplementation(
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
reject();
|
reject();
|
||||||
|
@ -50,7 +38,13 @@ describe('Sketch viewer', () => {
|
||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(SketchLoader.prototype, 'getZipFile').mockResolvedValue();
|
jest.spyOn(SketchLoader.prototype, 'getZipContents').mockResolvedValue({
|
||||||
|
files: {
|
||||||
|
'previews/preview.png': {
|
||||||
|
async: jest.fn().mockResolvedValue('foo'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new SketchLoader(document.getElementById('js-sketch-viewer'));
|
new SketchLoader(document.getElementById('js-sketch-viewer'));
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`BoardBlockedIcon on mouseenter on blocked icon with more than three blocking issues matches the snapshot 1`] = `
|
exports[`BoardBlockedIcon on mouseenter on blocked icon with more than three blocking issues matches the snapshot 1`] = `
|
||||||
"<div class=\\"gl-display-inline\\"><svg data-testid=\\"issue-blocked-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-icon s16\\" id=\\"blocked-icon-uniqueId\\">
|
"<div class=\\"gl-display-inline\\"><svg data-testid=\\"issue-blocked-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-text-red-500 gl-icon s16\\" id=\\"blocked-icon-uniqueId\\">
|
||||||
<use href=\\"#issue-block\\"></use>
|
<use href=\\"#issue-block\\"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class=\\"gl-popover\\">
|
<div class=\\"gl-popover\\">
|
||||||
|
|
|
@ -50,7 +50,7 @@ describe('IssueCount', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains maxIssueCount in the template', () => {
|
it('contains maxIssueCount in the template', () => {
|
||||||
expect(vm.find('.max-issue-size').text()).toEqual(String(maxIssueCount));
|
expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
|
it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
|
||||||
|
@ -75,7 +75,7 @@ describe('IssueCount', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains maxIssueCount in the template', () => {
|
it('contains maxIssueCount in the template', () => {
|
||||||
expect(vm.find('.max-issue-size').text()).toEqual(String(maxIssueCount));
|
expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has text-danger class', () => {
|
it('has text-danger class', () => {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 2 },
|
||||||
|
"suites": [
|
||||||
|
{
|
||||||
|
"name": "rspec:pg",
|
||||||
|
"summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2 },
|
||||||
|
"new_failures": [
|
||||||
|
{
|
||||||
|
"result": "failure",
|
||||||
|
"name": "Test#sum when a is 1 and b is 2 returns summary",
|
||||||
|
"file": null,
|
||||||
|
"execution_time": 0.009411,
|
||||||
|
"system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"result": "failure",
|
||||||
|
"name": "Test#sum when a is 100 and b is 200 returns summary",
|
||||||
|
"file": null,
|
||||||
|
"execution_time": 0.000162,
|
||||||
|
"system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolved_failures": [],
|
||||||
|
"existing_failures": [],
|
||||||
|
"new_errors": [],
|
||||||
|
"resolved_errors": [],
|
||||||
|
"existing_errors": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "java ant",
|
||||||
|
"summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
|
||||||
|
"new_failures": [],
|
||||||
|
"resolved_failures": [],
|
||||||
|
"existing_failures": [],
|
||||||
|
"new_errors": [],
|
||||||
|
"resolved_errors": [],
|
||||||
|
"existing_errors": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -9,10 +9,13 @@ describe('MR Widget', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
const findStatusIcon = () => wrapper.findComponent(StatusIcon);
|
const findStatusIcon = () => wrapper.findComponent(StatusIcon);
|
||||||
|
const findExpandedSection = () => wrapper.findByTestId('widget-extension-collapsed-section');
|
||||||
|
const findToggleButton = () => wrapper.findByTestId('toggle-button');
|
||||||
|
|
||||||
const createComponent = ({ propsData, slots } = {}) => {
|
const createComponent = ({ propsData, slots } = {}) => {
|
||||||
wrapper = shallowMountExtended(Widget, {
|
wrapper = shallowMountExtended(Widget, {
|
||||||
propsData: {
|
propsData: {
|
||||||
|
isCollapsible: false,
|
||||||
loadingText: 'Loading widget',
|
loadingText: 'Loading widget',
|
||||||
widgetName: 'MyWidget',
|
widgetName: 'MyWidget',
|
||||||
value: {
|
value: {
|
||||||
|
@ -111,7 +114,7 @@ describe('MR Widget', () => {
|
||||||
jest.spyOn(Sentry, 'captureException').mockImplementation();
|
jest.spyOn(Sentry, 'captureException').mockImplementation();
|
||||||
createComponent({
|
createComponent({
|
||||||
propsData: {
|
propsData: {
|
||||||
fetchCollapsedData: async () => Promise.reject(error),
|
fetchCollapsedData: () => Promise.reject(error),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
@ -125,7 +128,7 @@ describe('MR Widget', () => {
|
||||||
createComponent({
|
createComponent({
|
||||||
propsData: {
|
propsData: {
|
||||||
summary: 'Hello world',
|
summary: 'Hello world',
|
||||||
fetchCollapsedData: async () => Promise.resolve(),
|
fetchCollapsedData: () => Promise.resolve(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,7 +140,7 @@ describe('MR Widget', () => {
|
||||||
it('displays the summary slot when provided', () => {
|
it('displays the summary slot when provided', () => {
|
||||||
createComponent({
|
createComponent({
|
||||||
propsData: {
|
propsData: {
|
||||||
fetchCollapsedData: async () => Promise.resolve(),
|
fetchCollapsedData: () => Promise.resolve(),
|
||||||
},
|
},
|
||||||
slots: {
|
slots: {
|
||||||
summary: '<b>More complex summary</b>',
|
summary: '<b>More complex summary</b>',
|
||||||
|
@ -148,20 +151,35 @@ describe('MR Widget', () => {
|
||||||
'More complex summary',
|
'More complex summary',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('displays the content slot when provided', () => {
|
describe('handle collapse toggle', () => {
|
||||||
|
it('does not display the content slot until toggle is clicked', async () => {
|
||||||
createComponent({
|
createComponent({
|
||||||
propsData: {
|
propsData: {
|
||||||
fetchCollapsedData: async () => Promise.resolve(),
|
isCollapsible: true,
|
||||||
|
fetchCollapsedData: () => Promise.resolve(),
|
||||||
},
|
},
|
||||||
slots: {
|
slots: {
|
||||||
content: '<b>More complex content</b>',
|
content: '<b>More complex content</b>',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.findByTestId('widget-extension-collapsed-section').text()).toBe(
|
expect(findExpandedSection().exists()).toBe(false);
|
||||||
'More complex content',
|
findToggleButton().vm.$emit('click');
|
||||||
);
|
await nextTick();
|
||||||
|
expect(findExpandedSection().text()).toBe('More complex content');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not display the toggle button if isCollapsible is false', () => {
|
||||||
|
createComponent({
|
||||||
|
propsData: {
|
||||||
|
isCollapsible: false,
|
||||||
|
fetchCollapsedData: () => Promise.resolve(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(findToggleButton().exists()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { failedReport } from 'jest/reports/mock_data/mock_data';
|
||||||
import mixedResultsTestReports from 'jest/reports/mock_data/new_and_fixed_failures_report.json';
|
import mixedResultsTestReports from 'jest/reports/mock_data/new_and_fixed_failures_report.json';
|
||||||
import newErrorsTestReports from 'jest/reports/mock_data/new_errors_report.json';
|
import newErrorsTestReports from 'jest/reports/mock_data/new_errors_report.json';
|
||||||
import newFailedTestReports from 'jest/reports/mock_data/new_failures_report.json';
|
import newFailedTestReports from 'jest/reports/mock_data/new_failures_report.json';
|
||||||
|
import newFailedTestWithNullFilesReport from 'jest/reports/mock_data/new_failures_with_null_files_report.json';
|
||||||
import successTestReports from 'jest/reports/mock_data/no_failures_report.json';
|
import successTestReports from 'jest/reports/mock_data/no_failures_report.json';
|
||||||
import resolvedFailures from 'jest/reports/mock_data/resolved_failures.json';
|
import resolvedFailures from 'jest/reports/mock_data/resolved_failures.json';
|
||||||
import recentFailures from 'jest/reports/mock_data/recent_failures_report.json';
|
import recentFailures from 'jest/reports/mock_data/recent_failures_report.json';
|
||||||
|
@ -157,6 +158,15 @@ describe('Test report extension', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hides copy failed tests button when endpoint returns null files', async () => {
|
||||||
|
mockApi(httpStatusCodes.OK, newFailedTestWithNullFilesReport);
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
await waitForPromises();
|
||||||
|
|
||||||
|
expect(findCopyFailedSpecsBtn().exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('copy failed tests button updates tooltip text when clicked', async () => {
|
it('copy failed tests button updates tooltip text when clicked', async () => {
|
||||||
mockApi(httpStatusCodes.OK, newFailedTestReports);
|
mockApi(httpStatusCodes.OK, newFailedTestReports);
|
||||||
createComponent();
|
createComponent();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe API::Helpers::Pagination do
|
RSpec.describe API::Helpers::Pagination do
|
||||||
subject { Class.new.include(described_class).new }
|
subject { Class.new.include(described_class).new }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Backup::DatabaseBackupError do
|
RSpec.describe Backup::DatabaseBackupError do
|
||||||
let(:config) do
|
let(:config) do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Backup::Task do
|
RSpec.describe Backup::Task do
|
||||||
let(:progress) { StringIO.new }
|
let(:progress) { StringIO.new }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Banzai::ColorParser do
|
RSpec.describe Banzai::ColorParser do
|
||||||
describe '.parse' do
|
describe '.parse' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Banzai::Filter::OutputSafety do
|
RSpec.describe Banzai::Filter::OutputSafety do
|
||||||
subject do
|
subject do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Banzai::FilterArray do
|
RSpec.describe Banzai::FilterArray do
|
||||||
describe '#insert_after' do
|
describe '#insert_after' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Banzai::Pipeline do
|
RSpec.describe Banzai::Pipeline do
|
||||||
describe '.[]' do
|
describe '.[]' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Banzai::Querying do
|
RSpec.describe Banzai::Querying do
|
||||||
describe '.css' do
|
describe '.css' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
# Emulates paginator. It returns 2 pages with results
|
# Emulates paginator. It returns 2 pages with results
|
||||||
class TestPaginator
|
class TestPaginator
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Page do
|
RSpec.describe Bitbucket::Page do
|
||||||
let(:response) { { 'values' => [{ 'username' => 'Ben' }], 'pagelen' => 2, 'next' => '' } }
|
let(:response) { { 'values' => [{ 'username' => 'Ben' }], 'pagelen' => 2, 'next' => '' } }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Paginator do
|
RSpec.describe Bitbucket::Paginator do
|
||||||
let(:last_page) { double(:page, next?: false, items: ['item_2']) }
|
let(:last_page) { double(:page, next?: false, items: ['item_2']) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::Comment do
|
RSpec.describe Bitbucket::Representation::Comment do
|
||||||
describe '#author' do
|
describe '#author' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::Issue do
|
RSpec.describe Bitbucket::Representation::Issue do
|
||||||
describe '#iid' do
|
describe '#iid' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::PullRequestComment do
|
RSpec.describe Bitbucket::Representation::PullRequestComment do
|
||||||
describe '#iid' do
|
describe '#iid' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::PullRequest do
|
RSpec.describe Bitbucket::Representation::PullRequest do
|
||||||
describe '#iid' do
|
describe '#iid' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::Repo do
|
RSpec.describe Bitbucket::Representation::Repo do
|
||||||
describe '#has_wiki?' do
|
describe '#has_wiki?' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Bitbucket::Representation::User do
|
RSpec.describe Bitbucket::Representation::User do
|
||||||
describe '#username' do
|
describe '#username' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe BitbucketServer::Page do
|
RSpec.describe BitbucketServer::Page do
|
||||||
let(:response) { { 'values' => [{ 'description' => 'Test' }], 'isLastPage' => false, 'nextPageStart' => 2 } }
|
let(:response) { { 'values' => [{ 'description' => 'Test' }], 'isLastPage' => false, 'nextPageStart' => 2 } }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe BulkImports::Pipeline::ExtractedData do
|
RSpec.describe BulkImports::Pipeline::ExtractedData do
|
||||||
let(:data) { 'data' }
|
let(:data) { 'data' }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe BulkImports::Pipeline do
|
RSpec.describe BulkImports::Pipeline do
|
||||||
let(:context) { instance_double(BulkImports::Pipeline::Context, tracker: nil) }
|
let(:context) { instance_double(BulkImports::Pipeline::Context, tracker: nil) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe BulkImports::RetryPipelineError do
|
RSpec.describe BulkImports::RetryPipelineError do
|
||||||
describe '#retry_delay' do
|
describe '#retry_delay' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Constraints::JiraEncodedUrlConstrainer do
|
RSpec.describe Constraints::JiraEncodedUrlConstrainer do
|
||||||
let(:namespace_id) { 'group' }
|
let(:namespace_id) { 'group' }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe DeclarativeEnum do
|
RSpec.describe DeclarativeEnum do
|
||||||
let(:enum_module) do
|
let(:enum_module) do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe ErrorTracking::SentryClient do
|
RSpec.describe ErrorTracking::SentryClient do
|
||||||
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
|
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do
|
RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do
|
||||||
let(:instance) { described_class.new({}) }
|
let(:instance) { described_class.new({}) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::ApplicationRateLimiter::BaseStrategy do
|
RSpec.describe Gitlab::ApplicationRateLimiter::BaseStrategy do
|
||||||
describe '#increment' do
|
describe '#increment' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Asciidoc::Html5Converter do
|
RSpec.describe Gitlab::Asciidoc::Html5Converter do
|
||||||
describe 'convert AsciiDoc to HTML5' do
|
describe 'convert AsciiDoc to HTML5' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Audit::NullTarget do
|
RSpec.describe Gitlab::Audit::NullTarget do
|
||||||
subject { described_class.new }
|
subject { described_class.new }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "spec_helper"
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::ChangesList do
|
RSpec.describe Gitlab::ChangesList do
|
||||||
let(:valid_changes_string) { "\n000000 570e7b2 refs/heads/my_branch\nd14d6c 6fd24d refs/heads/master" }
|
let(:valid_changes_string) { "\n000000 570e7b2 refs/heads/my_branch\nd14d6c 6fd24d refs/heads/master" }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Chat::Responder::Base do
|
RSpec.describe Gitlab::Chat::Responder::Base do
|
||||||
let(:project) { double(:project) }
|
let(:project) { double(:project) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
# The rest of the specs for this class are covered in style_spec.rb
|
# The rest of the specs for this class are covered in style_spec.rb
|
||||||
RSpec.describe Gitlab::Ci::Ansi2json::Parser do
|
RSpec.describe Gitlab::Ci::Ansi2json::Parser do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Ansi2json::Result do
|
RSpec.describe Gitlab::Ci::Ansi2json::Result do
|
||||||
let(:stream) { StringIO.new('hello') }
|
let(:stream) { StringIO.new('hello') }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Build::Artifacts::Path do
|
RSpec.describe Gitlab::Ci::Build::Artifacts::Path do
|
||||||
describe '#valid?' do
|
describe '#valid?' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Build::Policy do
|
RSpec.describe Gitlab::Ci::Build::Policy do
|
||||||
let(:policy) { spy('policy specification') }
|
let(:policy) { spy('policy specification') }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Build::Port do
|
RSpec.describe Gitlab::Ci::Build::Port do
|
||||||
subject { described_class.new(port) }
|
subject { described_class.new(port) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
require_dependency 'active_model'
|
require_dependency 'active_model'
|
||||||
|
|
||||||
RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
|
RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::MaskSecret do
|
RSpec.describe Gitlab::Ci::MaskSecret do
|
||||||
subject { described_class }
|
subject { described_class }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
|
RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
|
||||||
subject(:parse_source) { described_class.parse_source(properties) }
|
subject(:parse_source) { described_class.parse_source(properties) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
|
RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
|
||||||
subject { described_class.source(property_data) }
|
subject { described_class.source(property_data) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Parsers do
|
RSpec.describe Gitlab::Ci::Parsers do
|
||||||
describe '.fabricate!' do
|
describe '.fabricate!' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Pipeline::Duration do
|
RSpec.describe Gitlab::Ci::Pipeline::Duration do
|
||||||
let(:calculated_duration) { calculate(data) }
|
let(:calculated_duration) { calculate(data) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Null do
|
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Null do
|
||||||
describe '.build' do
|
describe '.build' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::String do
|
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::String do
|
||||||
describe '.build' do
|
describe '.build' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Variable do
|
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Variable do
|
||||||
describe '.build' do
|
describe '.build' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe ::Gitlab::Ci::Pipeline::Metrics do
|
RSpec.describe ::Gitlab::Ci::Pipeline::Metrics do
|
||||||
describe '.pipeline_creation_step_duration_histogram' do
|
describe '.pipeline_creation_step_duration_histogram' do
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
|
RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
|
||||||
let(:comparer) { described_class.new(base_report, head_report) }
|
let(:comparer) { described_class.new(base_report, head_report) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do
|
RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do
|
||||||
let(:accessibility_report) { described_class.new }
|
let(:accessibility_report) { described_class.new }
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue