Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-05 09:12:22 +00:00
parent 6d706d5dc0
commit a7ad649614
75 changed files with 996 additions and 674 deletions

View File

@ -277,6 +277,7 @@ export default {
v-model="variable.key" v-model="variable.key"
:token-list="$options.tokenList" :token-list="$options.tokenList"
:label-text="__('Key')" :label-text="__('Key')"
data-testid="pipeline-form-ci-variable-key"
data-qa-selector="ci_variable_key_field" data-qa-selector="ci_variable_key_field"
/> />
@ -293,6 +294,7 @@ export default {
:state="variableValidationState" :state="variableValidationState"
rows="3" rows="3"
max-rows="6" max-rows="6"
data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field" data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!" class="gl-font-monospace!"
/> />

View File

@ -255,6 +255,7 @@ export default {
v-model="key" v-model="key"
:token-list="$options.tokenList" :token-list="$options.tokenList"
:label-text="__('Key')" :label-text="__('Key')"
data-testid="pipeline-form-ci-variable-key"
data-qa-selector="ci_variable_key_field" data-qa-selector="ci_variable_key_field"
/> />
@ -271,6 +272,7 @@ export default {
:state="variableValidationState" :state="variableValidationState"
rows="3" rows="3"
max-rows="6" max-rows="6"
data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field" data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!" class="gl-font-monospace!"
/> />

View File

@ -163,7 +163,6 @@ export default {
v-gl-modal="'configure-feature-flags'" v-gl-modal="'configure-feature-flags'"
variant="confirm" variant="confirm"
category="secondary" category="secondary"
data-qa-selector="configure_feature_flags_button"
data-testid="ff-configure-button" data-testid="ff-configure-button"
class="gl-mb-3" class="gl-mb-3"
> >

View File

@ -142,9 +142,16 @@ export const dayInQuarter = (date, quarter) => {
export const millisecondsPerDay = 1000 * 60 * 60 * 24; export const millisecondsPerDay = 1000 * 60 * 60 * 24;
export const getDayDifference = (a, b) => { /**
const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); * Calculates the number of days between 2 specified dates, excluding the current date
const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); *
* @param {Date} startDate the earlier date that we will substract from the end date
* @param {Date} endDate the last date in the range
* @return {Number} number of days in between
*/
export const getDayDifference = (startDate, endDate) => {
const date1 = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const date2 = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay); return Math.floor((date2 - date1) / millisecondsPerDay);
}; };

View File

@ -88,7 +88,8 @@ export default {
:action-primary="actionPrimary" :action-primary="actionPrimary"
:title="actionText" :title="actionText"
:visible="removeMemberModalVisible" :visible="removeMemberModalVisible"
data-qa-selector="remove_member_modal_content" data-qa-selector="remove_member_modal"
data-testid="remove-member-modal-content"
@primary="submitForm" @primary="submitForm"
@hide="hideRemoveMemberModal" @hide="hideRemoveMemberModal"
> >

View File

@ -391,7 +391,11 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="prometheus-graphs" data-qa-selector="prometheus_graphs"> <div
class="prometheus-graphs"
data-qa-selector="prometheus_graphs_content"
data-testid="prometheus-graphs"
>
<div> <div>
<gl-alert <gl-alert
v-if="!isDeprecationNoticeDismissed" v-if="!isDeprecationNoticeDismissed"

View File

@ -189,6 +189,7 @@ export default {
ref="monitorEnvironmentsDropdown" ref="monitorEnvironmentsDropdown"
class="flex-grow-1" class="flex-grow-1"
data-qa-selector="environments_dropdown" data-qa-selector="environments_dropdown"
data-testid="environments-dropdown"
toggle-class="dropdown-menu-toggle" toggle-class="dropdown-menu-toggle"
menu-class="monitor-environment-dropdown-menu" menu-class="monitor-environment-dropdown-menu"
:text="environmentDropdownText" :text="environmentDropdownText"

View File

@ -59,6 +59,7 @@ export default {
<resolve-discussion-button <resolve-discussion-button
v-if="discussion.resolvable" v-if="discussion.resolvable"
data-qa-selector="resolve_discussion_button" data-qa-selector="resolve_discussion_button"
data-testid="resolve-discussion-button"
:is-resolving="isResolving" :is-resolving="isResolving"
:button-title="resolveButtonTitle" :button-title="resolveButtonTitle"
@onClick="$emit('resolve')" @onClick="$emit('resolve')"

View File

@ -98,7 +98,7 @@ export default {
</div> </div>
<template v-else> <template v-else>
<div data-qa-selector="packages-table"> <div data-testid="packages-table">
<packages-list-row <packages-list-row
v-for="packageEntity in list" v-for="packageEntity in list"
:key="packageEntity.id" :key="packageEntity.id"

View File

@ -78,7 +78,7 @@ export default {
</script> </script>
<template> <template>
<list-item data-qa-selector="package_row" :disabled="disabledRow"> <list-item data-testid="package-row" :disabled="disabledRow">
<template #left-primary> <template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"> <div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<gl-link <gl-link

View File

@ -90,7 +90,7 @@ export default {
</script> </script>
<template> <template>
<list-item data-qa-selector="package_row"> <list-item data-testid="package-row">
<template #left-primary> <template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"> <div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<router-link <router-link

View File

@ -151,7 +151,7 @@ export default {
@primaryAction="showConfirmationModal" @primaryAction="showConfirmationModal"
>{{ $options.i18n.errorMessageBodyAlert }}</gl-alert >{{ $options.i18n.errorMessageBodyAlert }}</gl-alert
> >
<div data-qa-selector="packages-table"> <div data-testid="packages-table">
<packages-list-row <packages-list-row
v-for="packageEntity in list" v-for="packageEntity in list"
:key="packageEntity.id" :key="packageEntity.id"

View File

@ -87,7 +87,7 @@ export default {
v-else-if="!loadingContentFailed && !isLoadingContent" v-else-if="!loadingContentFailed && !isLoadingContent"
ref="content" ref="content"
data-qa-selector="wiki_page_content" data-qa-selector="wiki_page_content"
data-testid="wiki_page_content" data-testid="wiki-page-content"
class="js-wiki-page-content md" class="js-wiki-page-content md"
v-html="content /* eslint-disable-line vue/no-v-html */" v-html="content /* eslint-disable-line vue/no-v-html */"
></div> ></div>

View File

@ -244,7 +244,7 @@ export default {
/><span class="position-relative">{{ fullPath }}</span> /><span class="position-relative">{{ fullPath }}</span>
</component> </component>
<!-- eslint-disable @gitlab/vue-require-i18n-strings --> <!-- eslint-disable @gitlab/vue-require-i18n-strings -->
<gl-badge v-if="lfsOid" variant="muted" size="sm" class="ml-1" data-qa-selector="label-lfs" <gl-badge v-if="lfsOid" variant="muted" size="sm" class="ml-1" data-testid="label-lfs"
>LFS</gl-badge >LFS</gl-badge
> >
<!-- eslint-enable @gitlab/vue-require-i18n-strings --> <!-- eslint-enable @gitlab/vue-require-i18n-strings -->

View File

@ -32,17 +32,14 @@ export default {
<div> <div>
<runner-status-badge <runner-status-badge
:runner="runner" :runner="runner"
size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate" class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/> />
<runner-upgrade-status-badge <runner-upgrade-status-badge
:runner="runner" :runner="runner"
size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate" class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/> />
<runner-paused-badge <runner-paused-badge
v-if="paused" v-if="paused"
size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate" class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/> />
</div> </div>

View File

@ -57,7 +57,7 @@ export default {
:title="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION" :title="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION"
name="lock" name="lock"
/> />
<runner-type-badge class="gl-ml-2" :type="runnerType" size="sm" /> <runner-type-badge class="gl-ml-2 gl-vertical-align-middle" :type="runnerType" size="sm" />
<tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="description"> <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="description">
{{ description }} {{ description }}

View File

@ -38,11 +38,12 @@ export default {
</script> </script>
<template> <template>
<div <div
class="gl-display-flex gl-align-items-center gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100" class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-gap-3 gl-flex-wrap gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
> >
<div> <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap">
<runner-status-badge :runner="runner" /> <runner-status-badge :runner="runner" />
<runner-type-badge v-if="runner" :type="runner.runnerType" /> <runner-type-badge v-if="runner" :type="runner.runnerType" />
<span>
<template v-if="runner.createdAt"> <template v-if="runner.createdAt">
<gl-sprintf :message="__('%{runner} created %{timeago}')"> <gl-sprintf :message="__('%{runner} created %{timeago}')">
<template #runner> <template #runner>
@ -62,7 +63,8 @@ export default {
<template v-else> <template v-else>
<strong>{{ heading }}</strong> <strong>{{ heading }}</strong>
</template> </template>
</span>
</div> </div>
<div class="gl-ml-auto gl-flex-shrink-0"><slot name="actions"></slot></div> <div class="gl-display-flex gl-gap-3 gl-flex-wrap"><slot name="actions"></slot></div>
</div> </div>
</template> </template>

View File

@ -14,7 +14,7 @@ export default {
}; };
</script> </script>
<template> <template>
<span class="gl-font-weight-bold" <span class="gl-font-weight-bold gl-vertical-align-middle"
>#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span >#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span
> >
</template> </template>

View File

@ -1,6 +1,6 @@
<script> <script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui'; import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { I18N_PAUSED_DESCRIPTION } from '../constants'; import { I18N_PAUSED, I18N_PAUSED_DESCRIPTION } from '../constants';
export default { export default {
components: { components: {
@ -9,11 +9,17 @@ export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
I18N_PAUSED,
I18N_PAUSED_DESCRIPTION, I18N_PAUSED_DESCRIPTION,
}; };
</script> </script>
<template> <template>
<gl-badge v-gl-tooltip="$options.I18N_PAUSED_DESCRIPTION" variant="danger" v-bind="$attrs"> <gl-badge
{{ s__('Runners|paused') }} v-gl-tooltip="$options.I18N_PAUSED_DESCRIPTION"
variant="warning"
icon="status-paused"
v-bind="$attrs"
>
{{ $options.I18N_PAUSED }}
</gl-badge> </gl-badge>
</template> </template>

View File

@ -1,8 +1,12 @@
<script> <script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui'; import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { getTimeago } from '~/lib/utils/datetime_utility'; import { getTimeago } from '~/lib/utils/datetime_utility';
import { import {
I18N_STATUS_ONLINE,
I18N_STATUS_NEVER_CONTACTED,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
I18N_ONLINE_TIMEAGO_TOOLTIP, I18N_ONLINE_TIMEAGO_TOOLTIP,
I18N_NEVER_CONTACTED_TOOLTIP, I18N_NEVER_CONTACTED_TOOLTIP,
I18N_OFFLINE_TIMEAGO_TOOLTIP, I18N_OFFLINE_TIMEAGO_TOOLTIP,
@ -39,26 +43,30 @@ export default {
switch (this.runner?.status) { switch (this.runner?.status) {
case STATUS_ONLINE: case STATUS_ONLINE:
return { return {
icon: 'status-active',
variant: 'success', variant: 'success',
label: s__('Runners|online'), label: I18N_STATUS_ONLINE,
tooltip: this.timeAgoTooltip(I18N_ONLINE_TIMEAGO_TOOLTIP), tooltip: this.timeAgoTooltip(I18N_ONLINE_TIMEAGO_TOOLTIP),
}; };
case STATUS_NEVER_CONTACTED: case STATUS_NEVER_CONTACTED:
return { return {
icon: 'time-out',
variant: 'muted', variant: 'muted',
label: s__('Runners|never contacted'), label: I18N_STATUS_NEVER_CONTACTED,
tooltip: I18N_NEVER_CONTACTED_TOOLTIP, tooltip: I18N_NEVER_CONTACTED_TOOLTIP,
}; };
case STATUS_OFFLINE: case STATUS_OFFLINE:
return { return {
icon: 'time-out',
variant: 'muted', variant: 'muted',
label: s__('Runners|offline'), label: I18N_STATUS_OFFLINE,
tooltip: this.timeAgoTooltip(I18N_OFFLINE_TIMEAGO_TOOLTIP), tooltip: this.timeAgoTooltip(I18N_OFFLINE_TIMEAGO_TOOLTIP),
}; };
case STATUS_STALE: case STATUS_STALE:
return { return {
icon: 'time-out',
variant: 'warning', variant: 'warning',
label: s__('Runners|stale'), label: I18N_STATUS_STALE,
// runner may have contacted (or not) and be stale: consider both cases. // runner may have contacted (or not) and be stale: consider both cases.
tooltip: this.runner.contactedAt tooltip: this.runner.contactedAt
? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP) ? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP)
@ -77,7 +85,13 @@ export default {
}; };
</script> </script>
<template> <template>
<gl-badge v-if="badge" v-gl-tooltip="badge.tooltip" :variant="badge.variant" v-bind="$attrs"> <gl-badge
v-if="badge"
v-gl-tooltip="badge.tooltip"
:variant="badge.variant"
:icon="badge.icon"
v-bind="$attrs"
>
{{ badge.label }} {{ badge.label }}
</gl-badge> </gl-badge>
</template> </template>

View File

@ -1,26 +1,31 @@
<script> <script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui'; import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { import {
INSTANCE_TYPE, INSTANCE_TYPE,
GROUP_TYPE, GROUP_TYPE,
PROJECT_TYPE, PROJECT_TYPE,
I18N_INSTANCE_TYPE,
I18N_INSTANCE_RUNNER_DESCRIPTION, I18N_INSTANCE_RUNNER_DESCRIPTION,
I18N_GROUP_TYPE,
I18N_GROUP_RUNNER_DESCRIPTION, I18N_GROUP_RUNNER_DESCRIPTION,
I18N_PROJECT_TYPE,
I18N_PROJECT_RUNNER_DESCRIPTION, I18N_PROJECT_RUNNER_DESCRIPTION,
} from '../constants'; } from '../constants';
const BADGE_DATA = { const BADGE_DATA = {
[INSTANCE_TYPE]: { [INSTANCE_TYPE]: {
text: s__('Runners|shared'), icon: 'users',
text: I18N_INSTANCE_TYPE,
tooltip: I18N_INSTANCE_RUNNER_DESCRIPTION, tooltip: I18N_INSTANCE_RUNNER_DESCRIPTION,
}, },
[GROUP_TYPE]: { [GROUP_TYPE]: {
text: s__('Runners|group'), icon: 'group',
text: I18N_GROUP_TYPE,
tooltip: I18N_GROUP_RUNNER_DESCRIPTION, tooltip: I18N_GROUP_RUNNER_DESCRIPTION,
}, },
[PROJECT_TYPE]: { [PROJECT_TYPE]: {
text: s__('Runners|specific'), icon: 'project',
text: I18N_PROJECT_TYPE,
tooltip: I18N_PROJECT_RUNNER_DESCRIPTION, tooltip: I18N_PROJECT_RUNNER_DESCRIPTION,
}, },
}; };
@ -50,7 +55,13 @@ export default {
}; };
</script> </script>
<template> <template>
<gl-badge v-if="badge" v-gl-tooltip="badge.tooltip" variant="info" v-bind="$attrs"> <gl-badge
v-if="badge"
v-gl-tooltip="badge.tooltip"
variant="muted"
:icon="badge.icon"
v-bind="$attrs"
>
{{ badge.text }} {{ badge.text }}
</gl-badge> </gl-badge>
</template> </template>

View File

@ -1,7 +1,7 @@
import { __, s__ } from '~/locale'; import { __ } from '~/locale';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants'; import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { PARAM_KEY_PAUSED } from '../../constants'; import { PARAM_KEY_PAUSED, I18N_PAUSED } from '../../constants';
const options = [ const options = [
{ value: 'true', title: __('Yes') }, { value: 'true', title: __('Yes') },
@ -10,7 +10,7 @@ const options = [
export const pausedTokenConfig = { export const pausedTokenConfig = {
icon: 'pause', icon: 'pause',
title: s__('Runners|Paused'), title: I18N_PAUSED,
type: PARAM_KEY_PAUSED, type: PARAM_KEY_PAUSED,
token: BaseToken, token: BaseToken,
unique: true, unique: true,

View File

@ -1,7 +1,11 @@
import { __, s__ } from '~/locale'; import { __ } from '~/locale';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants'; import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { import {
I18N_STATUS_ONLINE,
I18N_STATUS_NEVER_CONTACTED,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
STATUS_ONLINE, STATUS_ONLINE,
STATUS_OFFLINE, STATUS_OFFLINE,
STATUS_NEVER_CONTACTED, STATUS_NEVER_CONTACTED,
@ -10,10 +14,10 @@ import {
} from '../../constants'; } from '../../constants';
const options = [ const options = [
{ value: STATUS_ONLINE, title: s__('Runners|Online') }, { value: STATUS_ONLINE, title: I18N_STATUS_ONLINE },
{ value: STATUS_OFFLINE, title: s__('Runners|Offline') }, { value: STATUS_OFFLINE, title: I18N_STATUS_OFFLINE },
{ value: STATUS_NEVER_CONTACTED, title: s__('Runners|Never contacted') }, { value: STATUS_NEVER_CONTACTED, title: I18N_STATUS_NEVER_CONTACTED },
{ value: STATUS_STALE, title: s__('Runners|Stale') }, { value: STATUS_STALE, title: I18N_STATUS_STALE },
]; ];
export const statusTokenConfig = { export const statusTokenConfig = {

View File

@ -1,7 +1,13 @@
<script> <script>
import { s__ } from '~/locale';
import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue'; import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '../../constants'; import {
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
} from '../../constants';
export default { export default {
components: { components: {
@ -29,7 +35,7 @@ export default {
skip: this.statusCountSkip(STATUS_ONLINE), skip: this.statusCountSkip(STATUS_ONLINE),
variables: { ...this.variables, status: STATUS_ONLINE }, variables: { ...this.variables, status: STATUS_ONLINE },
variant: 'success', variant: 'success',
title: s__('Runners|Online'), title: I18N_STATUS_ONLINE,
metaIcon: 'status-active', metaIcon: 'status-active',
}, },
}, },
@ -39,7 +45,7 @@ export default {
skip: this.statusCountSkip(STATUS_OFFLINE), skip: this.statusCountSkip(STATUS_OFFLINE),
variables: { ...this.variables, status: STATUS_OFFLINE }, variables: { ...this.variables, status: STATUS_OFFLINE },
variant: 'muted', variant: 'muted',
title: s__('Runners|Offline'), title: I18N_STATUS_OFFLINE,
metaIcon: 'status-waiting', metaIcon: 'status-waiting',
}, },
}, },
@ -49,7 +55,7 @@ export default {
skip: this.statusCountSkip(STATUS_STALE), skip: this.statusCountSkip(STATUS_STALE),
variables: { ...this.variables, status: STATUS_STALE }, variables: { ...this.variables, status: STATUS_STALE },
variant: 'warning', variant: 'warning',
title: s__('Runners|Stale'), title: I18N_STATUS_STALE,
metaIcon: 'time-out', metaIcon: 'time-out',
}, },
}, },

View File

@ -23,6 +23,12 @@ export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
); );
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects'); export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
// Status
export const I18N_STATUS_ONLINE = s__('Runners|Online');
export const I18N_STATUS_NEVER_CONTACTED = s__('Runners|Never contacted');
export const I18N_STATUS_OFFLINE = s__('Runners|Offline');
export const I18N_STATUS_STALE = s__('Runners|Stale');
// Status help popover // Status help popover
export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses'); export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
@ -62,6 +68,7 @@ export const I18N_STALE_NEVER_CONTACTED_TOOLTIP = s__(
export const I18N_EDIT = __('Edit'); export const I18N_EDIT = __('Edit');
export const I18N_PAUSE = __('Pause'); export const I18N_PAUSE = __('Pause');
export const I18N_PAUSED = s__('Runners|Paused');
export const I18N_PAUSE_TOOLTIP = s__('Runners|Pause from accepting jobs'); export const I18N_PAUSE_TOOLTIP = s__('Runners|Pause from accepting jobs');
export const I18N_PAUSED_DESCRIPTION = s__('Runners|Not accepting jobs'); export const I18N_PAUSED_DESCRIPTION = s__('Runners|Not accepting jobs');
@ -94,7 +101,7 @@ export const I18N_NO_JOBS_FOUND = s__('Runners|This runner has not run any jobs.
// Styles // Styles
export const RUNNER_TAG_BADGE_VARIANT = 'neutral'; export const RUNNER_TAG_BADGE_VARIANT = 'info';
export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100'; export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100';
// Filtered search parameter names // Filtered search parameter names

View File

@ -16,7 +16,7 @@ export default {
<template> <template>
<copyable-field <copyable-field
data-qa-selector="copy-forward-email" data-testid="copy-forward-email"
:name="s__('RightSidebar|Issue email')" :name="s__('RightSidebar|Issue email')"
:clipboard-tooltip-text="s__('RightSidebar|Copy email address')" :clipboard-tooltip-text="s__('RightSidebar|Copy email address')"
:value="issueEmailAddress" :value="issueEmailAddress"

View File

@ -1,2 +1,2 @@
.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json } } .js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json } }
= render_wiki_content(@page) = render_wiki_content(@page)

View File

@ -9,6 +9,6 @@
.gl-mt-5.gl-mb-3 .gl-mt-5.gl-mb-3
.gl-display-flex.gl-justify-content-space-between .gl-display-flex.gl-justify-content-space-between
%h2.gl-mt-0.gl-mb-5{ data: { qa_selector: 'wiki_page_title', testid: 'wiki_page_title' } }= @page ? @page.human_title : _('Failed to retrieve page') %h2.gl-mt-0.gl-mb-5{ data: { qa_selector: 'wiki_page_title', testid: 'wiki_page_title' } }= @page ? @page.human_title : _('Failed to retrieve page')
.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content' } } .js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content' } }
= _('The page could not be displayed because it timed out.') = _('The page could not be displayed because it timed out.')
= html_escape(_('You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}')) % { linkStart: "<a href=\"#{git_access_url}\">".html_safe, linkEnd: '</a>'.html_safe, cloneIcon: sprite_icon('download', css_class: 'gl-mr-2').html_safe } = html_escape(_('You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}')) % { linkStart: "<a href=\"#{git_access_url}\">".html_safe, linkEnd: '</a>'.html_safe, cloneIcon: sprite_icon('download', css_class: 'gl-mr-2').html_safe }

View File

@ -27,6 +27,6 @@
- if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding - if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding
= link_to sprite_icon('pencil', css_class: 'gl-icon'), wiki_page_path(@wiki, @page, action: :edit), title: 'Edit', role: "button", class: 'btn gl-button btn-icon btn-default js-wiki-edit', data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' } = link_to sprite_icon('pencil', css_class: 'gl-icon'), wiki_page_path(@wiki, @page, action: :edit), title: 'Edit', role: "button", class: 'btn gl-button btn-icon btn-default js-wiki-edit', data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' }
.js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } } .js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
= render 'shared/wikis/sidebar' = render 'shared/wikis/sidebar'

View File

@ -2,7 +2,7 @@
name: cache_issue_sums name: cache_issue_sums
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95048 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95048
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365940 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365940
milestone: '15.3' milestone: '15.4'
type: development type: development
group: group::product planning group: group::product planning
default_enabled: false default_enabled: false

View File

@ -81,7 +81,6 @@ module Gitlab
# @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry # @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
# @return [Array<String>] # @return [Array<String>]
def cached_collection(collection, presenter:, presenter_args:, context:, expires_in:) def cached_collection(collection, presenter:, presenter_args:, context:, expires_in:)
total_count = collection.size
misses = 0 misses = 0
json = fetch_multi(presenter, collection, context: context, expires_in: expires_in) do |obj| json = fetch_multi(presenter, collection, context: context, expires_in: expires_in) do |obj|
@ -92,7 +91,7 @@ module Gitlab
end end
end end
increment_cache_metric(render_type: :collection, total_count: total_count, miss_count: misses) increment_cache_metric(render_type: :collection, total_count: collection.length, miss_count: misses)
json.values json.values
end end

View File

@ -34251,15 +34251,6 @@ msgstr ""
msgid "Runners|group" msgid "Runners|group"
msgstr "" msgstr ""
msgid "Runners|never contacted"
msgstr ""
msgid "Runners|offline"
msgstr ""
msgid "Runners|online"
msgstr ""
msgid "Runners|paused" msgid "Runners|paused"
msgstr "" msgstr ""
@ -34269,15 +34260,6 @@ msgstr ""
msgid "Runners|specific" msgid "Runners|specific"
msgstr "" msgstr ""
msgid "Runners|stale"
msgstr ""
msgid "Runners|upgrade available"
msgstr ""
msgid "Runners|upgrade recommended"
msgstr ""
msgid "Runner|Owner" msgid "Runner|Owner"
msgstr "" msgstr ""

View File

@ -9,7 +9,7 @@ module QA
include Page::Component::MembersFilter include Page::Component::MembersFilter
view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
element :remove_member_modal_content element :remove_member_modal
end end
view 'app/assets/javascripts/pages/groups/group_members/index.js' do view 'app/assets/javascripts/pages/groups/group_members/index.js' do
@ -45,7 +45,7 @@ module QA
click_element :delete_member_button click_element :delete_member_button
end end
within_element(:remove_member_modal_content) do within_element(:remove_member_modal) do
click_button("Remove member") click_button("Remove member")
end end
end end

View File

@ -10,7 +10,7 @@ module QA
LOADING_MESSAGE = 'Waiting for performance data' LOADING_MESSAGE = 'Waiting for performance data'
view 'app/assets/javascripts/monitoring/components/dashboard.vue' do view 'app/assets/javascripts/monitoring/components/dashboard.vue' do
element :prometheus_graphs element :prometheus_graphs_content
end end
view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
@ -54,7 +54,7 @@ module QA
end end
def has_metrics? def has_metrics?
within_element :prometheus_graphs do within_element :prometheus_graphs_content do
has_text?(EXPECTED_TITLE) has_text?(EXPECTED_TITLE)
end end
end end
@ -102,7 +102,7 @@ module QA
end end
def has_custom_metric?(metric) def has_custom_metric?(metric)
within_element :prometheus_graphs do within_element :prometheus_graphs_content do
has_text?(metric) has_text?(metric)
end end
end end
@ -114,7 +114,7 @@ module QA
end end
def has_template_metric?(metric) def has_template_metric?(metric)
within_element :prometheus_graphs do within_element :prometheus_graphs_content do
has_text?(metric) has_text?(metric)
end end
end end

View File

@ -6,12 +6,10 @@ module QA
module Packages module Packages
class Index < QA::Page::Base class Index < QA::Page::Base
view 'app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue' do view 'app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue' do
element :package_row
element :package_link element :package_link
end end
view 'app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue' do view 'app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue' do
element :package_row
element :package_link element :package_link
end end

View File

@ -263,22 +263,6 @@ module QA
ENV['GITLAB_QA_PASSWORD_6'] ENV['GITLAB_QA_PASSWORD_6']
end end
def gitlab_qa_1p_email
ENV['GITLAB_QA_1P_EMAIL']
end
def gitlab_qa_1p_password
ENV['GITLAB_QA_1P_PASSWORD']
end
def gitlab_qa_1p_secret
ENV['GITLAB_QA_1P_SECRET']
end
def gitlab_qa_1p_github_uuid
ENV['GITLAB_QA_1P_GITHUB_UUID']
end
def jira_admin_username def jira_admin_username
ENV['JIRA_ADMIN_USERNAME'] ENV['JIRA_ADMIN_USERNAME']
end end

View File

@ -50,7 +50,7 @@ RSpec.describe "Admin Runners" do
it 'shows an instance badge' do it 'shows an instance badge' do
within_runner_row(instance_runner.id) do within_runner_row(instance_runner.id) do
expect(page).to have_selector '.badge', text: 'shared' expect(page).to have_selector '.badge', text: 'Instance'
end end
end end
end end
@ -66,9 +66,9 @@ RSpec.describe "Admin Runners" do
it 'has all necessary texts' do it 'has all necessary texts' do
expect(page).to have_text "Register an instance runner" expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Online 1" expect(page).to have_text "#{s_('Runners|Online')} 1"
expect(page).to have_text "Offline 2" expect(page).to have_text "#{s_('Runners|Offline')} 2"
expect(page).to have_text "Stale 1" expect(page).to have_text "#{s_('Runners|Stale')} 1"
end end
end end
@ -145,7 +145,7 @@ RSpec.describe "Admin Runners" do
end end
it 'shows paused runners' do it 'shows paused runners' do
input_filtered_search_filter_is_only('Paused', 'Yes') input_filtered_search_filter_is_only(s_('Runners|Paused'), 'Yes')
expect(page).to have_link('All 1') expect(page).to have_link('All 1')
@ -154,7 +154,7 @@ RSpec.describe "Admin Runners" do
end end
it 'shows active runners' do it 'shows active runners' do
input_filtered_search_filter_is_only('Paused', 'No') input_filtered_search_filter_is_only(s_('Runners|Paused'), 'No')
expect(page).to have_link('All 1') expect(page).to have_link('All 1')
@ -186,7 +186,7 @@ RSpec.describe "Admin Runners" do
end end
it 'shows correct runner when status matches' do it 'shows correct runner when status matches' do
input_filtered_search_filter_is_only('Status', 'Online') input_filtered_search_filter_is_only('Status', s_('Runners|Online'))
expect(page).to have_link('All 2') expect(page).to have_link('All 2')
@ -197,7 +197,7 @@ RSpec.describe "Admin Runners" do
end end
it 'shows correct runner when status is selected and search term is entered' do it 'shows correct runner when status is selected and search term is entered' do
input_filtered_search_filter_is_only('Status', 'Online') input_filtered_search_filter_is_only('Status', s_('Runners|Online'))
input_filtered_search_keys('runner-1') input_filtered_search_keys('runner-1')
expect(page).to have_link('All 1') expect(page).to have_link('All 1')
@ -220,7 +220,7 @@ RSpec.describe "Admin Runners" do
expect(page).to have_content 'runner-never-contacted' expect(page).to have_content 'runner-never-contacted'
within_runner_row(never_contacted.id) do within_runner_row(never_contacted.id) do
expect(page).to have_selector '.badge', text: 'never contacted' expect(page).to have_selector '.badge', text: s_('Runners|Never contacted')
end end
end end
@ -308,7 +308,7 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path visit admin_runners_path
input_filtered_search_filter_is_only('Paused', 'No') input_filtered_search_filter_is_only(s_('Runners|Paused'), 'No')
expect(page).to have_content 'runner-project' expect(page).to have_content 'runner-project'
expect(page).to have_content 'runner-group' expect(page).to have_content 'runner-group'
@ -535,6 +535,36 @@ RSpec.describe "Admin Runners" do
let(:runner_page_path) { admin_runner_path(project_runner) } let(:runner_page_path) { admin_runner_path(project_runner) }
end end
describe 'breadcrumbs' do
it 'contains the current runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do
expect(page).to have_link("##{project_runner.id} (#{project_runner.short_sha})")
expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
end
end
end
describe 'runner header', :js do
it 'contains the runner status, type and id' do
expect(page).to have_content("#{s_('Runners|Never contacted')} Project Runner ##{project_runner.id} created")
end
end
context 'when a runner is updated', :js do
before do
click_on _('Save changes')
wait_for_requests
end
it 'show success alert' do
expect(page.find('[data-testid="alert-success"]')).to have_content('saved')
end
it 'redirects to runner page' do
expect(current_url).to match(admin_runner_path(project_runner))
end
end
describe 'projects' do describe 'projects' do
it 'contains project names' do it 'contains project names' do
expect(page).to have_content(project1.full_name) expect(page).to have_content(project1.full_name)

View File

@ -3,14 +3,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Commit > User view commits' do RSpec.describe 'Commit > User view commits' do
let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:user) { create(:user) }
let_it_be(:user) { project.creator } let_it_be(:group) { create(:group, :public) }
before do shared_examples 'can view commits' do
visit project_commits_path(project)
end
describe 'Commits List' do
it 'displays the correct number of commits per day in the header' do it 'displays the correct number of commits per day in the header' do
expect(first('.js-commit-header').find('.commits-count').text).to eq('1 commit') expect(first('.js-commit-header').find('.commits-count').text).to eq('1 commit')
end end
@ -19,4 +15,51 @@ RSpec.describe 'Commit > User view commits' do
expect(page).to have_selector('#commits-list > li:nth-child(2) > ul', count: 1) expect(page).to have_selector('#commits-list > li:nth-child(2) > ul', count: 1)
end end
end end
describe 'Commits List' do
context 'when project is public' do
let(:project) { create(:project, :public, :repository, group: group) }
before do
visit project_commits_path(project)
end
it_behaves_like 'can view commits'
end
context 'when project is public with private repository' do
let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
visit project_commits_path(project)
end
it_behaves_like 'can view commits'
end
end
end
context 'when project is private' do
let(:project) { create(:project, :private, :repository, group: group) }
context 'and user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
visit project_commits_path(project)
end
it 'renders not found' do
expect(page).to have_title('Not Found')
expect(page).to have_content('Page Not Found')
end
end
end
end
end
end end

View File

@ -61,7 +61,7 @@ RSpec.describe "Group Runners" do
it 'shows a group badge' do it 'shows a group badge' do
within_runner_row(group_runner.id) do within_runner_row(group_runner.id) do
expect(page).to have_selector '.badge', text: 'group' expect(page).to have_selector '.badge', text: s_('Runners|Group')
end end
end end
@ -101,9 +101,9 @@ RSpec.describe "Group Runners" do
let(:runner) { project_runner } let(:runner) { project_runner }
end end
it 'shows a project (specific) badge' do it 'shows a project badge' do
within_runner_row(project_runner.id) do within_runner_row(project_runner.id) do
expect(page).to have_selector '.badge', text: 'specific' expect(page).to have_selector '.badge', text: s_('Runners|Project')
end end
end end
@ -137,7 +137,7 @@ RSpec.describe "Group Runners" do
focus_filtered_search focus_filtered_search
page.within(search_bar_selector) do page.within(search_bar_selector) do
expect(page).to have_link('Paused') expect(page).to have_link(s_('Runners|Paused'))
expect(page).to have_content('Status') expect(page).to have_content('Status')
end end
end end

View File

@ -39,7 +39,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
context 'resolving the thread' do context 'resolving the thread' do
before do before do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
it 'hides the link for creating a new issue' do it 'hides the link for creating a new issue' do

View File

@ -35,7 +35,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
context 'resolving the thread' do context 'resolving the thread' do
before do before do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
it 'hides the link for creating a new issue' do it 'hides the link for creating a new issue' do

View File

@ -302,7 +302,9 @@ RSpec.describe 'Issue Sidebar' do
context 'sidebar', :js do context 'sidebar', :js do
it 'finds issue copy forwarding email' do it 'finds issue copy forwarding email' do
expect(find('[data-qa-selector="copy-forward-email"]').text).to eq "Issue email: #{issue.creatable_note_email_address(user)}" # rubocop:disable QA/SelectorUsage expect(
find('[data-testid="copy-forward-email"]').text
).to eq "Issue email: #{issue.creatable_note_email_address(user)}"
end end
end end
@ -338,7 +340,7 @@ RSpec.describe 'Issue Sidebar' do
end end
it 'does not find issue email' do it 'does not find issue email' do
expect(page).not_to have_selector('[data-qa-selector="copy-forward-email"]') # rubocop:disable QA/SelectorUsage expect(page).not_to have_selector('[data-testid="copy-forward-email"]')
end end
end end
end end

View File

@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark thread as resolved' do it 'allows user to mark thread as resolved' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
expect(page).to have_selector('.discussion-body', visible: false) expect(page).to have_selector('.discussion-body', visible: false)
@ -82,7 +82,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to unresolve thread' do it 'allows user to unresolve thread' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
click_button 'Unresolve thread' click_button 'Unresolve thread'
end end
@ -94,7 +94,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
describe 'resolved thread' do describe 'resolved thread' do
before do before do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
visit_merge_request visit_merge_request
@ -194,7 +194,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to resolve from reply form without a comment' do it 'allows user to resolve from reply form without a comment' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
page.within '.discussions-counter' do page.within '.discussions-counter' do
@ -229,7 +229,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'hides jump to next button when all resolved' do it 'hides jump to next button when all resolved' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
expect(page).to have_selector('.discussion-next-btn', visible: false) expect(page).to have_selector('.discussion-next-btn', visible: false)
@ -324,7 +324,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark all threads as resolved' do it 'allows user to mark all threads as resolved' do
page.all('.discussion-reply-holder', count: 2).each do |reply_holder| page.all('.discussion-reply-holder', count: 2).each do |reply_holder|
page.within reply_holder do page.within reply_holder do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
end end
@ -335,7 +335,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to quickly scroll to next unresolved thread' do it 'allows user to quickly scroll to next unresolved thread' do
page.within('.discussion-reply-holder', match: :first) do page.within('.discussion-reply-holder', match: :first) do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
page.within '.discussions-counter' do page.within '.discussions-counter' do
@ -406,7 +406,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark thread as resolved' do it 'allows user to mark thread as resolved' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
end end
page.within '.diff-content .note' do page.within '.diff-content .note' do
@ -420,7 +420,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to unresolve thread' do it 'allows user to unresolve thread' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
click_button 'Unresolve thread' click_button 'Unresolve thread'
end end
@ -447,7 +447,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to comment & unresolve thread' do it 'allows user to comment & unresolve thread' do
page.within '.diff-content' do page.within '.diff-content' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
find_field('Reply…').click find_field('Reply…').click

View File

@ -30,9 +30,9 @@ RSpec.describe 'Environment > Metrics' do
click_link 'Monitoring' click_link 'Monitoring'
expect(page).to have_current_path(project_metrics_dashboard_path(project, environment: environment.id)) expect(page).to have_current_path(project_metrics_dashboard_path(project, environment: environment.id))
expect(page).to have_css('[data-qa-selector="environments_dropdown"]') # rubocop:disable QA/SelectorUsage expect(page).to have_css('[data-testid="environments-dropdown"]')
within('[data-qa-selector="environments_dropdown"]') do # rubocop:disable QA/SelectorUsage within('[data-testid="environments-dropdown"]') do
# Click on the dropdown # Click on the dropdown
click_on(environment.name) click_on(environment.name)
@ -59,7 +59,7 @@ RSpec.describe 'Environment > Metrics' do
visit_environment(environment) visit_environment(environment)
click_link 'Monitoring' click_link 'Monitoring'
expect(page).to have_css('[data-qa-selector="prometheus_graphs"]') # rubocop:disable QA/SelectorUsage expect(page).to have_css('[data-testid="prometheus-graphs"]')
end end
it_behaves_like 'has environment selector' it_behaves_like 'has environment selector'

View File

@ -90,7 +90,7 @@ RSpec.describe 'User sees feature flag list', :js do
it 'shows the empty page' do it 'shows the empty page' do
expect(page).to have_text 'Get started with feature flags' expect(page).to have_text 'Get started with feature flags'
expect(page).to have_selector('.btn-confirm', text: 'New feature flag') expect(page).to have_selector('.btn-confirm', text: 'New feature flag')
expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure') # rubocop:disable QA/SelectorUsage expect(page).to have_selector('[data-testid="ff-configure-button"]', text: 'Configure')
end end
end end
end end

View File

@ -26,7 +26,7 @@ RSpec.describe 'Projects tree', :js do
expect(page).to have_selector('.tree-item') expect(page).to have_selector('.tree-item')
expect(page).to have_content('add tests for .gitattributes custom highlighting') expect(page).to have_content('add tests for .gitattributes custom highlighting')
expect(page).not_to have_selector('[data-testid="alert-danger"]') expect(page).not_to have_selector('[data-testid="alert-danger"]')
expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage expect(page).not_to have_selector('[data-testid="label-lfs"]', text: 'LFS')
end end
it 'renders tree table for a subtree without errors' do it 'renders tree table for a subtree without errors' do
@ -35,7 +35,7 @@ RSpec.describe 'Projects tree', :js do
expect(page).to have_selector('.tree-item') expect(page).to have_selector('.tree-item')
expect(page).to have_content('add spaces in whitespace file') expect(page).to have_content('add spaces in whitespace file')
expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage expect(page).not_to have_selector('[data-testid="label-lfs"]', text: 'LFS')
expect(page).not_to have_selector('[data-testid="alert-danger"]') expect(page).not_to have_selector('[data-testid="alert-danger"]')
end end
@ -112,7 +112,7 @@ RSpec.describe 'Projects tree', :js do
it 'renders LFS badge on blob item' do it 'renders LFS badge on blob item' do
visit project_tree_path(project, File.join('master', 'files/lfs')) visit project_tree_path(project, File.join('master', 'files/lfs'))
expect(page).to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage expect(page).to have_selector('[data-testid="label-lfs"]', text: 'LFS')
end end
end end

View File

@ -3,7 +3,8 @@
exports[`Dashboard template matches the default snapshot 1`] = ` exports[`Dashboard template matches the default snapshot 1`] = `
<div <div
class="prometheus-graphs" class="prometheus-graphs"
data-qa-selector="prometheus_graphs" data-qa-selector="prometheus_graphs_content"
data-testid="prometheus-graphs"
environmentstate="available" environmentstate="available"
metricsdashboardbasepath="/monitoring/monitor-project/-/metrics?environment=1" metricsdashboardbasepath="/monitoring/monitor-project/-/metrics?environment=1"
metricsendpoint="/monitoring/monitor-project/-/environments/1/additional_metrics.json" metricsendpoint="/monitoring/monitor-project/-/environments/1/additional_metrics.json"
@ -60,6 +61,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
clearalltext="Clear all" clearalltext="Clear all"
clearalltextclass="gl-px-5" clearalltextclass="gl-px-5"
data-qa-selector="environments_dropdown" data-qa-selector="environments_dropdown"
data-testid="environments-dropdown"
headertext="" headertext=""
hideheaderborder="true" hideheaderborder="true"
highlighteditemstitle="Selected" highlighteditemstitle="Selected"

View File

@ -3,7 +3,7 @@
exports[`packages_list_row renders 1`] = ` exports[`packages_list_row renders 1`] = `
<div <div
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100" class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
data-qa-selector="package_row" data-testid="package-row"
> >
<div <div
class="gl-display-flex gl-align-items-center gl-py-3" class="gl-display-flex gl-align-items-center gl-py-3"

View File

@ -3,7 +3,7 @@
exports[`packages_list_row renders 1`] = ` exports[`packages_list_row renders 1`] = `
<div <div
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100" class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
data-qa-selector="package_row" data-testid="package-row"
> >
<div <div
class="gl-display-flex gl-align-items-center gl-py-3" class="gl-display-flex gl-align-items-center gl-py-3"

View File

@ -38,7 +38,7 @@ describe('pages/shared/wikis/components/wiki_content', () => {
const findGlAlert = () => wrapper.findComponent(GlAlert); const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); const findGlSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findContent = () => wrapper.find('[data-testid="wiki_page_content"]'); const findContent = () => wrapper.find('[data-testid="wiki-page-content"]');
describe('when loading content', () => { describe('when loading content', () => {
beforeEach(() => { beforeEach(() => {

View File

@ -33,6 +33,12 @@ import {
CREATED_ASC, CREATED_ASC,
CREATED_DESC, CREATED_DESC,
DEFAULT_SORT, DEFAULT_SORT,
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
I18N_INSTANCE_TYPE,
I18N_GROUP_TYPE,
I18N_PROJECT_TYPE,
INSTANCE_TYPE, INSTANCE_TYPE,
PARAM_KEY_PAUSED, PARAM_KEY_PAUSED,
PARAM_KEY_STATUS, PARAM_KEY_STATUS,
@ -156,15 +162,16 @@ describe('AdminRunnersApp', () => {
}); });
it('shows the runner tabs', () => { it('shows the runner tabs', () => {
expect(findRunnerTypeTabs().text()).toMatchInterpolatedText( const tabs = findRunnerTypeTabs().text();
`All ${mockRunnersCount} Instance ${mockRunnersCount} Group ${mockRunnersCount} Project ${mockRunnersCount}`, expect(tabs).toMatchInterpolatedText(
`All ${mockRunnersCount} ${I18N_INSTANCE_TYPE} ${mockRunnersCount} ${I18N_GROUP_TYPE} ${mockRunnersCount} ${I18N_PROJECT_TYPE} ${mockRunnersCount}`,
); );
}); });
it('shows the total', () => { it('shows the total', () => {
expect(findRunnerStats().text()).toContain(`${s__('Runners|Online')} ${mockRunnersCount}`); expect(findRunnerStats().text()).toContain(`${I18N_STATUS_ONLINE} ${mockRunnersCount}`);
expect(findRunnerStats().text()).toContain(`${s__('Runners|Offline')} ${mockRunnersCount}`); expect(findRunnerStats().text()).toContain(`${I18N_STATUS_OFFLINE} ${mockRunnersCount}`);
expect(findRunnerStats().text()).toContain(`${s__('Runners|Stale')} ${mockRunnersCount}`); expect(findRunnerStats().text()).toContain(`${I18N_STATUS_STALE} ${mockRunnersCount}`);
}); });
}); });

View File

@ -6,7 +6,7 @@ import RunnerTags from '~/runner/components/runner_tags.vue';
import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue'; import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants'; import { INSTANCE_TYPE, I18N_INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants';
import { allRunnersData } from '../../mock_data'; import { allRunnersData } from '../../mock_data';
@ -69,7 +69,7 @@ describe('RunnerTypeCell', () => {
locked: true, locked: true,
}); });
expect(wrapper.text()).toContain('shared'); expect(wrapper.text()).toContain(I18N_INSTANCE_TYPE);
}); });
it('Displays the runner version', () => { it('Displays the runner version', () => {

View File

@ -3,7 +3,14 @@ import RunnerStatusCell from '~/runner/components/cells/runner_status_cell.vue';
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue'; import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
import RunnerPausedBadge from '~/runner/components/runner_paused_badge.vue'; import RunnerPausedBadge from '~/runner/components/runner_paused_badge.vue';
import { INSTANCE_TYPE, STATUS_ONLINE, STATUS_OFFLINE } from '~/runner/constants'; import {
I18N_PAUSED,
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
INSTANCE_TYPE,
STATUS_ONLINE,
STATUS_OFFLINE,
} from '~/runner/constants';
describe('RunnerStatusCell', () => { describe('RunnerStatusCell', () => {
let wrapper; let wrapper;
@ -31,8 +38,8 @@ describe('RunnerStatusCell', () => {
it('Displays online status', () => { it('Displays online status', () => {
createComponent(); createComponent();
expect(wrapper.text()).toMatchInterpolatedText('online'); expect(wrapper.text()).toContain(I18N_STATUS_ONLINE);
expect(findStatusBadge().text()).toBe('online'); expect(findStatusBadge().text()).toBe(I18N_STATUS_ONLINE);
}); });
it('Displays offline status', () => { it('Displays offline status', () => {
@ -42,8 +49,8 @@ describe('RunnerStatusCell', () => {
}, },
}); });
expect(wrapper.text()).toMatchInterpolatedText('offline'); expect(wrapper.text()).toMatchInterpolatedText(I18N_STATUS_OFFLINE);
expect(findStatusBadge().text()).toBe('offline'); expect(findStatusBadge().text()).toBe(I18N_STATUS_OFFLINE);
}); });
it('Displays paused status', () => { it('Displays paused status', () => {
@ -54,8 +61,8 @@ describe('RunnerStatusCell', () => {
}, },
}); });
expect(wrapper.text()).toMatchInterpolatedText('online paused'); expect(wrapper.text()).toMatchInterpolatedText(`${I18N_STATUS_ONLINE} ${I18N_PAUSED}`);
expect(findPausedBadge().text()).toBe('paused'); expect(findPausedBadge().text()).toBe(I18N_PAUSED);
}); });
it('Is empty when data is missing', () => { it('Is empty when data is missing', () => {

View File

@ -2,7 +2,7 @@ import { __ } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import RunnerSummaryCell from '~/runner/components/cells/runner_summary_cell.vue'; import RunnerSummaryCell from '~/runner/components/cells/runner_summary_cell.vue';
import RunnerTags from '~/runner/components/runner_tags.vue'; import RunnerTags from '~/runner/components/runner_tags.vue';
import { INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants'; import { INSTANCE_TYPE, I18N_INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants';
const mockId = '1'; const mockId = '1';
const mockShortSha = '2P6oDVDm'; const mockShortSha = '2P6oDVDm';
@ -46,7 +46,7 @@ describe('RunnerTypeCell', () => {
}); });
it('Displays the runner type', () => { it('Displays the runner type', () => {
expect(wrapper.text()).toContain('shared'); expect(wrapper.text()).toContain(I18N_INSTANCE_TYPE);
}); });
it('Does not display the locked icon', () => { it('Does not display the locked icon', () => {

View File

@ -1,6 +1,6 @@
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants'; import { I18N_STATUS_ONLINE, I18N_GROUP_TYPE, GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants'; import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
@ -49,7 +49,7 @@ describe('RunnerHeader', () => {
}, },
}); });
expect(findRunnerStatusBadge().text()).toContain('online'); expect(findRunnerStatusBadge().text()).toContain(I18N_STATUS_ONLINE);
}); });
it('displays the runner type', () => { it('displays the runner type', () => {
@ -60,7 +60,7 @@ describe('RunnerHeader', () => {
}, },
}); });
expect(findRunnerTypeBadge().text()).toContain('group'); expect(findRunnerTypeBadge().text()).toContain(I18N_GROUP_TYPE);
}); });
it('displays the runner id', () => { it('displays the runner id', () => {

View File

@ -7,6 +7,7 @@ import {
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerList from '~/runner/components/runner_list.vue'; import RunnerList from '~/runner/components/runner_list.vue';
import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue'; import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue';
import { I18N_PROJECT_TYPE, I18N_STATUS_NEVER_CONTACTED } from '~/runner/constants';
import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data'; import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
const mockRunners = allRunnersData.data.runners.nodes; const mockRunners = allRunnersData.data.runners.nodes;
@ -91,7 +92,7 @@ describe('RunnerList', () => {
createComponent({}, mountExtended); createComponent({}, mountExtended);
// Badges // Badges
expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText('never contacted'); expect(findCell({ fieldKey: 'status' }).text()).toBe(I18N_STATUS_NEVER_CONTACTED);
// Runner summary // Runner summary
expect(findCell({ fieldKey: 'summary' }).text()).toContain( expect(findCell({ fieldKey: 'summary' }).text()).toContain(
@ -262,13 +263,15 @@ describe('RunnerList', () => {
const numericId = getIdFromGraphQLId(id); const numericId = getIdFromGraphQLId(id);
// Badges // Badges
expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText('never contacted'); expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
I18N_STATUS_NEVER_CONTACTED,
);
// Runner summary // Runner summary
const summary = findCell({ fieldKey: 'summary' }).text(); const summary = findCell({ fieldKey: 'summary' }).text();
expect(summary).toContain(`#${numericId} (${shortSha})`); expect(summary).toContain(`#${numericId} (${shortSha})`);
expect(summary).toContain('specific'); expect(summary).toContain(I18N_PROJECT_TYPE);
expect(summary).toContain(version); expect(summary).toContain(version);
expect(summary).toContain(description); expect(summary).toContain(description);

View File

@ -2,6 +2,7 @@ import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import RunnerStatePausedBadge from '~/runner/components/runner_paused_badge.vue'; import RunnerStatePausedBadge from '~/runner/components/runner_paused_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { I18N_PAUSED } from '~/runner/constants';
describe('RunnerTypeBadge', () => { describe('RunnerTypeBadge', () => {
let wrapper; let wrapper;
@ -29,8 +30,8 @@ describe('RunnerTypeBadge', () => {
}); });
it('renders paused state', () => { it('renders paused state', () => {
expect(wrapper.text()).toBe('paused'); expect(wrapper.text()).toBe(I18N_PAUSED);
expect(findBadge().props('variant')).toBe('danger'); expect(findBadge().props('variant')).toBe('warning');
}); });
it('renders tooltip', () => { it('renders tooltip', () => {

View File

@ -3,12 +3,16 @@ import { shallowMount } from '@vue/test-utils';
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue'; import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { import {
I18N_STATUS_ONLINE,
I18N_STATUS_NEVER_CONTACTED,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
I18N_NEVER_CONTACTED_TOOLTIP,
I18N_STALE_NEVER_CONTACTED_TOOLTIP,
STATUS_ONLINE, STATUS_ONLINE,
STATUS_OFFLINE, STATUS_OFFLINE,
STATUS_STALE, STATUS_STALE,
STATUS_NEVER_CONTACTED, STATUS_NEVER_CONTACTED,
I18N_NEVER_CONTACTED_TOOLTIP,
I18N_STALE_NEVER_CONTACTED_TOOLTIP,
} from '~/runner/constants'; } from '~/runner/constants';
describe('RunnerTypeBadge', () => { describe('RunnerTypeBadge', () => {
@ -46,7 +50,7 @@ describe('RunnerTypeBadge', () => {
it('renders online state', () => { it('renders online state', () => {
createComponent(); createComponent();
expect(wrapper.text()).toBe('online'); expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
expect(findBadge().props('variant')).toBe('success'); expect(findBadge().props('variant')).toBe('success');
expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago'); expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago');
}); });
@ -59,7 +63,7 @@ describe('RunnerTypeBadge', () => {
}, },
}); });
expect(wrapper.text()).toBe('never contacted'); expect(wrapper.text()).toBe(I18N_STATUS_NEVER_CONTACTED);
expect(findBadge().props('variant')).toBe('muted'); expect(findBadge().props('variant')).toBe('muted');
expect(getTooltip().value).toBe(I18N_NEVER_CONTACTED_TOOLTIP); expect(getTooltip().value).toBe(I18N_NEVER_CONTACTED_TOOLTIP);
}); });
@ -72,7 +76,7 @@ describe('RunnerTypeBadge', () => {
}, },
}); });
expect(wrapper.text()).toBe('offline'); expect(wrapper.text()).toBe(I18N_STATUS_OFFLINE);
expect(findBadge().props('variant')).toBe('muted'); expect(findBadge().props('variant')).toBe('muted');
expect(getTooltip().value).toBe('Runner is offline; last contact was 1 day ago'); expect(getTooltip().value).toBe('Runner is offline; last contact was 1 day ago');
}); });
@ -85,7 +89,7 @@ describe('RunnerTypeBadge', () => {
}, },
}); });
expect(wrapper.text()).toBe('stale'); expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning'); expect(findBadge().props('variant')).toBe('warning');
expect(getTooltip().value).toBe('Runner is stale; last contact was 1 year ago'); expect(getTooltip().value).toBe('Runner is stale; last contact was 1 year ago');
}); });
@ -98,7 +102,7 @@ describe('RunnerTypeBadge', () => {
}, },
}); });
expect(wrapper.text()).toBe('stale'); expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning'); expect(findBadge().props('variant')).toBe('warning');
expect(getTooltip().value).toBe(I18N_STALE_NEVER_CONTACTED_TOOLTIP); expect(getTooltip().value).toBe(I18N_STALE_NEVER_CONTACTED_TOOLTIP);
}); });
@ -112,7 +116,7 @@ describe('RunnerTypeBadge', () => {
}, },
}); });
expect(wrapper.text()).toBe('online'); expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
expect(getTooltip().value).toBe('Runner is online; last contact was never'); expect(getTooltip().value).toBe('Runner is online; last contact was never');
}); });

View File

@ -1,6 +1,8 @@
import { GlBadge } from '@gitlab/ui'; import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { RUNNER_TAG_BADGE_VARIANT } from '~/runner/constants';
import RunnerTag from '~/runner/components/runner_tag.vue'; import RunnerTag from '~/runner/components/runner_tag.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
@ -48,7 +50,7 @@ describe('RunnerTag', () => {
it('Displays tags with correct style', () => { it('Displays tags with correct style', () => {
expect(findBadge().props()).toMatchObject({ expect(findBadge().props()).toMatchObject({
size: 'sm', size: 'sm',
variant: 'neutral', variant: RUNNER_TAG_BADGE_VARIANT,
}); });
}); });

View File

@ -34,7 +34,6 @@ describe('RunnerTags', () => {
it('Displays tags with correct style', () => { it('Displays tags with correct style', () => {
expect(findBadge().props('size')).toBe('sm'); expect(findBadge().props('size')).toBe('sm');
expect(findBadge().props('variant')).toBe('neutral');
}); });
it('Displays tags with md size', () => { it('Displays tags with md size', () => {

View File

@ -2,7 +2,14 @@ import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue'; import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants'; import {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
I18N_INSTANCE_TYPE,
I18N_GROUP_TYPE,
I18N_PROJECT_TYPE,
} from '~/runner/constants';
describe('RunnerTypeBadge', () => { describe('RunnerTypeBadge', () => {
let wrapper; let wrapper;
@ -27,9 +34,9 @@ describe('RunnerTypeBadge', () => {
describe.each` describe.each`
type | text type | text
${INSTANCE_TYPE} | ${'shared'} ${INSTANCE_TYPE} | ${I18N_INSTANCE_TYPE}
${GROUP_TYPE} | ${'group'} ${GROUP_TYPE} | ${I18N_GROUP_TYPE}
${PROJECT_TYPE} | ${'specific'} ${PROJECT_TYPE} | ${I18N_PROJECT_TYPE}
`('displays $type runner', ({ type, text }) => { `('displays $type runner', ({ type, text }) => {
beforeEach(() => { beforeEach(() => {
createComponent({ props: { type } }); createComponent({ props: { type } });
@ -37,7 +44,7 @@ describe('RunnerTypeBadge', () => {
it(`as "${text}" with an "info" variant`, () => { it(`as "${text}" with an "info" variant`, () => {
expect(findBadge().text()).toBe(text); expect(findBadge().text()).toBe(text);
expect(findBadge().props('variant')).toBe('info'); expect(findBadge().props('variant')).toBe('muted');
}); });
it('with a tooltip', () => { it('with a tooltip', () => {

View File

@ -1,8 +1,15 @@
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { s__ } from '~/locale';
import RunnerStats from '~/runner/components/stat/runner_stats.vue'; import RunnerStats from '~/runner/components/stat/runner_stats.vue';
import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue'; import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
import { INSTANCE_TYPE, STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '~/runner/constants'; import {
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
INSTANCE_TYPE,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
} from '~/runner/constants';
describe('RunnerStats', () => { describe('RunnerStats', () => {
let wrapper; let wrapper;
@ -46,9 +53,9 @@ describe('RunnerStats', () => {
}); });
const text = wrapper.text(); const text = wrapper.text();
expect(text).toContain(`${s__('Runners|Online')} 3`); expect(text).toContain(`${I18N_STATUS_ONLINE} 3`);
expect(text).toContain(`${s__('Runners|Offline')} 2`); expect(text).toContain(`${I18N_STATUS_OFFLINE} 2`);
expect(text).toContain(`${s__('Runners|Stale')} 1`); expect(text).toContain(`${I18N_STATUS_STALE} 1`);
}); });
it('Skips query for other stats', () => { it('Skips query for other stats', () => {

View File

@ -28,6 +28,9 @@ import {
CREATED_ASC, CREATED_ASC,
CREATED_DESC, CREATED_DESC,
DEFAULT_SORT, DEFAULT_SORT,
I18N_STATUS_ONLINE,
I18N_STATUS_OFFLINE,
I18N_STATUS_STALE,
INSTANCE_TYPE, INSTANCE_TYPE,
GROUP_TYPE, GROUP_TYPE,
PARAM_KEY_PAUSED, PARAM_KEY_PAUSED,
@ -154,9 +157,9 @@ describe('GroupRunnersApp', () => {
}); });
const text = findRunnerStats().text(); const text = findRunnerStats().text();
expect(text).toContain(`${s__('Runners|Online')} ${mockGroupRunnersCount}`); expect(text).toContain(`${I18N_STATUS_ONLINE} ${mockGroupRunnersCount}`);
expect(text).toContain(`${s__('Runners|Offline')} ${mockGroupRunnersCount}`); expect(text).toContain(`${I18N_STATUS_OFFLINE} ${mockGroupRunnersCount}`);
expect(text).toContain(`${s__('Runners|Stale')} ${mockGroupRunnersCount}`); expect(text).toContain(`${I18N_STATUS_STALE} ${mockGroupRunnersCount}`);
}); });
it('shows the runners list', async () => { it('shows the runners list', async () => {

View File

@ -11,6 +11,7 @@ import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
import runnerFormQuery from '~/runner/graphql/edit/runner_form.query.graphql'; import runnerFormQuery from '~/runner/graphql/edit/runner_form.query.graphql';
import RunnerEditApp from '~//runner/runner_edit/runner_edit_app.vue'; import RunnerEditApp from '~//runner/runner_edit/runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { I18N_STATUS_NEVER_CONTACTED, I18N_INSTANCE_TYPE } from '~/runner/constants';
import { runnerFormData } from '../mock_data'; import { runnerFormData } from '../mock_data';
@ -69,8 +70,8 @@ describe('RunnerEditApp', () => {
it('displays the runner type and status', async () => { it('displays the runner type and status', async () => {
await createComponentWithApollo({ mountFn: mount }); await createComponentWithApollo({ mountFn: mount });
expect(findRunnerHeader().text()).toContain(`never contacted`); expect(findRunnerHeader().text()).toContain(I18N_STATUS_NEVER_CONTACTED);
expect(findRunnerHeader().text()).toContain(`shared`); expect(findRunnerHeader().text()).toContain(I18N_INSTANCE_TYPE);
}); });
it('displays a loading runner form', () => { it('displays a loading runner form', () => {

View File

@ -9,6 +9,7 @@ RSpec.describe Mutations::Commits::Create do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:group) { create(:group, :public) }
let(:context) do let(:context) do
GraphQL::Query::Context.new( GraphQL::Query::Context.new(
@ -39,11 +40,14 @@ RSpec.describe Mutations::Commits::Create do
let(:mutated_commit) { subject[:commit] } let(:mutated_commit) { subject[:commit] }
it 'raises an error if the resource is not accessible to the user' do context 'when user is not a project member' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end end
end
context 'when user does not have enough permissions' do context 'when user is a direct project member' do
context 'and user is a guest' do
before do before do
project.add_guest(user) project.add_guest(user)
end end
@ -53,17 +57,7 @@ RSpec.describe Mutations::Commits::Create do
end end
end end
context 'when user is a maintainer of a different project' do context 'and user is a developer' do
before do
create(:project_empty_repo).add_maintainer(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when the user can create a commit' do
let(:deltas) { mutated_commit.raw_deltas } let(:deltas) { mutated_commit.raw_deltas }
before_all do before_all do
@ -152,7 +146,7 @@ RSpec.describe Mutations::Commits::Create do
it 'returns errors' do it 'returns errors' do
expect(mutated_commit).to be_nil expect(mutated_commit).to be_nil
expect(subject[:errors]).to eq(['You can only create or edit files when you are on a branch']) expect(subject[:errors]).to match_array(['You can only create or edit files when you are on a branch'])
end end
end end
@ -194,7 +188,7 @@ RSpec.describe Mutations::Commits::Create do
it 'returns errors' do it 'returns errors' do
expect(mutated_commit).to be_nil expect(mutated_commit).to be_nil
expect(subject[:errors]).to eq(['Unknown action \'unknown\'']) expect(subject[:errors]).to match_array(['Unknown action \'unknown\''])
end end
end end
@ -205,12 +199,53 @@ RSpec.describe Mutations::Commits::Create do
it 'returns errors' do it 'returns errors' do
expect(mutated_commit).to be_nil expect(mutated_commit).to be_nil
expect(subject[:errors]).to eq(['You are not allowed to push into this branch']) expect(subject[:errors]).to match_array(['You are not allowed to push into this branch'])
end end
end end
end end
end end
context 'when user is an inherited member from the group' do
context 'when project is public with private repository' do
let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is a guest' do
before do
group.add_guest(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
context 'when project is private' do
let(:project) { create(:project, :private, :repository, group: group) }
context 'and user is a guest' do
before do
group.add_guest(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
context 'when user is a maintainer of a different project' do
before do
create(:project_empty_repo).add_maintainer(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
def expect_to_contain_deltas(expected_deltas) def expect_to_contain_deltas(expected_deltas)
expect(deltas.count).to eq(expected_deltas.count) expect(deltas.count).to eq(expected_deltas.count)
expect(deltas).to include(*expected_deltas) expect(deltas).to include(*expected_deltas)

View File

@ -10,11 +10,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
describe 'when exporter is enabled' do describe 'when exporter is enabled' do
before do before do
allow(::WEBrick::HTTPServer).to receive(:new).with( allow(::WEBrick::HTTPServer).to receive(:new).with(
{
Port: anything, Port: anything,
BindAddress: anything, BindAddress: anything,
Logger: anything, Logger: anything,
AccessLog: anything AccessLog: anything
).and_call_original }).and_call_original
allow(settings).to receive(:enabled).and_return(true) allow(settings).to receive(:enabled).and_return(true)
allow(settings).to receive(:port).and_return(0) allow(settings).to receive(:port).and_return(0)
@ -45,11 +46,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
it 'starts server with port and address from settings' do it 'starts server with port and address from settings' do
expect(::WEBrick::HTTPServer).to receive(:new).with( expect(::WEBrick::HTTPServer).to receive(:new).with(
{
Port: port, Port: port,
BindAddress: address, BindAddress: address,
Logger: anything, Logger: anything,
AccessLog: anything AccessLog: anything
).and_wrap_original do |m, *args| }).and_wrap_original do |m, *args|
m.call(DoNotListen: true, Logger: args.first[:Logger]) m.call(DoNotListen: true, Logger: args.first[:Logger])
end end

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe CommitPolicy do RSpec.describe CommitPolicy do
describe '#rules' do describe '#rules' do
let(:group) { create(:group, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:commit) { project.repository.head_commit } let(:commit) { project.repository.head_commit }
let(:policy) { described_class.new(user, commit) } let(:policy) { described_class.new(user, commit) }
@ -19,26 +20,31 @@ RSpec.describe CommitPolicy do
end end
shared_examples 'cannot read commit nor create a note' do shared_examples 'cannot read commit nor create a note' do
it 'can not read commit' do it 'cannot read commit' do
expect(policy).to be_disallowed(:read_commit) expect(policy).to be_disallowed(:read_commit)
end end
it 'can not create a note' do it 'cannot create a note' do
expect(policy).to be_disallowed(:create_note) expect(policy).to be_disallowed(:create_note)
end end
end end
context 'when project is public' do context 'when project is public' do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository, group: group) }
context 'when the user is not a project member' do
it_behaves_like 'can read commit and create a note' it_behaves_like 'can read commit and create a note'
end
context 'when repository access level is private' do context 'when repository access level is private' do
let(:project) { create(:project, :public, :repository, :repository_private) } let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'when the user is not a project member' do
it_behaves_like 'cannot read commit nor create a note' it_behaves_like 'cannot read commit nor create a note'
end
context 'when the user is a project member' do context 'when the user is a direct project member' do
context 'and the user is a developer' do
before do before do
project.add_developer(user) project.add_developer(user)
end end
@ -46,24 +52,52 @@ RSpec.describe CommitPolicy do
it_behaves_like 'can read commit and create a note' it_behaves_like 'can read commit and create a note'
end end
end end
context 'when the user is an inherited member from the group' do
context 'and the user is a guest' do
before do
group.add_guest(user)
end
it_behaves_like 'can read commit and create a note'
end
context 'and the user is a reporter' do
before do
group.add_reporter(user)
end
it_behaves_like 'can read commit and create a note'
end
context 'and the user is a developer' do
before do
group.add_developer(user)
end
it_behaves_like 'can read commit and create a note'
end
end
end
end end
context 'when project is private' do context 'when project is private' do
let(:project) { create(:project, :private, :repository) } let(:project) { create(:project, :private, :repository, group: group) }
context 'when the user is not a project member' do
it_behaves_like 'cannot read commit nor create a note' it_behaves_like 'cannot read commit nor create a note'
end
context 'when the user is a project member' do context 'when the user is a direct project member' do
context 'and the user is a developer' do
before do before do
project.add_developer(user) project.add_developer(user)
end end
it 'can read commit and create a note' do it_behaves_like 'can read commit and create a note'
expect(policy).to be_allowed(:read_commit)
end
end end
context 'when the user is a guest' do context 'and the user is a guest' do
before do before do
project.add_guest(user) project.add_guest(user)
end end
@ -75,5 +109,32 @@ RSpec.describe CommitPolicy do
end end
end end
end end
context 'when the user is an inherited member from the group' do
context 'and the user is a guest' do
before do
group.add_guest(user)
end
it_behaves_like 'cannot read commit nor create a note'
end
context 'and the user is a reporter' do
before do
group.add_reporter(user)
end
it_behaves_like 'can read commit and create a note'
end
context 'and the user is a developer' do
before do
group.add_developer(user)
end
it_behaves_like 'can read commit and create a note'
end
end
end
end end
end end

View File

@ -15,6 +15,8 @@ RSpec.describe API::Commits do
let(:branch_with_slash) { project.repository.find_branch('improve/awesome') } let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
let(:project_id) { project.id } let(:project_id) { project.id }
let(:current_user) { nil } let(:current_user) { nil }
let(:group) { create(:group, :public) }
let(:inherited_guest) { create(:user).tap { |u| group.add_guest(u) } }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
@ -56,7 +58,9 @@ RSpec.describe API::Commits do
end end
end end
context 'when authenticated', 'as a maintainer' do context 'when authenticated' do
context 'when user is a direct project member' do
context 'and user is a maintainer' do
let(:current_user) { user } let(:current_user) { user }
it_behaves_like 'project commits' it_behaves_like 'project commits'
@ -366,6 +370,33 @@ RSpec.describe API::Commits do
end end
end end
context 'when user is an inherited member from the group' do
context 'when project is public with private repository' do
let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is a guest' do
let(:current_user) { inherited_guest }
it_behaves_like 'project commits'
end
end
context 'when project is private' do
let(:project) { create(:project, :private, :repository, group: group) }
context 'and user is a guest' do
let(:current_user) { inherited_guest }
it_behaves_like '404 response' do
let(:request) { get api(route) }
let(:message) { '404 Project Not Found' }
end
end
end
end
end
end
describe "POST /projects/:id/repository/commits" do describe "POST /projects/:id/repository/commits" do
let!(:url) { "/projects/#{project_id}/repository/commits" } let!(:url) { "/projects/#{project_id}/repository/commits" }
@ -466,11 +497,37 @@ RSpec.describe API::Commits do
end end
context 'a new file in project repo' do context 'a new file in project repo' do
context 'when user is a direct project member' do
before do before do
post api(url, user), params: valid_c_params post api(url, user), params: valid_c_params
end end
it_behaves_like "successfully creates the commit" it_behaves_like 'successfully creates the commit'
end
context 'when user is an inherited member from the group' do
context 'when project is public with private repository' do
let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { post api(url, inherited_guest), params: valid_c_params }
let(:message) { '403 Forbidden' }
end
end
end
context 'when project is private' do
let(:project) { create(:project, :private, :repository, group: group) }
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { post api(url, inherited_guest), params: valid_c_params }
let(:message) { '403 Forbidden' }
end
end
end
end
end end
context 'a new file with utf8 chars in project repo' do context 'a new file with utf8 chars in project repo' do

View File

@ -304,7 +304,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] } let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] }
it 'can be replied to after resolving' do it 'can be replied to after resolving' do
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
wait_for_requests wait_for_requests
refresh refresh
@ -316,7 +316,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
it 'shows resolved thread when toggled' do it 'shows resolved thread when toggled' do
submit_reply('a') submit_reply('a')
find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage find('button[data-testid="resolve-discussion-button"]').click
wait_for_requests wait_for_requests
expect(page).to have_selector(".note-row-#{note_id}", visible: true) expect(page).to have_selector(".note-row-#{note_id}", visible: true)

View File

@ -14,7 +14,7 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
end end
def package_table_row(index) def package_table_row(index)
page.all("#{packages_table_selector} > [data-qa-selector=\"package_row\"]")[index].text # rubocop:disable QA/SelectorUsage page.all("#{packages_table_selector} > [data-testid=\"package-row\"]")[index].text
end end
end end
@ -84,7 +84,7 @@ RSpec.shared_examples 'shared package sorting' do
end end
def packages_table_selector def packages_table_selector
'[data-qa-selector="packages-table"]' # rubocop:disable QA/SelectorUsage '[data-testid="packages-table"]'
end end
def click_sort_option(option, ascending) def click_sort_option(option, ascending)

View File

@ -64,9 +64,9 @@ end
RSpec.shared_examples 'shows no runners registered' do RSpec.shared_examples 'shows no runners registered' do
it 'shows counts with 0' do it 'shows counts with 0' do
expect(page).to have_text "Online 0" expect(page).to have_text "#{s_('Runners|Online')} 0"
expect(page).to have_text "Offline 0" expect(page).to have_text "#{s_('Runners|Offline')} 0"
expect(page).to have_text "Stale 0" expect(page).to have_text "#{s_('Runners|Stale')} 0"
end end
it 'shows "no runners" message' do it 'shows "no runners" message' do
@ -101,7 +101,7 @@ RSpec.shared_examples 'pauses, resumes and deletes a runner' do
within_runner_row(runner.id) do within_runner_row(runner.id) do
click_button "Pause" click_button "Pause"
expect(page).to have_text 'paused' expect(page).to have_text s_('Runners|Paused')
expect(page).to have_button 'Resume' expect(page).to have_button 'Resume'
expect(page).not_to have_button 'Pause' expect(page).not_to have_button 'Pause'

View File

@ -91,7 +91,7 @@ RSpec.shared_examples 'variable list' do |is_admin|
end end
page.within('#add-ci-variable') do page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set('new_key') # rubocop:disable QA/SelectorUsage find('[data-testid="pipeline-form-ci-variable-key"] input').set('new_key')
click_button('Update variable') click_button('Update variable')
end end
@ -173,7 +173,7 @@ RSpec.shared_examples 'variable list' do |is_admin|
click_button('Add variable') click_button('Add variable')
page.within('#add-ci-variable') do page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set('empty_mask_key') # rubocop:disable QA/SelectorUsage find('[data-testid="pipeline-form-ci-variable-key"] input').set('empty_mask_key')
find('[data-testid="ci-variable-protected-checkbox"]').click find('[data-testid="ci-variable-protected-checkbox"]').click
find('[data-testid="ci-variable-masked-checkbox"]').click find('[data-testid="ci-variable-masked-checkbox"]').click
@ -290,8 +290,8 @@ RSpec.shared_examples 'variable list' do |is_admin|
wait_for_requests wait_for_requests
page.within('#add-ci-variable') do page.within('#add-ci-variable') do
find('[data-qa-selector="ci_variable_key_field"] input').set(key) # rubocop:disable QA/SelectorUsage find('[data-testid="pipeline-form-ci-variable-key"] input').set(key)
find('[data-qa-selector="ci_variable_value_field"]').set(value) if value.present? # rubocop:disable QA/SelectorUsage find('[data-testid="pipeline-form-ci-variable-value"]').set(value) if value.present?
find('[data-testid="ci-variable-protected-checkbox"]').click if protected find('[data-testid="ci-variable-protected-checkbox"]').click if protected
find('[data-testid="ci-variable-masked-checkbox"]').click if masked find('[data-testid="ci-variable-masked-checkbox"]').click if masked

View File

@ -64,7 +64,7 @@ RSpec.shared_examples 'User previews wiki changes' do
end end
it_behaves_like 'relative links' do it_behaves_like 'relative links' do
let(:element) { page.find('[data-testid="wiki_page_content"]') } let(:element) { page.find('[data-testid="wiki-page-content"]') }
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'User views AsciiDoc page with includes' do RSpec.shared_examples 'User views AsciiDoc page with includes' do
let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' } # rubocop:disable QA/SelectorUsage let_it_be(:wiki_content_selector) { '[data-testid=wiki-page-content]' }
let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page') } let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page') }
let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") } let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") }

View File

@ -185,6 +185,28 @@ RSpec.shared_examples_for 'collection cache helper' do
end end
end end
context 'when presentable has a group by clause' do
let(:presentable) { MergeRequest.group(:id) }
it "returns the presentables" do
expect(transaction)
.to receive(:increment)
.with(:cached_object_operations_total, 0, { caller_id: caller_id, render_type: :collection, cache_hit: true }).once
expect(transaction)
.to receive(:increment)
.with(:cached_object_operations_total, MergeRequest.count, { caller_id: caller_id, render_type: :collection, cache_hit: false }).once
parsed = Gitlab::Json.parse(subject.to_s)
expect(parsed).to be_an(Array)
presentable.each_with_index do |item, i|
expect(parsed[i]["id"]).to eq(item.id)
end
end
end
context 'when the presentables all miss' do context 'when the presentables all miss' do
it 'increments the counters' do it 'increments the counters' do
expect(transaction) expect(transaction)