Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4d8b6311a9
commit
f9adc222bc
|
|
@ -98,7 +98,7 @@ export default {
|
|||
:file-name="blob.name"
|
||||
:type="activeViewer.fileType"
|
||||
:hide-line-numbers="hideLineNumbers"
|
||||
data-qa-selector="blob_viewer_file_content"
|
||||
data-testid="blob-viewer-file-content"
|
||||
@richContentLoaded="richContentLoaded = true"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-button-group data-qa-selector="default_actions_container">
|
||||
<gl-button-group data-testid="default-actions-container">
|
||||
<gl-button
|
||||
v-if="showCopyButton"
|
||||
v-gl-tooltip.hover
|
||||
|
|
@ -104,8 +104,7 @@ export default {
|
|||
:title="$options.BTN_COPY_CONTENTS_TITLE"
|
||||
:disabled="copyDisabled"
|
||||
:data-clipboard-target="getBlobHashTarget"
|
||||
data-testid="copyContentsButton"
|
||||
data-qa-selector="copy_contents_button"
|
||||
data-testid="copy-contents-button"
|
||||
icon="copy-to-clipboard"
|
||||
category="primary"
|
||||
variant="default"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default {
|
|||
<file-icon :file-name="fileName" :size="16" aria-hidden="true" css-classes="gl-mr-3" />
|
||||
<strong
|
||||
class="file-title-name mr-1 js-blob-header-filepath"
|
||||
data-qa-selector="file_title_content"
|
||||
data-testid="file-title-content"
|
||||
>{{ fileName }}</strong
|
||||
>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlForm, GlFormInput, GlButton } from '@gitlab/ui';
|
||||
import { GlForm, GlFormInput, GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
|
|
@ -12,6 +12,9 @@ export default {
|
|||
GlButton,
|
||||
GlFormInput,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputEnabled: false,
|
||||
|
|
@ -37,7 +40,8 @@ export default {
|
|||
<div id="peek-view-add-request" class="view gl-display-flex">
|
||||
<gl-form class="gl-display-flex gl-align-items-center" @submit.prevent>
|
||||
<gl-button
|
||||
class="gl-text-blue-300! gl-mr-2"
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-text-white! gl-mr-2"
|
||||
category="tertiary"
|
||||
variant="link"
|
||||
icon="plus"
|
||||
|
|
@ -52,7 +56,7 @@ export default {
|
|||
type="text"
|
||||
:placeholder="$options.i18n.inputLabel"
|
||||
:aria-label="$options.i18n.inputLabel"
|
||||
class="gl-ml-2"
|
||||
class="gl-ml-2 gl-px-3! gl-py-2!"
|
||||
@keyup.enter="addRequest"
|
||||
@keyup.esc="clearForm"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<script>
|
||||
import { GlButton, GlModal, GlModalDirective, GlCollapsibleListbox } from '@gitlab/ui';
|
||||
import {
|
||||
GlButton,
|
||||
GlTooltipDirective,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlCollapsibleListbox,
|
||||
} from '@gitlab/ui';
|
||||
|
||||
import { __, s__ } from '~/locale';
|
||||
import { sortOrders, sortOrderOptions } from '../constants';
|
||||
|
|
@ -13,6 +19,7 @@ export default {
|
|||
GlCollapsibleListbox,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
'gl-modal': GlModalDirective,
|
||||
},
|
||||
props: {
|
||||
|
|
@ -133,14 +140,17 @@ export default {
|
|||
<div
|
||||
v-if="currentRequest.details && metricDetails"
|
||||
:id="`peek-view-${metric}`"
|
||||
class="gl-display-flex gl-align-items-center view"
|
||||
class="gl-display-flex gl-align-items-baseline view"
|
||||
data-qa-selector="detailed_metric_content"
|
||||
>
|
||||
<gl-button v-gl-modal="modalId" class="gl-mr-2" type="button" variant="link">
|
||||
<span
|
||||
class="gl-text-blue-200 gl-font-weight-bold"
|
||||
data-testid="performance-bar-details-label"
|
||||
>
|
||||
<gl-button
|
||||
v-gl-tooltip.viewport
|
||||
v-gl-modal="modalId"
|
||||
class="gl-text-white! gl-mr-2"
|
||||
:title="header"
|
||||
variant="link"
|
||||
>
|
||||
<span class="gl-font-sm gl-font-weight-semibold" data-testid="performance-bar-details-label">
|
||||
{{ metricDetailsLabel }}
|
||||
</span>
|
||||
</gl-button>
|
||||
|
|
@ -150,7 +160,7 @@ export default {
|
|||
<div v-for="(value, name) in metricDetailsSummary" :key="name" class="gl-pr-8">
|
||||
<div v-if="value" data-testid="performance-bar-summary-item">
|
||||
<div>{{ name }}</div>
|
||||
<div class="gl-font-size-h1 gl-font-weight-bold">{{ value }}</div>
|
||||
<div class="gl-font-size-h1 gl-font-weight-semibold">{{ value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -178,7 +188,7 @@ export default {
|
|||
v-for="(key, keyIndex) in keys"
|
||||
:key="key"
|
||||
class="text-break-word"
|
||||
:class="{ 'mb-3 bold': keyIndex == 0 }"
|
||||
:class="{ 'mb-3 gl-font-weight-semibold': keyIndex == 0 }"
|
||||
>
|
||||
{{ item[key] }}
|
||||
<gl-button
|
||||
|
|
@ -214,7 +224,7 @@ export default {
|
|||
<div></div>
|
||||
</template>
|
||||
</gl-modal>
|
||||
{{ actualTitle }}
|
||||
<span class="gl-opacity-7">{{ actualTitle }}</span>
|
||||
<request-warning :html-id="htmlId" :warnings="warnings" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<script>
|
||||
import { GlLink, GlPopover } from '@gitlab/ui';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { glEmojiTag } from '~/emoji';
|
||||
import { GlLink, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
|
||||
import { s__ } from '~/locale';
|
||||
|
|
@ -11,14 +9,13 @@ import RequestSelector from './request_selector.vue';
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlPopover,
|
||||
AddRequest,
|
||||
DetailedMetric,
|
||||
GlLink,
|
||||
RequestSelector,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
store: {
|
||||
|
|
@ -123,11 +120,8 @@ export default {
|
|||
hasHost() {
|
||||
return this.currentRequest && this.currentRequest.details && this.currentRequest.details.host;
|
||||
},
|
||||
birdEmoji() {
|
||||
if (this.hasHost && this.currentRequest.details.host.canary) {
|
||||
return glEmojiTag('baby_chick');
|
||||
}
|
||||
return '';
|
||||
isCanary() {
|
||||
return Boolean(this.currentRequest.details.host.canary);
|
||||
},
|
||||
downloadPath() {
|
||||
const data = JSON.stringify(this.requests);
|
||||
|
|
@ -165,7 +159,6 @@ export default {
|
|||
this.currentRequest = this.requestId;
|
||||
},
|
||||
methods: {
|
||||
glEmojiTag,
|
||||
changeCurrentRequest(newRequestId) {
|
||||
this.currentRequest = newRequestId;
|
||||
this.$emit('change-request', newRequestId);
|
||||
|
|
@ -180,96 +173,112 @@ export default {
|
|||
return this.store.findRequest(requestId)?.method?.toUpperCase() === 'GET';
|
||||
},
|
||||
},
|
||||
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div id="js-peek" :class="env">
|
||||
<div
|
||||
v-if="currentRequest"
|
||||
class="d-flex container-fluid container-limited justify-content-center gl-align-items-center"
|
||||
class="gl-display-flex container-fluid gl-overflow-x-auto"
|
||||
data-qa-selector="performance_bar"
|
||||
>
|
||||
<div id="peek-view-host" class="view">
|
||||
<span
|
||||
v-if="hasHost"
|
||||
class="current-host"
|
||||
:class="{ canary: currentRequest.details.host.canary }"
|
||||
<div class="gl-display-flex gl-flex-shrink-0 view-performance-container">
|
||||
<div v-if="hasHost" id="peek-view-host" class="gl-display-flex gl-gap-2 view">
|
||||
<span class="current-host" :class="{ canary: isCanary }">
|
||||
<gl-emoji
|
||||
v-if="isCanary"
|
||||
id="canary-emoji"
|
||||
v-gl-tooltip.viewport="'Canary'"
|
||||
data-name="baby_chick"
|
||||
/>
|
||||
<gl-emoji
|
||||
id="host-emoji"
|
||||
v-gl-tooltip.viewport="currentRequest.details.host.hostname"
|
||||
data-name="computer"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<detailed-metric
|
||||
v-for="metric in $options.detailedMetrics"
|
||||
:key="metric.metric"
|
||||
:current-request="currentRequest"
|
||||
:metric="metric.metric"
|
||||
:title="metric.title"
|
||||
:header="metric.header"
|
||||
:keys="metric.keys"
|
||||
/>
|
||||
<div
|
||||
v-if="currentRequest.details && currentRequest.details.tracing"
|
||||
id="peek-view-trace"
|
||||
class="view"
|
||||
>
|
||||
<span id="canary-emoji" v-safe-html:[$options.safeHtmlConfig]="birdEmoji"></span>
|
||||
<gl-popover placement="bottom" target="canary-emoji" content="Canary" />
|
||||
<span
|
||||
id="host-emoji"
|
||||
v-safe-html:[$options.safeHtmlConfig]="glEmojiTag('computer')"
|
||||
></span>
|
||||
<gl-popover
|
||||
placement="bottom"
|
||||
target="host-emoji"
|
||||
:content="currentRequest.details.host.hostname"
|
||||
/>
|
||||
</span>
|
||||
<gl-link
|
||||
class="gl-text-white! gl-text-decoration-underline"
|
||||
:href="currentRequest.details.tracing.tracing_url"
|
||||
>{{ s__('PerformanceBar|Trace') }}</gl-link
|
||||
>
|
||||
</div>
|
||||
<div v-if="showFlamegraphButtons" id="peek-flamegraph" class="view">
|
||||
<gl-link
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-font-sm gl-text-white!"
|
||||
:href="flamegraphPath('wall', currentRequestId)"
|
||||
:title="s__('PerformanceBar|Wall flamegraph')"
|
||||
>{{ s__('PerformanceBar|Wall') }}</gl-link
|
||||
>
|
||||
/
|
||||
<gl-link
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-font-sm gl-text-white!"
|
||||
:href="flamegraphPath('cpu', currentRequestId)"
|
||||
:title="s__('PerformanceBar|CPU flamegraph')"
|
||||
>{{ s__('PerformanceBar|CPU') }}</gl-link
|
||||
>
|
||||
/
|
||||
<gl-link
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-font-sm gl-text-white!"
|
||||
:href="flamegraphPath('object', currentRequestId)"
|
||||
:title="s__('PerformanceBar|Object flamegraph')"
|
||||
>{{ s__('PerformanceBar|Object') }}</gl-link
|
||||
>
|
||||
<span class="gl-opacity-7">{{ s__('PerformanceBar|flamegraph') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<detailed-metric
|
||||
v-for="metric in $options.detailedMetrics"
|
||||
:key="metric.metric"
|
||||
:current-request="currentRequest"
|
||||
:metric="metric.metric"
|
||||
:title="metric.title"
|
||||
:header="metric.header"
|
||||
:keys="metric.keys"
|
||||
/>
|
||||
<div
|
||||
v-if="currentRequest.details && currentRequest.details.tracing"
|
||||
id="peek-view-trace"
|
||||
class="view"
|
||||
>
|
||||
<gl-link class="gl-text-blue-200" :href="currentRequest.details.tracing.tracing_url">{{
|
||||
s__('PerformanceBar|Trace')
|
||||
<div class="gl-display-flex gl-flex-shrink-0 gl-ml-auto">
|
||||
<div class="gl-display-flex view-reports-container">
|
||||
<div v-if="currentRequest.details" id="peek-download" class="view">
|
||||
<gl-link
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-font-sm gl-text-white!"
|
||||
is-unsafe-link
|
||||
:download="downloadName"
|
||||
:href="downloadPath"
|
||||
:title="s__('PerformanceBar|Download report')"
|
||||
>{{ s__('PerformanceBar|Download') }}</gl-link
|
||||
>
|
||||
</div>
|
||||
<div v-if="showMemoryReportButton" id="peek-memory-report" class="view">
|
||||
<gl-link
|
||||
v-gl-tooltip.viewport
|
||||
class="gl-font-sm gl-text-white!"
|
||||
:href="memoryReportPath"
|
||||
:title="s__('PerformanceBar|Download memory report')"
|
||||
>{{ s__('PerformanceBar|Memory report') }}</gl-link
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<gl-link v-if="statsUrl" class="gl-text-white! view" :href="statsUrl">{{
|
||||
s__('PerformanceBar|Stats')
|
||||
}}</gl-link>
|
||||
<request-selector
|
||||
v-if="currentRequest"
|
||||
:current-request="currentRequest"
|
||||
:requests="requests"
|
||||
@change-current-request="changeCurrentRequest"
|
||||
/>
|
||||
<add-request v-on="$listeners" />
|
||||
</div>
|
||||
<div v-if="currentRequest.details" id="peek-download" class="view">
|
||||
<gl-link
|
||||
class="gl-text-blue-200"
|
||||
is-unsafe-link
|
||||
:download="downloadName"
|
||||
:href="downloadPath"
|
||||
>{{ s__('PerformanceBar|Download') }}</gl-link
|
||||
>
|
||||
</div>
|
||||
<div v-if="showMemoryReportButton" id="peek-memory-report" class="view">
|
||||
<gl-link class="gl-text-blue-200" :href="memoryReportPath">{{
|
||||
s__('PerformanceBar|Memory report')
|
||||
}}</gl-link>
|
||||
</div>
|
||||
<div v-if="showFlamegraphButtons" id="peek-flamegraph" class="view">
|
||||
<span id="flamegraph-emoji" class="gl-text-white-200">
|
||||
<span v-safe-html:[$options.safeHtmlConfig]="glEmojiTag('fire')"></span>
|
||||
<span v-safe-html:[$options.safeHtmlConfig]="glEmojiTag('bar_chart')"></span>
|
||||
</span>
|
||||
<gl-popover placement="bottom" target="flamegraph-emoji" content="Flamegraph" />
|
||||
<gl-link class="gl-text-blue-200" :href="flamegraphPath('wall', currentRequestId)">{{
|
||||
s__('PerformanceBar|wall')
|
||||
}}</gl-link>
|
||||
/
|
||||
<gl-link class="gl-text-blue-200" :href="flamegraphPath('cpu', currentRequestId)">{{
|
||||
s__('PerformanceBar|cpu')
|
||||
}}</gl-link>
|
||||
/
|
||||
<gl-link class="gl-text-blue-200" :href="flamegraphPath('object', currentRequestId)">{{
|
||||
s__('PerformanceBar|object')
|
||||
}}</gl-link>
|
||||
</div>
|
||||
<gl-link v-if="statsUrl" class="gl-text-blue-200 view" :href="statsUrl">{{
|
||||
s__('PerformanceBar|Stats')
|
||||
}}</gl-link>
|
||||
<request-selector
|
||||
v-if="currentRequest"
|
||||
:current-request="currentRequest"
|
||||
:requests="requests"
|
||||
class="gl-ml-auto"
|
||||
@change-current-request="changeCurrentRequest"
|
||||
/>
|
||||
<add-request v-on="$listeners" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<script>
|
||||
import { GlFormSelect } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormSelect,
|
||||
},
|
||||
props: {
|
||||
currentRequest: {
|
||||
type: Object,
|
||||
|
|
@ -23,8 +28,8 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div id="peek-request-selector" data-qa-selector="request_dropdown" class="view">
|
||||
<select v-model="currentRequestId">
|
||||
<div id="peek-request-selector" data-qa-selector="request_dropdown" class="view gl-mr-5">
|
||||
<gl-form-select v-model="currentRequestId" class="gl-px-3! gl-py-2!">
|
||||
<option
|
||||
v-for="request in requests"
|
||||
:key="request.id"
|
||||
|
|
@ -33,6 +38,6 @@ export default {
|
|||
>
|
||||
{{ request.displayName }}
|
||||
</option>
|
||||
</select>
|
||||
</gl-form-select>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
<script>
|
||||
import { GlPopover } from '@gitlab/ui';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { glEmojiTag } from '~/emoji';
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlPopover,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
htmlId: {
|
||||
|
|
@ -32,15 +27,17 @@ export default {
|
|||
return this.warnings.join('\n');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
glEmojiTag,
|
||||
},
|
||||
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<span v-if="hasWarnings" class="gl-cursor-default">
|
||||
<span :id="htmlId" v-safe-html:[$options.safeHtmlConfig]="glEmojiTag('warning')"></span>
|
||||
<gl-popover placement="bottom" :target="htmlId" :content="warningMessage" />
|
||||
<gl-emoji
|
||||
v-if="hasWarnings"
|
||||
:id="htmlId"
|
||||
v-gl-tooltip.viewport="warningMessage"
|
||||
data-name="warning"
|
||||
class="gl-ml-2"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
:aria-label="$options.MSG_COPY"
|
||||
:data-clipboard-text="value"
|
||||
icon="copy-to-clipboard"
|
||||
data-qa-selector="copy_button"
|
||||
data-testid="copy-button"
|
||||
:data-qa-action="name"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -68,14 +68,14 @@ export default {
|
|||
<embed-dropdown
|
||||
v-if="embeddable"
|
||||
:url="snippet.webUrl"
|
||||
data-qa-selector="snippet_embed_dropdown"
|
||||
data-testid="snippet-embed-dropdown"
|
||||
/>
|
||||
<clone-dropdown-button
|
||||
v-if="canBeCloned"
|
||||
class="gl-ml-3"
|
||||
:ssh-link="snippet.sshUrlToRepo"
|
||||
:http-link="snippet.httpUrlToRepo"
|
||||
data-qa-selector="clone_button"
|
||||
data-testid="clone-button"
|
||||
/>
|
||||
</div>
|
||||
<gl-alert v-if="hasUnretrievableBlobs" variant="danger" class="gl-mb-3" :dismissible="false">
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<markdown-field-view class="snippet-description" data-qa-selector="snippet_description_content">
|
||||
<markdown-field-view class="snippet-description" data-testid="snippet-description-content">
|
||||
<div
|
||||
v-safe-html:[$options.safeHtmlConfig]="description"
|
||||
class="md js-snippet-description"
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ export default {
|
|||
<div class="detail-page-header-body">
|
||||
<div
|
||||
class="snippet-box has-tooltip d-flex align-items-center gl-mr-2 mb-1"
|
||||
data-qa-selector="snippet_container"
|
||||
data-testid="snippet-container"
|
||||
:title="snippetVisibilityLevelDescription"
|
||||
data-container="body"
|
||||
>
|
||||
|
|
@ -267,7 +267,7 @@ export default {
|
|||
:category="action.category"
|
||||
:class="action.cssClass"
|
||||
:href="action.href"
|
||||
data-qa-selector="snippet_action_button"
|
||||
data-testid="snippet-action-button"
|
||||
:data-qa-action="action.text"
|
||||
@click="action.click ? action.click() : undefined"
|
||||
>{{ action.text }}</gl-button
|
||||
|
|
@ -321,8 +321,7 @@ export default {
|
|||
variant="danger"
|
||||
category="primary"
|
||||
:disabled="isLoading"
|
||||
data-qa-selector="delete_snippet_button"
|
||||
data-testid="delete-snippet"
|
||||
data-testid="delete-snippet-button"
|
||||
@click="deleteSnippet"
|
||||
>
|
||||
<gl-loading-icon v-if="isLoading" size="sm" inline />
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div class="snippet-header limited-header-width">
|
||||
<h2 class="snippet-title gl-mt-0 mb-3" data-qa-selector="snippet_title_content">
|
||||
<h2 class="snippet-title gl-mt-0 mb-3" data-testid="snippet-title-content">
|
||||
{{ snippet.title }}
|
||||
</h2>
|
||||
|
||||
|
|
|
|||
|
|
@ -47,13 +47,13 @@ export default {
|
|||
v-if="sshLink"
|
||||
:label="$options.labels.ssh"
|
||||
:link="sshLink"
|
||||
qa-selector="copy_ssh_url_button"
|
||||
test-id="copy-ssh-url-button"
|
||||
/>
|
||||
<clone-dropdown-item
|
||||
v-if="httpLink"
|
||||
:label="httpLabel"
|
||||
:link="httpLink"
|
||||
qa-selector="copy_http_url_button"
|
||||
test-id="copy-http-url-button"
|
||||
/>
|
||||
</gl-disclosure-dropdown>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
qaSelector: {
|
||||
testId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
|
@ -45,7 +45,7 @@ export default {
|
|||
:title="$options.copyURLTooltip"
|
||||
:aria-label="$options.copyURLTooltip"
|
||||
:data-clipboard-text="link"
|
||||
:data-qa-selector="qaSelector"
|
||||
:data-testid="testId"
|
||||
icon="copy-to-clipboard"
|
||||
class="gl-display-inline-flex"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export default {
|
|||
:class="$options.userColorScheme"
|
||||
data-type="simple"
|
||||
:data-path="blob.path"
|
||||
data-qa-selector="blob_viewer_file_content"
|
||||
data-testid="blob-viewer-file-content"
|
||||
>
|
||||
<codeowners-validation
|
||||
v-if="isCodeownersFile"
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ $count-arrow-border: #dce0e5;
|
|||
$general-hover-transition-duration: 100ms;
|
||||
$general-hover-transition-curve: linear;
|
||||
$highlight-changes-color: rgb(235, 255, 232);
|
||||
$performance-bar-height: 35px;
|
||||
$performance-bar-height: 40px;
|
||||
$system-header-height: 16px;
|
||||
$system-footer-height: $system-header-height;
|
||||
$mr-sticky-header-height: 72px;
|
||||
|
|
@ -834,7 +834,7 @@ Performance Bar
|
|||
*/
|
||||
$perf-bar-production: $gray-950;
|
||||
$perf-bar-staging: $indigo-950;
|
||||
$perf-bar-development: $red-950;
|
||||
$perf-bar-development: $red-900;
|
||||
$perf-bar-bucket-bg: $black;
|
||||
$perf-bar-bucket-box-shadow-from: rgba($white, 0.2);
|
||||
$perf-bar-bucket-box-shadow-to: rgba($black, 0.25);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@
|
|||
|
||||
height: $performance-bar-height;
|
||||
background: $black;
|
||||
font-size: $gl-font-size-small;
|
||||
line-height: $performance-bar-height;
|
||||
color: $gray-100;
|
||||
color: $gray-50;
|
||||
|
||||
select {
|
||||
width: 200px;
|
||||
|
|
@ -24,7 +25,11 @@
|
|||
select,
|
||||
input {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
background-color: rgba($white, 0.2);
|
||||
|
||||
&::placeholder {
|
||||
color: rgba($white, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
option {
|
||||
|
|
@ -88,7 +93,6 @@
|
|||
}
|
||||
|
||||
.view {
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:last-child {
|
||||
|
|
@ -96,6 +100,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
.view-performance-container,
|
||||
.view-reports-container {
|
||||
margin-right: $gl-padding-24;
|
||||
|
||||
.view:not(:first-child) {
|
||||
margin-right: 0;
|
||||
|
||||
&::before {
|
||||
content: '•';
|
||||
opacity: .5;
|
||||
display: inline-block;
|
||||
margin: 0 $gl-padding-8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.css-truncate {
|
||||
&.css-truncate-target,
|
||||
.css-truncate-target {
|
||||
|
|
|
|||
|
|
@ -255,6 +255,12 @@ class GraphqlController < ApplicationController
|
|||
end
|
||||
|
||||
def authorize_access_api!
|
||||
if current_user.nil? &&
|
||||
request_authenticator.authentication_token_present? &&
|
||||
Feature.enabled?(:invalid_graphql_auth_401)
|
||||
render_error('Invalid token', status: :unauthorized)
|
||||
end
|
||||
|
||||
return if can?(current_user, :access_api)
|
||||
|
||||
render_error('API not accessible for user', status: :forbidden)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class Namespace < ApplicationRecord
|
|||
length: { maximum: 255 }
|
||||
validates :name, uniqueness: { scope: [:type, :parent_id] }, if: -> { parent_id.present? }
|
||||
|
||||
validates :description, length: { maximum: 2000 }
|
||||
validates :description, length: { maximum: 255 }
|
||||
|
||||
validates :path,
|
||||
presence: true,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
= render_if_exists 'dashboard/todos/saml_reauth_notice'
|
||||
- add_page_specific_style 'page_bundles/todos'
|
||||
- add_page_specific_style 'page_bundles/issuable'
|
||||
- filter_by_done = params[:state] == 'done'
|
||||
- open_todo_count = !filter_by_done ? @allowed_todos.count : todos_pending_count
|
||||
- done_todo_count = filter_by_done ? @allowed_todos.count : todos_done_count
|
||||
|
||||
.page-title-holder.d-flex.align-items-center
|
||||
%h1.page-title.gl-font-size-h-display= _("To-Do List")
|
||||
|
|
@ -14,10 +17,10 @@
|
|||
= gl_tabs_nav({ class: 'gl-flex-grow-1 gl-border-0' }) do
|
||||
= gl_tab_link_to todos_filter_path(state: 'pending'), item_active: params[:state].blank? || params[:state] == 'pending', class: "js-todos-pending" do
|
||||
= _("To Do")
|
||||
= gl_tab_counter_badge(number_with_delimiter(todos_pending_count), { class: 'js-todos-badge' })
|
||||
= gl_tab_link_to todos_filter_path(state: 'done'), item_active: params[:state] == 'done', class: "js-todos-done" do
|
||||
= gl_tab_counter_badge(number_with_delimiter(open_todo_count), { class: 'js-todos-badge' })
|
||||
= gl_tab_link_to todos_filter_path(state: 'done'), item_active: filter_by_done, class: "js-todos-done" do
|
||||
= _("Done")
|
||||
= gl_tab_counter_badge(number_with_delimiter(todos_done_count), { class: 'js-todos-badge' })
|
||||
= gl_tab_counter_badge(number_with_delimiter(done_todo_count), { class: 'js-todos-badge' })
|
||||
|
||||
.nav-controls
|
||||
- if @allowed_todos.any?(&:pending?)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
.note-actions-item.gl-ml-0
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||
icon: 'pencil',
|
||||
button_options: { class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', qa_selector: 'edit_comment_button' }, title: _('Edit comment'), 'aria-label': _('Edit comment') })
|
||||
button_options: { class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', testid: 'edit-comment-button' }, title: _('Edit comment'), 'aria-label': _('Edit comment') })
|
||||
|
||||
= render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
- if note_editable || !is_current_user
|
||||
%div{ class: "dropdown more-actions note-actions-item gl-ml-0!" }
|
||||
= render Pajamas::ButtonComponent.new(icon: 'ellipsis_v', category: :tertiary, button_options: { class: 'note-action-button more-actions-toggle has-tooltip', data: { title: 'More actions', toggle: 'dropdown', container: 'body', qa_selector: 'more_actions_dropdown' }})
|
||||
= render Pajamas::ButtonComponent.new(icon: 'ellipsis_v', category: :tertiary, button_options: { class: 'note-action-button more-actions-toggle has-tooltip', data: { title: 'More actions', toggle: 'dropdown', container: 'body', testid: 'more-actions-dropdown' }})
|
||||
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
|
||||
%li
|
||||
= deprecated_clipboard_button(text: noteable_note_url(note), title: _('Copy reference'), button_text: _('Copy link'), class: 'btn-clipboard', hide_tooltip: true, hide_button_icon: true)
|
||||
|
|
@ -11,6 +11,6 @@
|
|||
.js-report-abuse-dropdown-item{ data: { report_abuse_path: add_category_abuse_reports_path, reported_user_id: note.author.id, reported_from_url: noteable_note_url(note) } }
|
||||
- if note_editable
|
||||
%li
|
||||
= link_to note_url(note), method: :delete, data: { confirm: _('Are you sure you want to delete this comment?'), confirm_btn_variant: 'danger', qa_selector: 'delete_comment_button' }, aria: { label: _('Delete comment') }, remote: true, class: 'js-note-delete' do
|
||||
= link_to note_url(note), method: :delete, data: { confirm: _('Are you sure you want to delete this comment?'), confirm_btn_variant: 'danger', testid: 'delete-comment-button' }, aria: { label: _('Delete comment') }, remote: true, class: 'js-note-delete' do
|
||||
%span.text-danger
|
||||
= _('Delete comment')
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
- current_text ||= nil
|
||||
- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
|
||||
- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
|
||||
- qa_selector = local_assigns.fetch(:qa_selector, '')
|
||||
- testid = local_assigns.fetch(:testid, '')
|
||||
- autofocus = local_assigns.fetch(:autofocus, false)
|
||||
|
||||
.zen-backdrop
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
dir: 'auto',
|
||||
data: { supports_quick_actions: supports_quick_actions,
|
||||
supports_autocomplete: supports_autocomplete,
|
||||
qa_selector: qa_selector,
|
||||
testid: testid,
|
||||
autofocus: autofocus }
|
||||
- else
|
||||
= text_area_tag attr, current_text, data: { qa_selector: qa_selector }, class: classes, placeholder: placeholder
|
||||
= text_area_tag attr, current_text, data: { testid: testid }, class: classes, placeholder: placeholder
|
||||
%a.zen-control.zen-control-leave.js-zen-leave.gl-text-gray-500{ href: "#" }
|
||||
= sprite_icon('minimize')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- noteable_name = @note.noteable.human_class_name
|
||||
|
||||
.js-comment-type-dropdown.float-left.gl-sm-mr-3{ data: { noteable_name: noteable_name } }
|
||||
= render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { class: 'js-comment-button js-comment-submit-button', value: _('Comment'), data: { qa_selector: 'comment_button' }}) do
|
||||
= render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { class: 'js-comment-button js-comment-submit-button', value: _('Comment'), data: { testid: 'comment-button' }}) do
|
||||
= _('Comment')
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
= hidden_field_tag :target_type, '', class: 'js-form-target-type'
|
||||
.flash-container
|
||||
= render layout: 'shared/md_preview', locals: { url: preview_markdown_path(project), referenced_users: true } do
|
||||
= render 'shared/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', qa_selector: 'edit_note_field', placeholder: _("Write a comment or drag your files here…")
|
||||
= render 'shared/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', testid: 'edit-note-field', placeholder: _("Write a comment or drag your files here…")
|
||||
= render 'shared/notes/hints'
|
||||
|
||||
.note-form-actions.clearfix
|
||||
.settings-message.note-edit-warning.js-finish-edit-warning
|
||||
= _("Finish editing this message first!")
|
||||
= render Pajamas::ButtonComponent.new(type: 'submit', variant: :confirm, button_options: { class: 'js-comment-save-button', data: { qa_selector: 'save_comment_button' } }) do
|
||||
= render Pajamas::ButtonComponent.new(type: 'submit', variant: :confirm, button_options: { class: 'js-comment-save-button', data: { testid: 'save-comment-button' } }) do
|
||||
= _("Save comment")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'note-edit-cancel' }) do
|
||||
= _("Cancel")
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
.discussion-form-container.discussion-with-resolve-btn.flex-column.p-0
|
||||
= render layout: 'shared/md_preview', locals: { url: preview_url, referenced_users: true, supports_quick_actions: supports_quick_actions } do
|
||||
= render 'shared/zen', f: f, qa_selector: 'note_field',
|
||||
= render 'shared/zen', f: f, testid: 'note-field',
|
||||
attr: :note,
|
||||
classes: 'note-textarea js-note-text',
|
||||
placeholder: _("Write a comment or drag your files here…"),
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
%span.note-header-author-name.bold
|
||||
= note.author.name
|
||||
= user_status(note.author)
|
||||
%spannote-headline-light{ data: { qa_selector: 'note_author_content' } }
|
||||
%spannote-headline-light{ data: { testid: 'note-author-content' } }
|
||||
= note.author.to_reference
|
||||
%span.note-headline-ligh.note-headline-meta
|
||||
- if note.system
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
- else
|
||||
= render 'projects/notes/actions', note: note, note_editable: note_editable
|
||||
.note-body{ class: note_editable ? 'js-task-list-container' : '' }
|
||||
.note-text.md{ data: { qa_selector: 'note_content' } }
|
||||
.note-text.md{ data: { testid: 'note-content' } }
|
||||
= markdown_field(note, :note)
|
||||
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
||||
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- issuable = @issue || @merge_request
|
||||
- discussion_locked = issuable&.discussion_locked?
|
||||
|
||||
%ul#notes-list.notes.main-notes-list.timeline{ data: { 'qa_selector': 'notes_list' } }
|
||||
%ul#notes-list.notes.main-notes-list.timeline{ data: { 'testid': 'notes-list' } }
|
||||
= render "shared/notes/notes"
|
||||
|
||||
= render 'shared/notes/edit_form', project: @project
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@
|
|||
|
||||
- if note_editable
|
||||
.note-actions-item.gl-ml-0
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'pencil', button_options: { title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', qa_selector: 'edit_comment_button' } })
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'pencil', button_options: { title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', testid: 'edit-comment-button' } })
|
||||
|
||||
= render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: use_primary_store_as_default_for_queues_metadata
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131736
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425508
|
||||
name: invalid_graphql_auth_401
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132149
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/426196
|
||||
milestone: '16.5'
|
||||
type: development
|
||||
group: group::scalability
|
||||
group: group::import and integrate
|
||||
default_enabled: false
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_primary_and_secondary_stores_for_queues_metadata
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131736
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425508
|
||||
milestone: '16.5'
|
||||
type: development
|
||||
group: group::scalability
|
||||
default_enabled: false
|
||||
|
|
@ -894,6 +894,10 @@ Gitlab.ee do
|
|||
Settings.cron_jobs['click_house_events_sync_worker'] ||= {}
|
||||
Settings.cron_jobs['click_house_events_sync_worker']['cron'] ||= "*/3 * * * *"
|
||||
Settings.cron_jobs['click_house_events_sync_worker']['job_class'] = 'ClickHouse::EventsSyncWorker'
|
||||
Settings.cron_jobs['click_house_ci_finished_builds_sync_worker'] ||= {}
|
||||
Settings.cron_jobs['click_house_ci_finished_builds_sync_worker']['cron'] ||= '*/3 * * * *'
|
||||
Settings.cron_jobs['click_house_ci_finished_builds_sync_worker']['args'] ||= [0, 1]
|
||||
Settings.cron_jobs['click_house_ci_finished_builds_sync_worker']['job_class'] = 'ClickHouse::CiFinishedBuildsSyncCronWorker'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
table_name: analytics_cycle_analytics_value_stream_settings
|
||||
classes:
|
||||
- Analytics::CycleAnalytics::ValueStreamSetting
|
||||
feature_categories:
|
||||
- value_stream_management
|
||||
description: Stores settings for each value stream.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132650
|
||||
milestone: '16.5'
|
||||
gitlab_schema: gitlab_main
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateValueStreamAnalyticsSettings < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
create_table :analytics_cycle_analytics_value_stream_settings, id: false do |t|
|
||||
t.references(
|
||||
:value_stream,
|
||||
primary_key: true,
|
||||
default: nil,
|
||||
type: :bigint,
|
||||
index: false,
|
||||
foreign_key: {
|
||||
to_table: :analytics_cycle_analytics_group_value_streams,
|
||||
column: :analytics_cycle_analytics_group_value_stream_id,
|
||||
on_delete: :cascade
|
||||
}
|
||||
)
|
||||
|
||||
t.bigint :project_ids_filter, array: true, default: []
|
||||
t.check_constraint 'CARDINALITY(project_ids_filter) <= 100'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
class CleanupProjectPipelineStatusKey < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
# TODO: to remove after feature-flag in duplicate-jobs client middleware is removed
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION_WORKER_CLASS = 'BackfillProjectPipelineStatusTtl'
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
03edad77c4b9ca8754a6365f42abe3e1ae139b934603085fd88d01c0a3e0acbc
|
||||
|
|
@ -11179,6 +11179,12 @@ CREATE SEQUENCE analytics_cycle_analytics_stage_event_hashes_id_seq
|
|||
|
||||
ALTER SEQUENCE analytics_cycle_analytics_stage_event_hashes_id_seq OWNED BY analytics_cycle_analytics_stage_event_hashes.id;
|
||||
|
||||
CREATE TABLE analytics_cycle_analytics_value_stream_settings (
|
||||
value_stream_id bigint NOT NULL,
|
||||
project_ids_filter bigint[] DEFAULT '{}'::bigint[],
|
||||
CONSTRAINT chk_rails_a91b547c97 CHECK ((cardinality(project_ids_filter) <= 100))
|
||||
);
|
||||
|
||||
CREATE TABLE analytics_dashboards_pointers (
|
||||
id bigint NOT NULL,
|
||||
namespace_id bigint,
|
||||
|
|
@ -27605,6 +27611,9 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_value_streams
|
|||
ALTER TABLE ONLY analytics_cycle_analytics_stage_event_hashes
|
||||
ADD CONSTRAINT analytics_cycle_analytics_stage_event_hashes_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY analytics_cycle_analytics_value_stream_settings
|
||||
ADD CONSTRAINT analytics_cycle_analytics_value_stream_settings_pkey PRIMARY KEY (value_stream_id);
|
||||
|
||||
ALTER TABLE ONLY analytics_dashboards_pointers
|
||||
ADD CONSTRAINT analytics_dashboards_pointers_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -38166,6 +38175,9 @@ ALTER TABLE ONLY batched_background_migration_jobs
|
|||
ALTER TABLE ONLY operations_strategies_user_lists
|
||||
ADD CONSTRAINT fk_rails_43241e8d29 FOREIGN KEY (strategy_id) REFERENCES operations_strategies(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY analytics_cycle_analytics_value_stream_settings
|
||||
ADD CONSTRAINT fk_rails_4360d37256 FOREIGN KEY (value_stream_id) REFERENCES analytics_cycle_analytics_group_value_streams(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY merge_request_assignment_events
|
||||
ADD CONSTRAINT fk_rails_4378a2e8d7 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,25 +66,25 @@ All Work Item types share the same pool of predefined widgets and are customized
|
|||
|
||||
### Work Item widget types (updating)
|
||||
|
||||
| Widget | Description | feature flag |
|
||||
|---|---|---|
|
||||
| [WorkItemWidgetAssignees](../../../api/graphql/reference/index.md#workitemwidgetassignees) | List of work item assignees | |
|
||||
| [WorkItemWidgetAwardEmoji](../../../api/graphql/reference/index.md#workitemwidgetawardemoji) | Emoji reactions added to work item, including support for upvote/downvote counts | |
|
||||
| [WorkItemWidgetCurrentUserTodos](../../../api/graphql/reference/index.md#workitemwidgetcurrentusertodos) | User todo state of work item | |
|
||||
| [WorkItemWidgetDescription](../../../api/graphql/reference/index.md#workitemwidgetdescription) | Description of work item, including support for edited state, timestamp, and author | |
|
||||
| [WorkItemWidgetHealthStatus](../../../api/graphql/reference/index.md#workitemwidgethealthstatus) | Health status assignment support for work item | |
|
||||
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | Hierarchy of work items, including support for boolean representing presence of children. **Note:** Hierarchy is currently available only for OKRs. | `okrs_mvc` |
|
||||
| [WorkItemWidgetIteration](../../../api/graphql/reference/index.md#workitemwidgetiteration) | Iteration assignment support for work item | |
|
||||
| [WorkItemWidgetLabels](../../../api/graphql/reference/index.md#workitemwidgetlabels) | List of labels added to work items, including support for checking whether scoped labels are supported |
|
||||
| [WorkItemWidgetLinkedItems](../../../api/graphql/reference/index.md#workitemwidgetlinkeditems) | List of work items added as related to a given work item, with possible relationship types being `relates_to`, `blocks`, and `blocked_by`. Includes support for individual counts of blocked status, blocked by, blocking, and related to. | `linked_work_items` |
|
||||
| [WorkItemWidgetMilestone](../../../api/graphql/reference/index.md#workitemwidgetmilestone) | Milestone assignment support for work item | |
|
||||
| [WorkItemWidgetNotes](../../../api/graphql/reference/index.md#workitemwidgetnotes) | List of discussions within a work item | |
|
||||
| [WorkItemWidgetNotifications](../../../api/graphql/reference/index.md#workitemwidgetnotifications) | Notifications subscription status of a work item for current user | |
|
||||
| [WorkItemWidgetProgress](../../../api/graphql/reference/index.md#workitemwidgetprogress) | Progress value of a work item. **Note:** Progress is currently available only for OKRs. | `okrs_mvc` |
|
||||
| [WorkItemWidgetStartAndDueDate](../../../api/graphql/reference/index.md#workitemwidgetstartandduedate) | Set start and due dates for a work item | |
|
||||
| [WorkItemWidgetStatus](../../../api/graphql/reference/index.md#workitemwidgetstatus) | Status of a work item when type is Requirement, with possible status types being `unverified`, `satisfied`, or `failed` | |
|
||||
| [WorkItemWidgetTestReports](../../../api/graphql/reference/index.md#workitemwidgettestreports) | Test reports associated with a work item | |
|
||||
| [WorkItemWidgetWeight](../../../api/graphql/reference/index.md#workitemwidgetweight) | Set weight of a work item | |
|
||||
| Widget | Description | Feature flag | Write permission | GraphQL Subscription Support |
|
||||
|---|---|---|---|---|
|
||||
| [WorkItemWidgetAssignees](../../../api/graphql/reference/index.md#workitemwidgetassignees) | List of work item assignees | |`Guest`|Yes|
|
||||
| [WorkItemWidgetAwardEmoji](../../../api/graphql/reference/index.md#workitemwidgetawardemoji) | Emoji reactions added to work item, including support for upvote/downvote counts | |Anyone who can view|No|
|
||||
| [WorkItemWidgetCurrentUserTodos](../../../api/graphql/reference/index.md#workitemwidgetcurrentusertodos) | User todo state of work item | |Anyone who can view|No|
|
||||
| [WorkItemWidgetDescription](../../../api/graphql/reference/index.md#workitemwidgetdescription) | Description of work item, including support for edited state, timestamp, and author | |`Reporter`|No|
|
||||
| [WorkItemWidgetHealthStatus](../../../api/graphql/reference/index.md#workitemwidgethealthstatus) | Health status assignment support for work item | |`Reporter`|No|
|
||||
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | Hierarchy of work items, including support for boolean representing presence of children. **Note:** Hierarchy is currently available only for OKRs. | `okrs_mvc` |`Guest`|No|
|
||||
| [WorkItemWidgetIteration](../../../api/graphql/reference/index.md#workitemwidgetiteration) | Iteration assignment support for work item | |`Reporter`|No|
|
||||
| [WorkItemWidgetLabels](../../../api/graphql/reference/index.md#workitemwidgetlabels) | List of labels added to work items, including support for checking whether scoped labels are supported | |`Reporter`|Yes|
|
||||
| [WorkItemWidgetLinkedItems](../../../api/graphql/reference/index.md#workitemwidgetlinkeditems) | List of work items added as related to a given work item, with possible relationship types being `relates_to`, `blocks`, and `blocked_by`. Includes support for individual counts of blocked status, blocked by, blocking, and related to. | `linked_work_items`|`Guest`|No|
|
||||
| [WorkItemWidgetMilestone](../../../api/graphql/reference/index.md#workitemwidgetmilestone) | Milestone assignment support for work item | |`Reporter`|No|
|
||||
| [WorkItemWidgetNotes](../../../api/graphql/reference/index.md#workitemwidgetnotes) | List of discussions within a work item | |`Guest`|Yes|
|
||||
| [WorkItemWidgetNotifications](../../../api/graphql/reference/index.md#workitemwidgetnotifications) | Notifications subscription status of a work item for current user | |Anyone who can view|No|
|
||||
| [WorkItemWidgetProgress](../../../api/graphql/reference/index.md#workitemwidgetprogress) | Progress value of a work item. **Note:** Progress is currently available only for OKRs. | `okrs_mvc` |`Reporter`|No|
|
||||
| [WorkItemWidgetStartAndDueDate](../../../api/graphql/reference/index.md#workitemwidgetstartandduedate) | Set start and due dates for a work item | |`Reporter`|No|
|
||||
| [WorkItemWidgetStatus](../../../api/graphql/reference/index.md#workitemwidgetstatus) | Status of a work item when type is Requirement, with possible status types being `unverified`, `satisfied`, or `failed` | | |No|
|
||||
| [WorkItemWidgetTestReports](../../../api/graphql/reference/index.md#workitemwidgettestreports) | Test reports associated with a work item | | | |
|
||||
| [WorkItemWidgetWeight](../../../api/graphql/reference/index.md#workitemwidgetweight) | Set weight of a work item | |`Reporter`|No|
|
||||
|
||||
### Work item relationships
|
||||
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ coverage-jdk11:
|
|||
# The `visualize` stage does not exist by default.
|
||||
# Please define it first, or choose an existing stage like `deploy`.
|
||||
stage: visualize
|
||||
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
|
||||
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.9
|
||||
script:
|
||||
# convert report from jacoco to cobertura, using relative project path
|
||||
- python /opt/cover2cover.py target/site/jacoco/jacoco.xml $CI_PROJECT_DIR/src/main/java/ > target/site/cobertura.xml
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ module Gitlab
|
|||
RUNNER_JOB_TOKEN_PARAM = :token
|
||||
PATH_DEPENDENT_FEED_TOKEN_REGEX = /\A#{User::FEED_TOKEN_PREFIX}(\h{64})-(\d+)\z/
|
||||
|
||||
PARAM_TOKEN_KEYS = [
|
||||
PRIVATE_TOKEN_PARAM,
|
||||
JOB_TOKEN_PARAM,
|
||||
RUNNER_JOB_TOKEN_PARAM
|
||||
].map(&:to_s).freeze
|
||||
HEADER_TOKEN_KEYS = [
|
||||
PRIVATE_TOKEN_HEADER,
|
||||
JOB_TOKEN_HEADER,
|
||||
DEPLOY_TOKEN_HEADER
|
||||
].freeze
|
||||
|
||||
# Check the Rails session for valid authentication details
|
||||
def find_user_from_warden
|
||||
current_request.env['warden']&.authenticate if verified_request?
|
||||
|
|
@ -204,6 +215,12 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def authentication_token_present?
|
||||
PARAM_TOKEN_KEYS.intersection(current_request.params.keys).any? ||
|
||||
HEADER_TOKEN_KEYS.intersection(current_request.env.keys).any? ||
|
||||
parsed_oauth_token.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_user_from_job_bearer_token
|
||||
|
|
|
|||
|
|
@ -7,15 +7,6 @@ module Gitlab
|
|||
def config_fallback
|
||||
Queues
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redis
|
||||
primary_store = ::Redis.new(params)
|
||||
secondary_store = ::Redis.new(config_fallback.params)
|
||||
|
||||
MultiStore.new(primary_store, secondary_store, name.demodulize)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -257,12 +257,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def with_redis(&block)
|
||||
if Feature.enabled?(:use_primary_and_secondary_stores_for_queues_metadata) ||
|
||||
Feature.enabled?(:use_primary_store_as_default_for_queues_metadata)
|
||||
Gitlab::Redis::QueuesMetadata.with(&block) # rubocop:disable CodeReuse/ActiveRecord
|
||||
else
|
||||
Gitlab::Redis::Queues.with(&block) # rubocop:disable Cop/RedisQueueUsage, CodeReuse/ActiveRecord
|
||||
end
|
||||
Gitlab::Redis::QueuesMetadata.with(&block) # rubocop:disable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28931,6 +28931,9 @@ msgstr ""
|
|||
msgid "Maximum project import requests per minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum projects allowed in the filter is %{count}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum push size"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33936,6 +33939,12 @@ msgstr ""
|
|||
msgid "PerformanceBar|Bullet notifications"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|CPU"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|CPU flamegraph"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|ClickHouse queries"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33945,6 +33954,12 @@ msgstr ""
|
|||
msgid "PerformanceBar|Download"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Download memory report"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Download report"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Elasticsearch calls"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33966,6 +33981,12 @@ msgstr ""
|
|||
msgid "PerformanceBar|Memory report"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Object"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Object flamegraph"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Redis calls"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33990,16 +34011,16 @@ msgstr ""
|
|||
msgid "PerformanceBar|Trace"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Wall"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Wall flamegraph"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|Zoekt calls"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|cpu"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|object"
|
||||
msgstr ""
|
||||
|
||||
msgid "PerformanceBar|wall"
|
||||
msgid "PerformanceBar|flamegraph"
|
||||
msgstr ""
|
||||
|
||||
msgid "Period in seconds"
|
||||
|
|
|
|||
|
|
@ -10,20 +10,20 @@ module QA
|
|||
super
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_header_filepath.vue' do
|
||||
element :file_title_content
|
||||
element 'file-title-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_content.vue' do
|
||||
element :blob_viewer_file_content
|
||||
element 'blob-viewer-file-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_header_default_actions.vue' do
|
||||
element :default_actions_container
|
||||
element :copy_contents_button
|
||||
element 'default-actions-container'
|
||||
element 'copy-contents-button'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue' do
|
||||
element :blob_viewer_file_content
|
||||
element 'blob-viewer-file-content'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -36,30 +36,30 @@ module QA
|
|||
end
|
||||
|
||||
def has_file_name?(file_name, file_number = nil)
|
||||
within_file_by_number(:file_title_content, file_number) { has_text?(file_name) }
|
||||
within_file_by_number('file-title-content', file_number) { has_text?(file_name) }
|
||||
end
|
||||
|
||||
def has_no_file_name?(file_name)
|
||||
within_element(:file_title_content) do
|
||||
within_element('file-title-content') do
|
||||
has_no_text?(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_content?(file_content, file_number = nil)
|
||||
within_file_by_number(:blob_viewer_file_content, file_number) { has_text?(file_content) }
|
||||
within_file_by_number('blob-viewer-file-content', file_number) { has_text?(file_content) }
|
||||
end
|
||||
|
||||
def has_no_file_content?(file_content)
|
||||
within_element(:blob_viewer_file_content) do
|
||||
within_element('blob-viewer-file-content') do
|
||||
has_no_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
||||
def has_normalized_ws_text?(text, wait: Capybara.default_max_wait_time)
|
||||
if has_element?(:blob_viewer_file_content, wait: 1)
|
||||
if has_element?('blob-viewer-file-content', wait: 1)
|
||||
# The blob viewer renders line numbers and whitespace in a way that doesn't match the source file
|
||||
# This isn't a visual validation test, so we ignore line numbers and whitespace
|
||||
find_element(:blob_viewer_file_content, wait: 0).text.gsub(/^\d+\s|\s*/, '')
|
||||
find_element('blob-viewer-file-content', wait: 0).text.gsub(/^\d+\s|\s*/, '')
|
||||
.start_with?(text.gsub(/\s*/, ''))
|
||||
else
|
||||
has_text?(text.gsub(/\s+/, " "), wait: wait)
|
||||
|
|
@ -67,7 +67,7 @@ module QA
|
|||
end
|
||||
|
||||
def click_copy_file_contents(file_number = nil)
|
||||
within_file_by_number(:default_actions_container, file_number) { click_element(:copy_contents_button) }
|
||||
within_file_by_number('default-actions-container', file_number) { click_element('copy-contents-button') }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ module QA
|
|||
|
||||
base.view 'app/views/shared/_zen.html.haml' do
|
||||
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
|
||||
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
|
||||
element :_, "testid = local_assigns.fetch(:testid, '')" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do
|
||||
|
|
|
|||
|
|
@ -14,78 +14,75 @@ module QA
|
|||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_title.vue' do
|
||||
element :snippet_title_content
|
||||
element 'snippet-title-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_description_view.vue' do
|
||||
element :snippet_description_content
|
||||
element 'snippet-description-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_container
|
||||
element 'snippet-container'
|
||||
element 'snippet-action-button'
|
||||
element 'delete-snippet-button'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_header_filepath.vue' do
|
||||
element :file_title_content
|
||||
element 'file-title-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_content.vue' do
|
||||
element :blob_viewer_file_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_header.vue' do
|
||||
element :snippet_action_button
|
||||
element :delete_snippet_button
|
||||
element 'blob-viewer-file-content'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/show.vue' do
|
||||
element :clone_button
|
||||
element :snippet_embed_dropdown
|
||||
element 'clone-button'
|
||||
element 'snippet-embed-dropdown'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/clone_dropdown/clone_dropdown.vue' do
|
||||
element :copy_http_url_button
|
||||
element :copy_ssh_url_button
|
||||
element 'copy-http-url-button'
|
||||
element 'copy-ssh-url-button'
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_comment_button.html.haml' do
|
||||
element :comment_button
|
||||
element 'comment-button'
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_form.html.haml' do
|
||||
element :note_field
|
||||
element 'note-field'
|
||||
end
|
||||
|
||||
base.view 'app/views/snippets/notes/_actions.html.haml' do
|
||||
element :edit_comment_button
|
||||
base.view 'app/views/projects/notes/_actions.html.haml' do
|
||||
element 'edit-comment-button'
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_edit_form.html.haml' do
|
||||
element :edit_note_field
|
||||
element :save_comment_button
|
||||
element 'edit-note-field'
|
||||
element 'save-comment-button'
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_note.html.haml' do
|
||||
element :note_content
|
||||
element :note_author_content
|
||||
element 'note-content'
|
||||
element 'note-author-content'
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/notes/_notes_with_form.html.haml' do
|
||||
element :notes_list
|
||||
element 'notes-list'
|
||||
end
|
||||
|
||||
base.view 'app/views/projects/notes/_more_actions_dropdown.html.haml' do
|
||||
element :more_actions_dropdown
|
||||
element :delete_comment_button
|
||||
element 'more-actions-dropdown'
|
||||
element 'delete-comment-button'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/embed_dropdown.vue' do
|
||||
element :copy_button
|
||||
element 'copy-button'
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/blob/components/blob_header_default_actions.vue' do
|
||||
element :default_actions_container
|
||||
element :copy_contents_button
|
||||
element 'default-actions-container'
|
||||
element 'copy-contents-button'
|
||||
end
|
||||
|
||||
base.view 'app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml' do
|
||||
|
|
@ -95,30 +92,30 @@ module QA
|
|||
end
|
||||
|
||||
def has_snippet_title?(snippet_title)
|
||||
has_element?(:snippet_title_content, text: snippet_title, wait: 10)
|
||||
has_element?('snippet-title-content', text: snippet_title, wait: 10)
|
||||
end
|
||||
|
||||
def has_snippet_description?(snippet_description)
|
||||
has_element? :snippet_description_content, text: snippet_description
|
||||
has_element? 'snippet-description-content', text: snippet_description
|
||||
end
|
||||
|
||||
def has_no_snippet_description?
|
||||
has_no_element?(:snippet_description_field)
|
||||
has_no_element?('snippet-description-content')
|
||||
end
|
||||
|
||||
def has_visibility_type?(visibility_type)
|
||||
within_element(:snippet_container) do
|
||||
within_element('snippet-container') do
|
||||
has_text?(visibility_type)
|
||||
end
|
||||
end
|
||||
|
||||
def has_file_name?(file_name, file_number = nil)
|
||||
if file_number
|
||||
within_element_by_index(:file_title_content, file_number - 1) do
|
||||
within_element_by_index('file-title-content', file_number - 1) do
|
||||
has_text?(file_name)
|
||||
end
|
||||
else
|
||||
within_element(:file_title_content) do
|
||||
within_element('file-title-content') do
|
||||
has_text?(file_name)
|
||||
end
|
||||
end
|
||||
|
|
@ -126,11 +123,11 @@ module QA
|
|||
|
||||
def has_no_file_name?(file_name, file_number = nil)
|
||||
if file_number
|
||||
within_element_by_index(:file_title_content, file_number - 1) do
|
||||
within_element_by_index('file-title-content', file_number - 1) do
|
||||
has_no_text?(file_name)
|
||||
end
|
||||
else
|
||||
within_element(:file_title_content) do
|
||||
within_element('file-title-content') do
|
||||
has_no_text?(file_name)
|
||||
end
|
||||
end
|
||||
|
|
@ -138,11 +135,11 @@ module QA
|
|||
|
||||
def has_file_content?(file_content, file_number = nil)
|
||||
if file_number
|
||||
within_element_by_index(:blob_viewer_file_content, file_number - 1) do
|
||||
within_element_by_index('blob-viewer-file-content', file_number - 1) do
|
||||
has_text?(file_content)
|
||||
end
|
||||
else
|
||||
within_element(:blob_viewer_file_content) do
|
||||
within_element('blob-viewer-file-content') do
|
||||
has_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
|
@ -150,11 +147,11 @@ module QA
|
|||
|
||||
def has_no_file_content?(file_content, file_number = nil)
|
||||
if file_number
|
||||
within_element_by_index(:blob_viewer_file_content, file_number - 1) do
|
||||
within_element_by_index('blob-viewer-file-content', file_number - 1) do
|
||||
has_no_text?(file_content)
|
||||
end
|
||||
else
|
||||
within_element(:blob_viewer_file_content) do
|
||||
within_element('blob-viewer-file-content') do
|
||||
has_no_text?(file_content)
|
||||
end
|
||||
end
|
||||
|
|
@ -162,112 +159,112 @@ module QA
|
|||
|
||||
RSpec::Matchers.define :have_embed_dropdown do
|
||||
match do |page|
|
||||
page.has_element?(:snippet_embed_dropdown)
|
||||
page.has_element?('snippet-embed-dropdown')
|
||||
end
|
||||
|
||||
match_when_negated do |page|
|
||||
page.has_no_element?(:snippet_embed_dropdown)
|
||||
page.has_no_element?('snippet-embed-dropdown')
|
||||
end
|
||||
end
|
||||
|
||||
def click_edit_button
|
||||
click_element(:snippet_action_button, Page::Dashboard::Snippet::Edit, action: 'Edit')
|
||||
click_element('snippet-action-button', Page::Dashboard::Snippet::Edit, action: 'Edit')
|
||||
end
|
||||
|
||||
def click_delete_button
|
||||
click_element(:snippet_action_button, action: 'Delete')
|
||||
click_element(:delete_snippet_button)
|
||||
click_element('snippet-action-button', action: 'Delete')
|
||||
click_element('delete-snippet-button')
|
||||
# wait for the page to reload after deletion
|
||||
wait_until(reload: false) do
|
||||
has_no_element?(:delete_snippet_button) &&
|
||||
has_no_element?(:snippet_action_button, action: 'Delete')
|
||||
has_no_element?('delete-snippet-button') &&
|
||||
has_no_element?('snippet-action-button', action: 'Delete')
|
||||
end
|
||||
end
|
||||
|
||||
def get_repository_uri_http
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_http_url_button)['data-clipboard-text']).uri.to_s
|
||||
click_element('clone-button')
|
||||
Git::Location.new(find_element('copy-http-url-button')['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
|
||||
def get_repository_uri_ssh
|
||||
click_element(:clone_button)
|
||||
Git::Location.new(find_element(:copy_ssh_url_button)['data-clipboard-text']).uri.to_s
|
||||
click_element('clone-button')
|
||||
Git::Location.new(find_element('copy-ssh-url-button')['data-clipboard-text']).uri.to_s
|
||||
end
|
||||
|
||||
def get_sharing_link
|
||||
click_element(:snippet_embed_dropdown)
|
||||
find_element(:copy_button, action: 'Share')['data-clipboard-text']
|
||||
click_element('snippet-embed-dropdown')
|
||||
find_element('copy-button', action: 'Share')['data-clipboard-text']
|
||||
end
|
||||
|
||||
def add_comment(comment)
|
||||
fill_element(:note_field, comment)
|
||||
click_element(:comment_button)
|
||||
fill_element('note-field', comment)
|
||||
click_element('comment-button')
|
||||
|
||||
unless has_element?(:note_author_content)
|
||||
unless has_element?('note-author-content')
|
||||
raise ElementNotFound, "Comment did not appear as expected"
|
||||
end
|
||||
end
|
||||
|
||||
def has_comment_author?(author_username)
|
||||
within_element(:note_author_content) do
|
||||
within_element('note-author-content') do
|
||||
has_text?('@' + author_username)
|
||||
end
|
||||
end
|
||||
|
||||
def has_comment_content?(comment_content)
|
||||
within_element(:note_content) do
|
||||
within_element('note-content') do
|
||||
has_text?(comment_content)
|
||||
end
|
||||
end
|
||||
|
||||
def within_notes_list(&block)
|
||||
within_element :notes_list, &block
|
||||
within_element 'notes-list', &block
|
||||
end
|
||||
|
||||
def has_syntax_highlighting?(language)
|
||||
within_element(:blob_viewer_file_content) do
|
||||
within_element('blob-viewer-file-content') do
|
||||
find('.line')['lang'].to_s == language
|
||||
end
|
||||
end
|
||||
|
||||
def edit_comment(comment)
|
||||
click_element(:edit_comment_button)
|
||||
fill_element(:edit_note_field, comment)
|
||||
click_element(:save_comment_button)
|
||||
click_element('edit-comment-button')
|
||||
fill_element('edit-note-field', comment)
|
||||
click_element('save-comment-button')
|
||||
|
||||
unless has_element?(:note_author_content)
|
||||
unless has_element?('note-author-content')
|
||||
raise ElementNotFound, "Comment did not appear as expected"
|
||||
end
|
||||
end
|
||||
|
||||
def delete_comment(comment)
|
||||
click_element(:more_actions_dropdown)
|
||||
click_element(:delete_comment_button)
|
||||
click_element('more-actions-dropdown')
|
||||
click_element('delete-comment-button')
|
||||
click_confirmation_ok_button
|
||||
|
||||
unless has_no_element?(:note_content, text: comment)
|
||||
unless has_no_element?('note-content', text: comment)
|
||||
raise ElementNotFound, "Comment was not removed as expected"
|
||||
end
|
||||
end
|
||||
|
||||
def click_copy_file_contents(file_number = nil)
|
||||
if file_number
|
||||
within_element_by_index(:default_actions_container, file_number - 1) do
|
||||
click_element(:copy_contents_button)
|
||||
within_element_by_index('default-actions-container', file_number - 1) do
|
||||
click_element('copy-contents-button')
|
||||
end
|
||||
else
|
||||
within_element(:default_actions_container) do
|
||||
click_element(:copy_contents_button)
|
||||
within_element('default-actions-container') do
|
||||
click_element('copy-contents-button')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def copy_file_contents_to_comment(file_number = nil)
|
||||
click_copy_file_contents(file_number)
|
||||
send_keys_to_element(:note_field, [:shift, :insert])
|
||||
click_element(:comment_button)
|
||||
send_keys_to_element('note-field', [:shift, :insert])
|
||||
click_element('comment-button')
|
||||
|
||||
unless has_element?(:note_author_content)
|
||||
unless has_element?('note-author-content')
|
||||
raise ElementNotFound, "Comment did not appear as expected"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module QA
|
|||
include Page::Component::BlobContent
|
||||
|
||||
view 'app/assets/javascripts/snippets/components/snippet_title.vue' do
|
||||
element :snippet_title_content
|
||||
element 'snippet-title-content'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module QA
|
|||
include Page::Component::BlobContent
|
||||
|
||||
view 'app/views/projects/notes/_actions.html.haml' do
|
||||
element :edit_comment_button
|
||||
element 'edit-comment-button'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -317,6 +317,73 @@ RSpec.describe GraphqlController, feature_category: :integrations do
|
|||
|
||||
subject { post :execute, params: { query: query, access_token: token.token } }
|
||||
|
||||
shared_examples 'invalid token' do
|
||||
it 'returns 401 with invalid token message' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
expect_graphql_errors_to_include('Invalid token')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid token' do
|
||||
context 'with auth header' do
|
||||
subject do
|
||||
request.headers[header] = 'invalid'
|
||||
post :execute, params: { query: query, user: nil }
|
||||
end
|
||||
|
||||
context 'with private-token' do
|
||||
let(:header) { 'Private-Token' }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
|
||||
context 'with job-token' do
|
||||
let(:header) { 'Job-Token' }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
|
||||
context 'with deploy-token' do
|
||||
let(:header) { 'Deploy-Token' }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with authorization bearer (oauth token)' do
|
||||
subject do
|
||||
request.headers['Authorization'] = 'Bearer invalid'
|
||||
post :execute, params: { query: query, user: nil }
|
||||
end
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
|
||||
context 'with auth param' do
|
||||
subject { post :execute, params: { query: query, user: nil }.merge(header) }
|
||||
|
||||
context 'with private_token' do
|
||||
let(:header) { { private_token: 'invalid' } }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
|
||||
context 'with job_token' do
|
||||
let(:header) { { job_token: 'invalid' } }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
let(:header) { { token: 'invalid' } }
|
||||
|
||||
it_behaves_like 'invalid token'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a project bot' do
|
||||
let(:user) { create(:user, :project_bot, last_activity_on: last_activity_on) }
|
||||
|
||||
|
|
|
|||
|
|
@ -188,4 +188,47 @@ RSpec.describe 'Dashboard > User filters todos', :js, feature_category: :team_pl
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'todos tab count' do
|
||||
context 'when filtering by open todos' do
|
||||
it 'includes all open todos' do
|
||||
expect(find('.js-todos-pending .gl-badge')).to have_content('3')
|
||||
end
|
||||
|
||||
it 'only counts open todos that match when filtered by project' do
|
||||
click_button 'Project'
|
||||
|
||||
within '.dropdown-menu-project' do
|
||||
fill_in 'Search projects', with: project_1.full_name
|
||||
click_link project_1.full_name
|
||||
end
|
||||
|
||||
expect(find('.js-todos-pending .gl-badge')).to have_content('1')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filtering by done todos' do
|
||||
before do
|
||||
create(:todo, user: user_1, author: user_2, project: project_1, target: issue1, action: 1, state: :done)
|
||||
create(:todo, user: user_1, author: user_1, project: project_2, target: merge_request, action: 2, state: :done)
|
||||
|
||||
visit dashboard_todos_path(state: 'done')
|
||||
end
|
||||
|
||||
it 'includes all done todos' do
|
||||
expect(find('.js-todos-done .gl-badge')).to have_content('2')
|
||||
end
|
||||
|
||||
it 'only counts done todos that match when filtered by project' do
|
||||
click_button 'Project'
|
||||
|
||||
within '.dropdown-menu-project' do
|
||||
fill_in 'Search projects', with: project_1.full_name
|
||||
click_link project_1.full_name
|
||||
end
|
||||
|
||||
expect(find('.js-todos-done .gl-badge')).to have_content('1')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ exports[`Blob Header Filepath rendering matches the snapshot 1`] = `
|
|||
/>
|
||||
<strong
|
||||
class="file-title-name js-blob-header-filepath mr-1"
|
||||
data-qa-selector="file_title_content"
|
||||
data-testid="file-title-content"
|
||||
>
|
||||
foo/bar/dummy.md
|
||||
</strong>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ describe('Blob Header Default Actions', () => {
|
|||
});
|
||||
|
||||
describe('renders', () => {
|
||||
const findCopyButton = () => wrapper.findByTestId('copyContentsButton');
|
||||
const findCopyButton = () => wrapper.findByTestId('copy-contents-button');
|
||||
const findViewRawButton = () => wrapper.findByTestId('viewRawButton');
|
||||
|
||||
it('gl-button-group component', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import RequestWarning from '~/performance_bar/components/request_warning.vue';
|
||||
|
||||
Vue.config.ignoredElements = ['gl-emoji'];
|
||||
|
||||
describe('request warning', () => {
|
||||
let wrapper;
|
||||
const htmlId = 'request-123';
|
||||
|
|
@ -16,8 +19,8 @@ describe('request warning', () => {
|
|||
});
|
||||
|
||||
it('adds a warning emoji with the correct ID', () => {
|
||||
expect(wrapper.find('span[id]').attributes('id')).toEqual(htmlId);
|
||||
expect(wrapper.find('span[id] gl-emoji').element.dataset.name).toEqual('warning');
|
||||
expect(wrapper.find('span gl-emoji[id]').attributes('id')).toEqual(htmlId);
|
||||
expect(wrapper.find('span gl-emoji[id]').element.dataset.name).toEqual('warning');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import Vue from 'vue';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -6,6 +7,8 @@ import '~/performance_bar/components/performance_bar_app.vue';
|
|||
import performanceBar from '~/performance_bar';
|
||||
import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
|
||||
|
||||
Vue.config.ignoredElements = ['gl-emoji'];
|
||||
|
||||
jest.mock('~/performance_bar/performance_bar_log');
|
||||
|
||||
describe('performance bar wrapper', () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
exports[`Snippet Description component matches the snapshot 1`] = `
|
||||
<markdown-field-view-stub
|
||||
class="snippet-description"
|
||||
data-qa-selector="snippet_description_content"
|
||||
data-testid="snippet-description-content"
|
||||
>
|
||||
<div
|
||||
class="js-snippet-description md"
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ describe('Snippet header component', () => {
|
|||
expect(findDeleteModal().props().visible).toBe(true);
|
||||
|
||||
// Click delete button in delete modal
|
||||
document.querySelector('[data-testid="delete-snippet"').click();
|
||||
document.querySelector('[data-testid="delete-snippet-button"').click();
|
||||
await waitForPromises();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ describe('Clone Dropdown Button', () => {
|
|||
let wrapper;
|
||||
const link = 'ssh://foo.bar';
|
||||
const label = 'SSH';
|
||||
const qaSelector = 'some-selector';
|
||||
const testId = 'some-selector';
|
||||
const defaultPropsData = {
|
||||
link,
|
||||
label,
|
||||
qaSelector,
|
||||
testId,
|
||||
};
|
||||
|
||||
const findCopyButton = () => wrapper.findComponent(GlButton);
|
||||
|
|
@ -46,7 +46,7 @@ describe('Clone Dropdown Button', () => {
|
|||
});
|
||||
|
||||
it('sets the qa selector', () => {
|
||||
expect(findCopyButton().attributes('data-qa-selector')).toBe(qaSelector);
|
||||
expect(findCopyButton().attributes('data-testid')).toBe(testId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1149,4 +1149,75 @@ RSpec.describe Gitlab::Auth::AuthFinders, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#authentication_token_present?' do
|
||||
subject { authentication_token_present? }
|
||||
|
||||
context 'no auth header/param/oauth' do
|
||||
before do
|
||||
request.headers['Random'] = 'Something'
|
||||
set_param(:random, 'something')
|
||||
end
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
end
|
||||
|
||||
context 'with auth header' do
|
||||
before do
|
||||
request.headers[header] = 'invalid'
|
||||
end
|
||||
|
||||
context 'with private-token' do
|
||||
let(:header) { 'Private-Token' }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'with job-token' do
|
||||
let(:header) { 'Job-Token' }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'with deploy-token' do
|
||||
let(:header) { 'Deploy-Token' }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with authorization bearer (oauth token)' do
|
||||
before do
|
||||
request.headers['Authorization'] = 'Bearer invalid'
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'with auth param' do
|
||||
context 'with private_token' do
|
||||
it 'returns true' do
|
||||
set_param(:private_token, 'invalid')
|
||||
|
||||
expect(subject).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with job_token' do
|
||||
it 'returns true' do
|
||||
set_param(:job_token, 'invalid')
|
||||
|
||||
expect(subject).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
it 'returns true' do
|
||||
set_param(:token, 'invalid')
|
||||
|
||||
expect(subject).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,39 +5,4 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Redis::QueuesMetadata, feature_category: :redis do
|
||||
include_examples "redis_new_instance_shared_examples", 'queues_metadata', Gitlab::Redis::Queues
|
||||
include_examples "redis_shared_examples"
|
||||
|
||||
describe '#pool' do
|
||||
let(:config_new_format_host) { "spec/fixtures/config/redis_new_format_host.yml" }
|
||||
let(:config_new_format_socket) { "spec/fixtures/config/redis_new_format_socket.yml" }
|
||||
|
||||
subject { described_class.pool }
|
||||
|
||||
around do |example|
|
||||
clear_pool
|
||||
example.run
|
||||
ensure
|
||||
clear_pool
|
||||
end
|
||||
|
||||
before do
|
||||
allow(described_class).to receive(:config_file_name).and_return(config_new_format_host)
|
||||
|
||||
allow(described_class).to receive(:config_file_name).and_return(config_new_format_host)
|
||||
allow(Gitlab::Redis::Queues).to receive(:config_file_name).and_return(config_new_format_socket)
|
||||
end
|
||||
|
||||
it 'instantiates an instance of MultiStore' do
|
||||
subject.with do |redis_instance|
|
||||
expect(redis_instance).to be_instance_of(::Gitlab::Redis::MultiStore)
|
||||
|
||||
expect(redis_instance.primary_store.connection[:id]).to eq("redis://test-host:6379/99")
|
||||
expect(redis_instance.secondary_store.connection[:id]).to eq("unix:///path/to/redis.sock/0")
|
||||
|
||||
expect(redis_instance.instance_name).to eq('QueuesMetadata')
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'multi store feature flags', :use_primary_and_secondary_stores_for_queues_metadata,
|
||||
:use_primary_store_as_default_for_queues_metadata
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob,
|
||||
:clean_gitlab_redis_queues, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_queues_metadata,
|
||||
feature_category: :shared do
|
||||
:clean_gitlab_redis_queues_metadata, feature_category: :shared do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
subject(:duplicate_job) do
|
||||
|
|
@ -79,7 +78,11 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob,
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'with Redis cookies' do
|
||||
context 'with Redis cookies' do
|
||||
def with_redis(&block)
|
||||
Gitlab::Redis::QueuesMetadata.with(&block)
|
||||
end
|
||||
|
||||
let(:cookie_key) { "#{Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE}:#{idempotency_key}:cookie:v2" }
|
||||
let(:cookie) { get_redis_msgpack(cookie_key) }
|
||||
|
||||
|
|
@ -413,62 +416,6 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob,
|
|||
end
|
||||
end
|
||||
|
||||
context 'with multi-store feature flags turned on' do
|
||||
def with_redis(&block)
|
||||
Gitlab::Redis::QueuesMetadata.with(&block)
|
||||
end
|
||||
|
||||
shared_examples 'uses QueuesMetadata' do
|
||||
it 'use Gitlab::Redis::QueuesMetadata.with' do
|
||||
expect(Gitlab::Redis::QueuesMetadata).to receive(:with).and_call_original
|
||||
expect(Gitlab::Redis::Queues).not_to receive(:with)
|
||||
|
||||
duplicate_job.check!
|
||||
end
|
||||
end
|
||||
|
||||
context 'when migration is ongoing with double-write' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_queues_metadata: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'uses QueuesMetadata'
|
||||
it_behaves_like 'with Redis cookies'
|
||||
end
|
||||
|
||||
context 'when migration is completed' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_queues_metadata: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'uses QueuesMetadata'
|
||||
it_behaves_like 'with Redis cookies'
|
||||
end
|
||||
|
||||
it_behaves_like 'uses QueuesMetadata'
|
||||
it_behaves_like 'with Redis cookies'
|
||||
end
|
||||
|
||||
context 'when both multi-store feature flags are off' do
|
||||
def with_redis(&block)
|
||||
Gitlab::Redis::Queues.with(&block)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_queues_metadata: false)
|
||||
stub_feature_flags(use_primary_store_as_default_for_queues_metadata: false)
|
||||
end
|
||||
|
||||
it 'use Gitlab::Redis::Queues' do
|
||||
expect(Gitlab::Redis::Queues).to receive(:with).and_call_original
|
||||
expect(Gitlab::Redis::QueuesMetadata).not_to receive(:with)
|
||||
|
||||
duplicate_job.check!
|
||||
end
|
||||
|
||||
it_behaves_like 'with Redis cookies'
|
||||
end
|
||||
|
||||
describe '#scheduled?' do
|
||||
it 'returns false for non-scheduled jobs' do
|
||||
expect(duplicate_job.scheduled?).to be(false)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do
|
|||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:description).is_at_most(2000) }
|
||||
it { is_expected.to validate_length_of(:description).is_at_most(255) }
|
||||
it { is_expected.to validate_presence_of(:path) }
|
||||
it { is_expected.to validate_length_of(:path).is_at_most(255) }
|
||||
it { is_expected.to validate_presence_of(:owner) }
|
||||
|
|
|
|||
|
|
@ -282,9 +282,9 @@ RSpec.describe 'GraphQL', feature_category: :shared do
|
|||
it 'does not authenticate user' do
|
||||
post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
|
||||
expect(graphql_data['echo']).to eq('nil says: Hello world')
|
||||
expect_graphql_errors_to_include('Invalid token')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -308,9 +308,9 @@ RSpec.describe 'GraphQL', feature_category: :shared do
|
|||
|
||||
post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
|
||||
expect(graphql_data['echo']).to eq('nil says: Hello world')
|
||||
expect_graphql_errors_to_include('Invalid token')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue