Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot
2021-07-09 15:09:47 +00:00
parent a373ecffca
commit ffd8742a62
27 changed files with 592 additions and 161 deletions

View File

@ -310,7 +310,7 @@ gem 'pg_query', '~> 2.1'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.18.0'
gem 'gitlab-labkit', '~> 0.20.0'
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
gem 'thrift', '>= 0.14.0'

View File

@ -482,13 +482,13 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
gitlab-labkit (0.18.0)
gitlab-labkit (0.20.0)
actionpack (>= 5.0.0, < 7.0.0)
activesupport (>= 5.0.0, < 7.0.0)
gitlab-pg_query (~> 2.0)
grpc (~> 1.19)
jaeger-client (~> 1.1)
opentracing (~> 0.4)
pg_query (~> 2.1)
redis (> 3.0.0, < 5.0.0)
gitlab-license (1.5.0)
gitlab-mail_room (0.0.9)
@ -498,8 +498,6 @@ GEM
addressable (~> 2.7)
omniauth (~> 1.9)
openid_connect (~> 1.2)
gitlab-pg_query (2.0.4)
google-protobuf (>= 3.17.1)
gitlab-sidekiq-fetcher (0.5.6)
sidekiq (~> 5)
gitlab-styles (6.2.0)
@ -1492,7 +1490,7 @@ DEPENDENCIES
gitlab-dangerfiles (~> 2.2.1)
gitlab-experiment (~> 0.6.1)
gitlab-fog-azure-rm (~> 1.1.1)
gitlab-labkit (~> 0.18.0)
gitlab-labkit (~> 0.20.0)
gitlab-license (~> 1.5)
gitlab-mail_room (~> 0.0.9)
gitlab-markup (~> 1.7.1)

View File

@ -11,31 +11,10 @@ import { isObject } from './type_utility';
import { getLocationHash } from './url_utility';
export const getPagePath = (index = 0) => {
const page = $('body').attr('data-page') || '';
const { page = '' } = document?.body?.dataset;
return page.split(':')[index];
};
export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null;
export const isInGroupsPage = () => getPagePath() === 'groups';
export const isInProjectPage = () => getPagePath() === 'projects';
export const getProjectSlug = () => {
if (isInProjectPage()) {
return $('body').data('project');
}
return null;
};
export const getGroupSlug = () => {
if (isInProjectPage() || isInGroupsPage()) {
return $('body').data('group');
}
return null;
};
export const checkPageAndAction = (page, action) => {
const pagePath = getPagePath(1);
const actionPath = getPagePath(2);
@ -49,6 +28,8 @@ export const isInDesignPage = () => checkPageAndAction('issues', 'designs');
export const isInMRPage = () => checkPageAndAction('merge_requests', 'show');
export const isInEpicPage = () => checkPageAndAction('epics', 'show');
export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null;
export const getCspNonceValue = () => {
const metaTag = document.querySelector('meta[name=csp-nonce]');
return metaTag && metaTag.content;
@ -328,8 +309,8 @@ export const insertText = (target, text) => {
};
/**
this will take in the headers from an API response and normalize them
this way we don't run into production issues when nginx gives us lowercased header keys
this will take in the headers from an API response and normalize them
this way we don't run into production issues when nginx gives us lowercased header keys
*/
export const normalizeHeaders = (headers) => {
const upperCaseHeaders = {};

View File

@ -0,0 +1,71 @@
<script>
import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
import {
ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
CLEANUP_STATUS_SCHEDULED,
CLEANUP_STATUS_ONGOING,
CLEANUP_STATUS_UNFINISHED,
UNFINISHED_STATUS,
UNSCHEDULED_STATUS,
SCHEDULED_STATUS,
ONGOING_STATUS,
} from '../../constants/index';
export default {
name: 'CleanupStatus',
components: {
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
status: {
type: String,
required: true,
validator(value) {
return [UNFINISHED_STATUS, UNSCHEDULED_STATUS, SCHEDULED_STATUS, ONGOING_STATUS].includes(
value,
);
},
},
},
i18n: {
CLEANUP_STATUS_SCHEDULED,
CLEANUP_STATUS_ONGOING,
CLEANUP_STATUS_UNFINISHED,
ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
},
computed: {
showStatus() {
return this.status !== UNSCHEDULED_STATUS;
},
failedDelete() {
return this.status === UNFINISHED_STATUS;
},
statusText() {
return this.$options.i18n[`CLEANUP_STATUS_${this.status}`];
},
expireIconClass() {
return this.failedDelete ? 'gl-text-orange-500' : '';
},
},
};
</script>
<template>
<div v-if="showStatus" class="gl-display-inline-flex gl-align-items-center">
<gl-icon name="expire" data-testid="main-icon" :class="expireIconClass" />
<span class="gl-mx-2">
{{ statusText }}
</span>
<gl-icon
v-if="failedDelete"
v-gl-tooltip="{ title: $options.i18n.ASYNC_DELETE_IMAGE_ERROR_MESSAGE }"
:size="14"
class="gl-text-black-normal"
data-testid="extra-info"
name="information"
/>
</div>
</template>

View File

@ -16,6 +16,7 @@ import {
ROOT_IMAGE_TEXT,
} from '../../constants/index';
import DeleteButton from '../delete_button.vue';
import CleanupStatus from './cleanup_status.vue';
export default {
name: 'ImageListRow',
@ -26,6 +27,7 @@ export default {
GlIcon,
ListItem,
GlSkeletonLoader,
CleanupStatus,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -112,27 +114,24 @@ export default {
:title="item.location"
category="tertiary"
/>
<gl-icon
v-if="warningIconText"
v-gl-tooltip="{ title: warningIconText }"
data-testid="warning-icon"
name="warning"
class="gl-text-orange-500"
/>
</template>
<template #left-secondary>
<span
v-if="!metadataLoading"
class="gl-display-flex gl-align-items-center"
data-testid="tags-count"
>
<gl-icon name="tag" class="gl-mr-2" />
<gl-sprintf :message="tagsCountText">
<template #count>
{{ item.tagsCount }}
</template>
</gl-sprintf>
</span>
<template v-if="!metadataLoading">
<span class="gl-display-flex gl-align-items-center" data-testid="tags-count">
<gl-icon name="tag" class="gl-mr-2" />
<gl-sprintf :message="tagsCountText">
<template #count>
{{ item.tagsCount }}
</template>
</gl-sprintf>
</span>
<cleanup-status
v-if="item.expirationPolicyCleanupStatus"
class="ml-2"
:status="item.expirationPolicyCleanupStatus"
/>
</template>
<div v-else class="gl-w-full">
<gl-skeleton-loader :width="900" :height="16" preserve-aspect-ratio="xMinYMax meet">

View File

@ -89,6 +89,10 @@ export const CLEANUP_DISABLED_TOOLTIP = s__(
'ContainerRegistry|Cleanup is disabled for this project',
);
export const CLEANUP_STATUS_SCHEDULED = s__('ContainerRegistry|Cleanup will run soon');
export const CLEANUP_STATUS_ONGOING = s__('ContainerRegistry|Cleanup is ongoing');
export const CLEANUP_STATUS_UNFINISHED = s__('ContainerRegistry|Cleanup timed out');
export const DETAILS_DELETE_IMAGE_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while scheduling the image for deletion.',
);

View File

@ -0,0 +1,60 @@
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlButton,
},
props: {
value: {
type: String,
required: false,
default: '',
},
},
data() {
return {
isMasked: true,
};
},
computed: {
label() {
if (this.isMasked) {
return __('Click to reveal');
}
return __('Click to hide');
},
icon() {
if (this.isMasked) {
return 'eye';
}
return 'eye-slash';
},
displayedValue() {
if (this.isMasked && this.value?.length) {
return '*'.repeat(this.value.length);
}
return this.value;
},
},
methods: {
toggleMasked() {
this.isMasked = !this.isMasked;
},
},
};
</script>
<template>
<span
>{{ displayedValue }}
<gl-button
:aria-label="label"
:icon="icon"
class="gl-text-body!"
data-testid="toggle-masked"
variant="link"
@click="toggleMasked"
/>
</span>
</template>

View File

@ -1,6 +1,7 @@
<script>
import { GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import RunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
@ -11,6 +12,7 @@ export default {
GlLink,
GlSprintf,
ClipboardButton,
MaskedValue,
RunnerInstructions,
RunnerRegistrationTokenReset,
},
@ -92,7 +94,9 @@ export default {
{{ __('And this registration token:') }}
<br />
<code data-testid="registration-token">{{ currentRegistrationToken }}</code>
<code data-testid="registration-token"
><masked-value :value="currentRegistrationToken"
/></code>
<clipboard-button :title="__('Copy token')" :text="currentRegistrationToken" />
</li>
</ol>

View File

@ -8,13 +8,13 @@ import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { s__, __, sprintf } from '~/locale';
import Tracking from '~/tracking';
import axios from './lib/utils/axios_utils';
import { spriteIcon } from './lib/utils/common_utils';
import {
isInGroupsPage,
isInProjectPage,
getGroupSlug,
getProjectSlug,
spriteIcon,
} from './lib/utils/common_utils';
} from './search_autocomplete_utils';
/**
* Search input in top navigation bar.

View File

@ -0,0 +1,19 @@
import { getPagePath } from './lib/utils/common_utils';
export const isInGroupsPage = () => getPagePath() === 'groups';
export const isInProjectPage = () => getPagePath() === 'projects';
export const getProjectSlug = () => {
if (isInProjectPage()) {
return document?.body?.dataset?.project;
}
return null;
};
export const getGroupSlug = () => {
if (isInProjectPage() || isInGroupsPage()) {
return document?.body?.dataset?.group;
}
return null;
};

View File

@ -7,6 +7,7 @@
.diff-files-holder {
flex: 1;
min-width: 0;
z-index: 203;
.vue-recycle-scroller__item-wrapper {
overflow: visible;

View File

@ -29,6 +29,7 @@ query getProjectContainerRepositories(
canDelete
createdAt
expirationPolicyStartedAt
expirationPolicyCleanupStatus
__typename
}
pageInfo {
@ -61,6 +62,7 @@ query getProjectContainerRepositories(
canDelete
createdAt
expirationPolicyStartedAt
expirationPolicyCleanupStatus
__typename
}
pageInfo {

View File

@ -10,9 +10,7 @@
= f.label :repository_checks_enabled, class: 'form-check-label' do
= _("Enable repository checks")
.form-text.text-muted
= _("Run")
%code= _("git fsck")
= _("periodically in all project and wiki repositories to look for silent disk corruption issues.")
= html_escape(s_('Run %{code_start}git fsck%{code_end} periodically in all project and wiki repositories to look for silent disk corruption issues.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
.form-group
.form-text.text-muted
= _("If you get a lot of false alarms from repository checks, you can clear all repository check information from the database.")
@ -41,22 +39,16 @@
= f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
= f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
.form-text.text-muted
= _("Number of Git pushes after which an incremental")
%code= _("git repack")
= _("is run.")
= html_escape(s_('Number of Git pushes after which an incremental %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
.form-group
= f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
= f.number_field :housekeeping_full_repack_period, class: 'form-control gl-form-input'
.form-text.text-muted
= _("Number of Git pushes after which a full")
%code= _("git repack")
= _("is run.")
= html_escape(s_('Number of Git pushes after which a full %{code_start}git repack%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
.form-group
= f.label :housekeeping_gc_period, _('Git GC period'), class: 'label-bold'
= f.number_field :housekeeping_gc_period, class: 'form-control gl-form-input'
.form-text.text-muted
= _("Number of Git pushes after which")
%code= _("git gc")
= _("is run.")
= html_escape(s_('Number of Git pushes after which %{code_start}git gc%{code_end} is run.')) % { code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"

View File

@ -43,11 +43,11 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure')
= link_to s_('repository checks'), help_page_path('administration/repository_checks.md'), target: '_blank', rel: 'noopener noreferrer'
= _('and')
= link_to s_('housekeeping'), help_page_path('administration/housekeeping.md'), target: '_blank', rel: 'noopener noreferrer'
= _('on repositories.')
- repository_checks_link_url = help_page_path('administration/repository_checks.md')
- repository_checks_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: repository_checks_link_url }
- housekeeping_link_url = help_page_path('administration/housekeeping.md')
- housekeeping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: housekeeping_link_url }
= html_escape(s_('Configure %{repository_checks_link_start}repository checks%{link_end} and %{housekeeping_link_start}housekeeping%{link_end} on repositories.')) % { repository_checks_link_start: repository_checks_link_start, housekeeping_link_start: housekeeping_link_start, link_end: '</a>'.html_safe }
.settings-content
= render 'repository_check'

View File

@ -1,5 +1,4 @@
- page_title _("Blame"), @blob.path, @ref
- link_icon = sprite_icon("link", size: 12)
#blob-content-holder.tree-holder
= render "projects/blob/breadcrumb", blob: @blob, blame: true
@ -48,8 +47,8 @@
%td.line-numbers
- (current_line...(current_line + line_count)).each do |i|
%a.diff-line-num.gl-justify-content-end{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i, class: "gl-display-flex!" }
= link_icon
= i
.file-line-num
= i
\
%td.lines

View File

@ -122,6 +122,7 @@ From there, you can see the following actions:
- When default branch changes for a project ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/52339) in GitLab 13.9)
- Created, updated, or deleted DAST profiles, DAST scanner profiles, and DAST site profiles
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217872) in GitLab 14.1)
- Changed a project's compliance framework ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329362) in GitLab 14.1)
Project events can also be accessed via the [Project Audit Events API](../api/audit_events.md#project-audit-events).

View File

@ -73,6 +73,10 @@ WARNING:
GitLab does not back up any configuration files, SSL certificates, or system
files. You are highly advised to read about [storing configuration files](#storing-configuration-files).
WARNING:
The backup command requires [additional parameters](#backup-and-restore-for-installations-using-pgbouncer) when
your installation is using PgBouncer, for either performance reasons or when using it with a Patroni cluster.
Depending on your version of GitLab, use the following command if you installed
GitLab using the Omnibus package:
@ -955,8 +959,9 @@ installed version of GitLab, the restore command aborts with an error
message. Install the [correct GitLab version](https://packages.gitlab.com/gitlab/),
and then try again.
NOTE:
There is a known issue with restore not working with `pgbouncer`. [Read more about backup and restore with `pgbouncer`](#backup-and-restore-for-installations-using-pgbouncer).
WARNING:
The restore command requires [additional parameters](#backup-and-restore-for-installations-using-pgbouncer) when
your installation is using PgBouncer, for either performance reasons or when using it with a Patroni cluster.
Next, restore `/etc/gitlab/gitlab-secrets.json` if necessary,
[as previously mentioned](#restore-prerequisites).

View File

@ -720,10 +720,10 @@ msgstr ""
msgid "%{mrText}, this issue will be closed automatically."
msgstr ""
msgid "%{name_with_link} has %{percent} or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
msgid "%{name_with_link} namespace has %{percent} or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "%{name_with_link} has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run."
msgid "%{name_with_link} namespace has run out of Shared Runner Pipeline minutes. No new jobs or pipelines in its projects will run."
msgstr ""
msgid "%{name} %{status}"
@ -747,10 +747,10 @@ msgstr ""
msgid "%{name}'s avatar"
msgstr ""
msgid "%{name}(%{url}) has %{percent} or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
msgid "%{name}(%{url}) namespace has %{percent} or less Shared Runner Pipeline minutes remaining. After it runs out, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "%{name}(%{url}) has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run."
msgid "%{name}(%{url}) namespace has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run."
msgstr ""
msgid "%{name}, confirm your email address now!"
@ -6840,6 +6840,12 @@ msgstr ""
msgid "Click to expand text"
msgstr ""
msgid "Click to hide"
msgstr ""
msgid "Click to reveal"
msgstr ""
msgid "Client authentication certificate"
msgstr ""
@ -8237,15 +8243,15 @@ msgstr ""
msgid "Configuration help"
msgstr ""
msgid "Configure"
msgstr ""
msgid "Configure %{italic_start}What's new%{italic_end} drawer and content."
msgstr ""
msgid "Configure %{link} to track events. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Configure %{repository_checks_link_start}repository checks%{link_end} and %{housekeeping_link_start}housekeeping%{link_end} on repositories."
msgstr ""
msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
msgstr ""
@ -8490,6 +8496,9 @@ msgstr ""
msgid "ContainerRegistry|Cleanup is disabled for this project"
msgstr ""
msgid "ContainerRegistry|Cleanup is ongoing"
msgstr ""
msgid "ContainerRegistry|Cleanup pending"
msgstr ""
@ -8505,6 +8514,9 @@ msgstr ""
msgid "ContainerRegistry|Cleanup ran but some tags were not removed"
msgstr ""
msgid "ContainerRegistry|Cleanup timed out"
msgstr ""
msgid "ContainerRegistry|Cleanup timed out before it could delete all tags"
msgstr ""
@ -22616,13 +22628,13 @@ msgstr ""
msgid "Number of Elasticsearch shards and replicas (per index)"
msgstr ""
msgid "Number of Git pushes after which"
msgid "Number of Git pushes after which %{code_start}git gc%{code_end} is run."
msgstr ""
msgid "Number of Git pushes after which a full"
msgid "Number of Git pushes after which a full %{code_start}git repack%{code_end} is run."
msgstr ""
msgid "Number of Git pushes after which an incremental"
msgid "Number of Git pushes after which an incremental %{code_start}git repack%{code_end} is run."
msgstr ""
msgid "Number of LOCs per commit"
@ -24127,12 +24139,6 @@ msgstr ""
msgid "Pipelines|GitLab CI/CD can automatically build, test, and deploy your code. Let GitLab take care of time consuming tasks, so you can spend more time creating."
msgstr ""
msgid "Pipelines|Group %{namespace_name} has %{percentage}%% or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|Group %{namespace_name} has exceeded its pipeline minutes quota. Unless you buy additional pipeline minutes, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|If you are unsure, please ask a project maintainer to review it for you."
msgstr ""
@ -24187,6 +24193,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
msgid "Pipelines|The %{namespace_name} namespace has %{percentage}%% or less Shared Runner Pipeline minutes remaining. After it runs out, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|The %{namespace_name} namespace has exceeded its pipeline minutes quota. Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|The CI configuration was not loaded, please try again."
msgstr ""
@ -28134,7 +28146,7 @@ msgstr ""
msgid "Rules that define what git pushes are accepted for a project. All newly created projects will use these settings."
msgstr ""
msgid "Run"
msgid "Run %{code_start}git fsck%{code_end} periodically in all project and wiki repositories to look for silent disk corruption issues."
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
@ -36570,10 +36582,10 @@ msgstr ""
msgid "We recommend leaving all SAST analyzers enabled"
msgstr ""
msgid "We recommend that you buy more Pipeline minutes to avoid any interruption of service."
msgid "We recommend that you buy additional Pipeline minutes to avoid any interruption of service."
msgstr ""
msgid "We recommend that you buy more Pipeline minutes to resume normal service."
msgid "We recommend that you buy additional Pipeline minutes to resume normal service."
msgstr ""
msgid "We sent you an email with reset password instructions"
@ -38752,15 +38764,6 @@ msgid_plural "from %d jobs"
msgstr[0] ""
msgstr[1] ""
msgid "git fsck"
msgstr ""
msgid "git gc"
msgstr ""
msgid "git repack"
msgstr ""
msgid "group"
msgstr ""
@ -38794,9 +38797,6 @@ msgstr ""
msgid "here"
msgstr ""
msgid "housekeeping"
msgstr ""
msgid "http:"
msgstr ""
@ -38892,9 +38892,6 @@ msgstr ""
msgid "is read-only"
msgstr ""
msgid "is run."
msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
@ -39395,9 +39392,6 @@ msgstr ""
msgid "nounSeries|%{item}, and %{lastItem}"
msgstr ""
msgid "on repositories."
msgstr ""
msgid "on track"
msgstr ""
@ -39441,9 +39435,6 @@ msgstr ""
msgid "per day"
msgstr ""
msgid "periodically in all project and wiki repositories to look for silent disk corruption issues."
msgstr ""
msgid "personal access token"
msgstr ""
@ -39549,9 +39540,6 @@ msgid_plural "replies"
msgstr[0] ""
msgstr[1] ""
msgid "repository checks"
msgstr ""
msgid "repository:"
msgstr ""

View File

@ -56,7 +56,7 @@ module DeprecationToolkitEnv
def self.allowed_kwarg_warning_paths
%w[
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
gitlab-labkit-0.18.0/lib/labkit/correlation/grpc/client_interceptor.rb
gitlab-labkit-0.20.0/lib/labkit/correlation/grpc/client_interceptor.rb
]
end

View File

@ -1,6 +1,56 @@
import * as commonUtils from '~/lib/utils/common_utils';
describe('common_utils', () => {
describe('getPagePath', () => {
const { getPagePath } = commonUtils;
let originalBody;
beforeEach(() => {
originalBody = document.body;
document.body = document.createElement('body');
});
afterEach(() => {
document.body = originalBody;
});
it('returns an empty path if none is defined', () => {
expect(getPagePath()).toBe('');
expect(getPagePath(0)).toBe('');
});
describe('returns a path', () => {
const mockSection = 'my_section';
const mockSubSection = 'my_sub_section';
const mockPage = 'my_page';
it('returns a page', () => {
document.body.dataset.page = mockPage;
expect(getPagePath()).toBe(mockPage);
expect(getPagePath(0)).toBe(mockPage);
});
it('returns a section and page', () => {
document.body.dataset.page = `${mockSection}:${mockPage}`;
expect(getPagePath()).toBe(mockSection);
expect(getPagePath(0)).toBe(mockSection);
expect(getPagePath(1)).toBe(mockPage);
});
it('returns a section and subsection', () => {
document.body.dataset.page = `${mockSection}:${mockSubSection}:${mockPage}`;
expect(getPagePath()).toBe(mockSection);
expect(getPagePath(0)).toBe(mockSection);
expect(getPagePath(1)).toBe(mockSubSection);
expect(getPagePath(2)).toBe(mockPage);
});
});
});
describe('parseUrl', () => {
it('returns an anchor tag with url', () => {
expect(commonUtils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');

View File

@ -0,0 +1,87 @@
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import CleanupStatus from '~/registry/explorer/components/list_page/cleanup_status.vue';
import {
ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
CLEANUP_STATUS_SCHEDULED,
CLEANUP_STATUS_ONGOING,
CLEANUP_STATUS_UNFINISHED,
UNFINISHED_STATUS,
UNSCHEDULED_STATUS,
SCHEDULED_STATUS,
ONGOING_STATUS,
} from '~/registry/explorer/constants';
describe('cleanup_status', () => {
let wrapper;
const findMainIcon = () => wrapper.findByTestId('main-icon');
const findExtraInfoIcon = () => wrapper.findByTestId('extra-info');
const mountComponent = (propsData = { status: SCHEDULED_STATUS }) => {
wrapper = shallowMountExtended(CleanupStatus, {
propsData,
directives: {
GlTooltip: createMockDirective(),
},
});
};
afterEach(() => {
wrapper.destroy();
});
it.each`
status | visible | text
${UNFINISHED_STATUS} | ${true} | ${CLEANUP_STATUS_UNFINISHED}
${SCHEDULED_STATUS} | ${true} | ${CLEANUP_STATUS_SCHEDULED}
${ONGOING_STATUS} | ${true} | ${CLEANUP_STATUS_ONGOING}
${UNSCHEDULED_STATUS} | ${false} | ${''}
`(
'when the status is $status is $visible that the component is mounted and has the correct text',
({ status, visible, text }) => {
mountComponent({ status });
expect(findMainIcon().exists()).toBe(visible);
expect(wrapper.text()).toBe(text);
},
);
describe('main icon', () => {
it('exists', () => {
mountComponent();
expect(findMainIcon().exists()).toBe(true);
});
it(`has the orange class when the status is ${UNFINISHED_STATUS}`, () => {
mountComponent({ status: UNFINISHED_STATUS });
expect(findMainIcon().classes('gl-text-orange-500')).toBe(true);
});
});
describe('extra info icon', () => {
it.each`
status | visible
${UNFINISHED_STATUS} | ${true}
${SCHEDULED_STATUS} | ${false}
${ONGOING_STATUS} | ${false}
`(
'when the status is $status is $visible that the extra icon is visible',
({ status, visible }) => {
mountComponent({ status });
expect(findExtraInfoIcon().exists()).toBe(visible);
},
);
it(`has a tooltip`, () => {
mountComponent({ status: UNFINISHED_STATUS });
const tooltip = getBinding(findExtraInfoIcon().element, 'gl-tooltip');
expect(tooltip.value.title).toBe(ASYNC_DELETE_IMAGE_ERROR_MESSAGE);
});
});
});

View File

@ -3,15 +3,14 @@ import { shallowMount } from '@vue/test-utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import DeleteButton from '~/registry/explorer/components/delete_button.vue';
import CleanupStatus from '~/registry/explorer/components/list_page/cleanup_status.vue';
import Component from '~/registry/explorer/components/list_page/image_list_row.vue';
import {
ROW_SCHEDULED_FOR_DELETION,
LIST_DELETE_BUTTON_DISABLED,
REMOVE_REPOSITORY_LABEL,
ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
CLEANUP_TIMED_OUT_ERROR_MESSAGE,
IMAGE_DELETE_SCHEDULED_STATUS,
IMAGE_FAILED_DELETED_STATUS,
SCHEDULED_STATUS,
ROOT_IMAGE_TEXT,
} from '~/registry/explorer/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
@ -27,7 +26,7 @@ describe('Image List Row', () => {
const findTagsCount = () => wrapper.find('[data-testid="tags-count"]');
const findDeleteBtn = () => wrapper.findComponent(DeleteButton);
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findWarningIcon = () => wrapper.find('[data-testid="warning-icon"]');
const findCleanupStatus = () => wrapper.findComponent(CleanupStatus);
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findListItemComponent = () => wrapper.findComponent(ListItem);
@ -106,23 +105,22 @@ describe('Image List Row', () => {
expect(button.props('title')).toBe(item.location);
});
describe('warning icon', () => {
describe('cleanup status component', () => {
it.each`
status | expirationPolicyStartedAt | shown | title
${IMAGE_FAILED_DELETED_STATUS} | ${true} | ${true} | ${ASYNC_DELETE_IMAGE_ERROR_MESSAGE}
${''} | ${true} | ${true} | ${CLEANUP_TIMED_OUT_ERROR_MESSAGE}
${''} | ${false} | ${false} | ${''}
expirationPolicyCleanupStatus | shown
${null} | ${false}
${SCHEDULED_STATUS} | ${true}
`(
'when status is $status and expirationPolicyStartedAt is $expirationPolicyStartedAt',
({ expirationPolicyStartedAt, status, shown, title }) => {
mountComponent({ item: { ...item, status, expirationPolicyStartedAt } });
'when expirationPolicyCleanupStatus is $expirationPolicyCleanupStatus it is $shown that the component exists',
({ expirationPolicyCleanupStatus, shown }) => {
mountComponent({ item: { ...item, expirationPolicyCleanupStatus } });
const icon = findWarningIcon();
expect(icon.exists()).toBe(shown);
expect(findCleanupStatus().exists()).toBe(shown);
if (shown) {
const tooltip = getBinding(icon.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(title);
expect(findCleanupStatus().props()).toMatchObject({
status: expirationPolicyCleanupStatus,
});
}
},
);

View File

@ -9,6 +9,7 @@ export const imagesListResponse = [
canDelete: true,
createdAt: '2020-11-03T13:29:21Z',
expirationPolicyStartedAt: null,
expirationPolicyCleanupStatus: 'UNSCHEDULED',
},
{
__typename: 'ContainerRepository',
@ -20,6 +21,7 @@ export const imagesListResponse = [
canDelete: true,
createdAt: '2020-09-21T06:57:43Z',
expirationPolicyStartedAt: null,
expirationPolicyCleanupStatus: 'UNSCHEDULED',
},
];

View File

@ -0,0 +1,51 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
const mockSecret = '01234567890';
const mockMasked = '***********';
describe('MaskedValue', () => {
let wrapper;
const findButton = () => wrapper.findComponent(GlButton);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(MaskedValue, {
propsData: {
value: mockSecret,
...props,
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('Displays masked value by default', () => {
expect(wrapper.text()).toBe(mockMasked);
});
describe('When the icon is clicked', () => {
beforeEach(() => {
findButton().vm.$emit('click');
});
it('Displays the actual value', () => {
expect(wrapper.text()).toBe(mockSecret);
expect(wrapper.text()).not.toBe(mockMasked);
});
it('When user clicks again, displays masked value', async () => {
await findButton().vm.$emit('click');
expect(wrapper.text()).toBe(mockMasked);
expect(wrapper.text()).not.toBe(mockSecret);
});
});
});

View File

@ -3,6 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
import RunnerManualSetupHelp from '~/runner/components/runner_manual_setup_help.vue';
import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
@ -37,6 +38,7 @@ describe('RunnerManualSetupHelp', () => {
...props,
},
stubs: {
MaskedValue,
GlSprintf,
},
}),
@ -93,7 +95,11 @@ describe('RunnerManualSetupHelp', () => {
expect(findRunnerInstructions().exists()).toBe(true);
});
it('Displays the registration token', () => {
it('Displays the registration token', async () => {
findRegistrationToken().find('[data-testid="toggle-masked"]').vm.$emit('click');
await nextTick();
expect(findRegistrationToken().text()).toBe(mockRegistrationToken);
expect(findClipboardButtons().at(1).props('text')).toBe(mockRegistrationToken);
});
@ -105,6 +111,7 @@ describe('RunnerManualSetupHelp', () => {
it('Replaces the runner reset button', async () => {
const mockNewRegistrationToken = 'NEW_MOCK_REGISTRATION_TOKEN';
findRegistrationToken().find('[data-testid="toggle-masked"]').vm.$emit('click');
findRunnerRegistrationTokenReset().vm.$emit('tokenReset', mockNewRegistrationToken);
await nextTick();

View File

@ -1,4 +1,3 @@
/* eslint-disable no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign */
import AxiosMockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
@ -22,31 +21,33 @@ describe('Search autocomplete dropdown', () => {
const groupName = 'Gitlab Org';
const removeBodyAttributes = () => {
const $body = $('body');
const { body } = document;
$body.removeAttr('data-page');
$body.removeAttr('data-project');
$body.removeAttr('data-group');
delete body.dataset.page;
delete body.dataset.project;
delete body.dataset.group;
};
// Add required attributes to body before starting the test.
// section would be dashboard|group|project
const addBodyAttributes = (section) => {
if (section == null) {
section = 'dashboard';
}
const $body = $('body');
const addBodyAttributes = (section = 'dashboard') => {
removeBodyAttributes();
const { body } = document;
switch (section) {
case 'dashboard':
return $body.attr('data-page', 'root:index');
body.dataset.page = 'root:index';
break;
case 'group':
$body.attr('data-page', 'groups:show');
return $body.data('group', 'gitlab-org');
body.dataset.page = 'groups:show';
body.dataset.group = 'gitlab-org';
break;
case 'project':
$body.attr('data-page', 'projects:show');
return $body.data('project', 'gitlab-ce');
body.dataset.page = 'projects:show';
body.dataset.project = 'gitlab-ce';
break;
default:
break;
}
};
@ -56,34 +57,31 @@ describe('Search autocomplete dropdown', () => {
// Mock `gl` object in window for dashboard specific page. App code will need it.
const mockDashboardOptions = () => {
window.gl || (window.gl = {});
return (window.gl.dashboardOptions = {
window.gl.dashboardOptions = {
issuesPath: dashboardIssuesPath,
mrPath: dashboardMRsPath,
});
};
};
// Mock `gl` object in window for project specific page. App code will need it.
const mockProjectOptions = () => {
window.gl || (window.gl = {});
return (window.gl.projectOptions = {
window.gl.projectOptions = {
'gitlab-ce': {
issuesPath: projectIssuesPath,
mrPath: projectMRsPath,
projectName,
},
});
};
};
const mockGroupOptions = () => {
window.gl || (window.gl = {});
return (window.gl.groupOptions = {
window.gl.groupOptions = {
'gitlab-org': {
issuesPath: groupIssuesPath,
mrPath: groupMRsPath,
projectName: groupName,
},
});
};
};
const assertLinks = (list, issuesPath, mrsPath) => {
@ -113,7 +111,7 @@ describe('Search autocomplete dropdown', () => {
window.gon.current_username = userName;
window.gl = window.gl || (window.gl = {});
return (widget = initSearchAutocomplete({ autocompletePath }));
widget = initSearchAutocomplete({ autocompletePath });
});
afterEach(() => {

View File

@ -0,0 +1,114 @@
import {
isInGroupsPage,
isInProjectPage,
getGroupSlug,
getProjectSlug,
} from '~/search_autocomplete_utils';
describe('search_autocomplete_utils', () => {
let originalBody;
beforeEach(() => {
originalBody = document.body;
document.body = document.createElement('body');
});
afterEach(() => {
document.body = originalBody;
});
describe('isInGroupsPage', () => {
it.each`
page | result
${'groups:index'} | ${true}
${'groups:show'} | ${true}
${'projects:show'} | ${false}
`(`returns $result in for page $page`, ({ page, result }) => {
document.body.dataset.page = page;
expect(isInGroupsPage()).toBe(result);
});
});
describe('isInProjectPage', () => {
it.each`
page | result
${'projects:index'} | ${true}
${'projects:show'} | ${true}
${'groups:show'} | ${false}
`(`returns $result in for page $page`, ({ page, result }) => {
document.body.dataset.page = page;
expect(isInProjectPage()).toBe(result);
});
});
describe('getProjectSlug', () => {
it('returns null when no project is present or on project page', () => {
expect(getProjectSlug()).toBe(null);
});
it('returns null when not on project page', () => {
document.body.dataset.project = 'gitlab';
expect(getProjectSlug()).toBe(null);
});
it('returns null when project is missing', () => {
document.body.dataset.page = 'projects';
expect(getProjectSlug()).toBe(undefined);
});
it('returns project', () => {
document.body.dataset.page = 'projects';
document.body.dataset.project = 'gitlab';
expect(getProjectSlug()).toBe('gitlab');
});
it('returns project in edit page', () => {
document.body.dataset.page = 'projects:edit';
document.body.dataset.project = 'gitlab';
expect(getProjectSlug()).toBe('gitlab');
});
});
describe('getGroupSlug', () => {
it('returns null when no group is present or on group page', () => {
expect(getGroupSlug()).toBe(null);
});
it('returns null when not on group page', () => {
document.body.dataset.group = 'gitlab-org';
expect(getGroupSlug()).toBe(null);
});
it('returns null when group is missing on groups page', () => {
document.body.dataset.page = 'groups';
expect(getGroupSlug()).toBe(undefined);
});
it('returns null when group is missing on project page', () => {
document.body.dataset.page = 'project';
expect(getGroupSlug()).toBe(null);
});
it.each`
page
${'groups'}
${'groups:edit'}
${'projects'}
${'projects:edit'}
`(`returns group in page $page`, ({ page }) => {
document.body.dataset.page = page;
document.body.dataset.group = 'gitlab-org';
expect(getGroupSlug()).toBe('gitlab-org');
});
});
});