Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9b5bb10544
commit
d562dddd65
|
|
@ -158,6 +158,9 @@ Dangerfile
|
|||
^[Distribution] @gitlab-org/distribution
|
||||
/lib/support/
|
||||
|
||||
[Upgrade path] @gitlab-org/distribution
|
||||
/config/upgrade_path.yml
|
||||
|
||||
# Secure & Threat Management ownership delineation
|
||||
# https://about.gitlab.com/handbook/engineering/development/threat-management/delineate-secure-threat-management.html#technical-boundaries
|
||||
^[Threat Insights backend] @gitlab-org/govern/threat-insights-backend-team
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
|
||||
.ai-gateway-services:
|
||||
services:
|
||||
- name: registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.5.0
|
||||
- name: registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.6.0
|
||||
alias: ai-gateway
|
||||
|
||||
.use-pg13:
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ update-minor:
|
|||
- .update-script
|
||||
variables:
|
||||
UPDATE_TYPE: minor
|
||||
QA_RSPEC_TAGS: --tag smoke
|
||||
QA_RSPEC_TAGS: --tag health_check
|
||||
rules:
|
||||
- !reference [.rules:test:update, rules]
|
||||
- !reference [.rules:test:manual, rules]
|
||||
|
|
@ -480,7 +480,7 @@ update-major:
|
|||
- .update-script
|
||||
variables:
|
||||
UPDATE_TYPE: major
|
||||
QA_RSPEC_TAGS: --tag smoke
|
||||
QA_RSPEC_TAGS: --tag health_check
|
||||
rules:
|
||||
- !reference [.rules:test:update, rules]
|
||||
- !reference [.rules:test:manual, rules]
|
||||
|
|
@ -492,7 +492,7 @@ update-ee-to-ce:
|
|||
variables:
|
||||
UPDATE_TYPE: minor
|
||||
UPDATE_FROM_EDITION: ee
|
||||
QA_RSPEC_TAGS: --tag smoke
|
||||
QA_RSPEC_TAGS: --tag health_check
|
||||
rules:
|
||||
- !reference [.rules:test:ce-only, rules]
|
||||
- !reference [.rules:test:update, rules]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
query getPipelineMiniGraph($fullPath: ID!, $iid: ID!) {
|
||||
project(fullPath: $fullPath) {
|
||||
id
|
||||
pipeline(iid: $iid) {
|
||||
id
|
||||
path
|
||||
stages {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
detailedStatus {
|
||||
id
|
||||
icon
|
||||
group
|
||||
}
|
||||
}
|
||||
}
|
||||
upstream {
|
||||
id
|
||||
path
|
||||
project {
|
||||
id
|
||||
name
|
||||
}
|
||||
detailedStatus {
|
||||
id
|
||||
group
|
||||
icon
|
||||
label
|
||||
}
|
||||
}
|
||||
downstream {
|
||||
nodes {
|
||||
id
|
||||
path
|
||||
project {
|
||||
id
|
||||
name
|
||||
}
|
||||
detailedStatus {
|
||||
id
|
||||
group
|
||||
icon
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,7 @@ import { __ } from '~/locale';
|
|||
import { keepLatestDownstreamPipelines } from '~/ci/pipeline_details/utils/parsing_utils';
|
||||
import { getQueryHeaders, toggleQueryPollingByVisibility } from '~/ci/pipeline_details/graph/utils';
|
||||
import { PIPELINE_MINI_GRAPH_POLL_INTERVAL } from '~/ci/pipeline_details/constants';
|
||||
import getLinkedPipelinesQuery from '~/ci/pipeline_details/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import getPipelineStagesQuery from './graphql/queries/get_pipeline_stages.query.graphql';
|
||||
import getPipelineMiniGraphQuery from './graphql/queries/get_pipeline_mini_graph.query.graphql';
|
||||
import LinkedPipelinesMiniList from './linked_pipelines_mini_list.vue';
|
||||
import PipelineStages from './pipeline_stages.vue';
|
||||
/**
|
||||
|
|
@ -14,8 +13,7 @@ import PipelineStages from './pipeline_stages.vue';
|
|||
*/
|
||||
export default {
|
||||
i18n: {
|
||||
linkedPipelinesFetchError: __('There was a problem fetching linked pipelines.'),
|
||||
stagesFetchError: __('There was a problem fetching the pipeline stages.'),
|
||||
pipelineMiniGraphFetchError: __('There was a problem fetching the pipeline mini graph.'),
|
||||
},
|
||||
arrowStyles: ['arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 !gl-align-middle'],
|
||||
components: {
|
||||
|
|
@ -50,16 +48,15 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
linkedPipelines: null,
|
||||
pipelineStages: [],
|
||||
pipeline: {},
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
linkedPipelines: {
|
||||
pipeline: {
|
||||
context() {
|
||||
return getQueryHeaders(this.pipelineEtag);
|
||||
},
|
||||
query: getLinkedPipelinesQuery,
|
||||
query: getPipelineMiniGraphQuery,
|
||||
pollInterval() {
|
||||
return this.pollInterval;
|
||||
},
|
||||
|
|
@ -70,78 +67,39 @@ export default {
|
|||
};
|
||||
},
|
||||
update({ project }) {
|
||||
return project?.pipeline || this.linkedpipelines;
|
||||
return project?.pipeline || {};
|
||||
},
|
||||
error() {
|
||||
createAlert({ message: this.$options.i18n.linkedPipelinesFetchError });
|
||||
},
|
||||
},
|
||||
pipelineStages: {
|
||||
context() {
|
||||
return getQueryHeaders(this.pipelineEtag);
|
||||
},
|
||||
query: getPipelineStagesQuery,
|
||||
pollInterval() {
|
||||
return this.pollInterval;
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
fullPath: this.fullPath,
|
||||
iid: this.iid,
|
||||
};
|
||||
},
|
||||
update({ project }) {
|
||||
return project?.pipeline?.stages?.nodes || this.pipelineStages;
|
||||
},
|
||||
error() {
|
||||
createAlert({ message: this.$options.i18n.stagesFetchError });
|
||||
createAlert({ message: this.$options.i18n.pipelineMiniGraphFetchError });
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
downstreamPipelines() {
|
||||
return keepLatestDownstreamPipelines(this.linkedPipelines?.downstream?.nodes);
|
||||
return keepLatestDownstreamPipelines(this.pipeline?.downstream?.nodes);
|
||||
},
|
||||
formattedStages() {
|
||||
return this.pipelineStages.map((stage) => {
|
||||
const { name, detailedStatus } = stage;
|
||||
return {
|
||||
// TODO: Once we fetch stage by ID with GraphQL,
|
||||
// this method will change.
|
||||
// see https://gitlab.com/gitlab-org/gitlab/-/issues/384853
|
||||
id: stage.id,
|
||||
dropdown_path: `${this.pipelinePath}/stage.json?stage=${name}`,
|
||||
name,
|
||||
path: `${this.pipelinePath}#${name}`,
|
||||
status: {
|
||||
details_path: `${this.pipelinePath}#${name}`,
|
||||
has_details: detailedStatus?.hasDetails || false,
|
||||
...detailedStatus,
|
||||
},
|
||||
title: `${name}: ${detailedStatus?.text || ''}`,
|
||||
};
|
||||
});
|
||||
pipelineStages() {
|
||||
return this.pipeline?.stages?.nodes || [];
|
||||
},
|
||||
hasDownstreamPipelines() {
|
||||
return Boolean(this.downstreamPipelines.length);
|
||||
},
|
||||
pipelinePath() {
|
||||
return this.linkedPipelines?.path || '';
|
||||
return this.pipeline?.path || '';
|
||||
},
|
||||
upstreamPipeline() {
|
||||
return this.linkedPipelines?.upstream;
|
||||
return this.pipeline?.upstream;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
toggleQueryPollingByVisibility(this.$apollo.queries.linkedPipelines);
|
||||
toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStages);
|
||||
toggleQueryPollingByVisibility(this.$apollo.queries.pipeline);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-loading-icon v-if="$apollo.queries.pipelineStages.loading" />
|
||||
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
|
||||
<div v-else data-testid="pipeline-mini-graph">
|
||||
<linked-pipelines-mini-list
|
||||
v-if="upstreamPipeline"
|
||||
|
|
@ -158,7 +116,7 @@ export default {
|
|||
/>
|
||||
<pipeline-stages
|
||||
:is-merge-train="isMergeTrain"
|
||||
:stages="formattedStages"
|
||||
:stages="pipelineStages"
|
||||
@miniGraphStageClick="$emit('miniGraphStageClick')"
|
||||
/>
|
||||
<gl-icon
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
import { createAlert } from '~/alert';
|
||||
import { __ } from '~/locale';
|
||||
import { PIPELINE_MINI_GRAPH_POLL_INTERVAL } from '~/ci/pipeline_details/constants';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
|
||||
import { getQueryHeaders, toggleQueryPollingByVisibility } from '~/ci/pipeline_details/graph/utils';
|
||||
import getPipelineStageQuery from './graphql/queries/get_pipeline_stage.query.graphql';
|
||||
import JobItem from './job_item.vue';
|
||||
// import JobItem from './job_item.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
stageFetchError: __('There was a problem fetching the pipeline stage.'),
|
||||
},
|
||||
|
||||
components: {
|
||||
JobItem,
|
||||
// JobItem,
|
||||
CiIcon,
|
||||
},
|
||||
props: {
|
||||
isMergeTrain: {
|
||||
|
|
@ -29,15 +30,14 @@ export default {
|
|||
required: false,
|
||||
default: PIPELINE_MINI_GRAPH_POLL_INTERVAL,
|
||||
},
|
||||
stageId: {
|
||||
type: String,
|
||||
stage: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
jobs: [],
|
||||
stage: null,
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
|
|
@ -51,11 +51,12 @@ export default {
|
|||
},
|
||||
variables() {
|
||||
return {
|
||||
id: this.stageId,
|
||||
id: this.stage.id,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.stageId;
|
||||
// TODO: This query should occur on click
|
||||
return true;
|
||||
},
|
||||
update(data) {
|
||||
this.jobs = data?.ciPipelineStage?.jobs.nodes;
|
||||
|
|
@ -74,8 +75,14 @@ export default {
|
|||
|
||||
<template>
|
||||
<div data-testid="pipeline-stage">
|
||||
<ul v-for="job in jobs" :key="job.id">
|
||||
<ci-icon
|
||||
:status="stage.detailedStatus"
|
||||
:show-tooltip="false"
|
||||
:use-link="false"
|
||||
class="gl-mb-0!"
|
||||
/>
|
||||
<!-- <ul v-for="job in jobs" :key="job.id">
|
||||
<job-item :job="job" />
|
||||
</ul>
|
||||
</ul> -->
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ export default {
|
|||
<div class="gl-display-inline gl-align-middle">
|
||||
<div
|
||||
v-for="stage in stages"
|
||||
:key="stage.name"
|
||||
:key="stage.id"
|
||||
class="pipeline-mini-graph-stage-container dropdown gl-display-inline-flex gl-mr-2 gl-my-2 gl-align-middle"
|
||||
>
|
||||
<pipeline-stage
|
||||
:stage-id="stage.id"
|
||||
:stage="stage"
|
||||
:is-merge-train="isMergeTrain"
|
||||
:pipeline-etag="pipelineEtag"
|
||||
@miniGraphStageClick="$emit('miniGraphStageClick')"
|
||||
|
|
|
|||
|
|
@ -198,10 +198,10 @@ export const filtersMap = {
|
|||
[NORMAL_FILTER]: 'author_username',
|
||||
},
|
||||
[OPERATOR_NOT]: {
|
||||
[NORMAL_FILTER]: 'not[author_username]',
|
||||
[NORMAL_FILTER]: 'not[author_username][]',
|
||||
},
|
||||
[OPERATOR_OR]: {
|
||||
[ALTERNATIVE_FILTER]: 'or[author_username]',
|
||||
[ALTERNATIVE_FILTER]: 'or[author_username][]',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Wikis from '~/pages/shared/wikis/wikis';
|
||||
import { mountApplications } from '~/pages/shared/wikis/async_edit';
|
||||
import { mountApplications } from '~/pages/shared/wikis/edit';
|
||||
|
||||
mountApplications();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,53 @@ import { GlAlert } from '@gitlab/ui';
|
|||
import { s__ } from '~/locale';
|
||||
import WikiHeader from './components/wiki_header.vue';
|
||||
import WikiContent from './components/wiki_content.vue';
|
||||
import WikiEditForm from './components/wiki_form.vue';
|
||||
import WikiAlert from './components/wiki_alert.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlAlert,
|
||||
WikiHeader,
|
||||
WikiContent,
|
||||
WikiEditForm,
|
||||
WikiAlert,
|
||||
},
|
||||
inject: ['contentApi', 'isPageHistorical', 'wikiUrl', 'historyUrl'],
|
||||
inject: ['isEditingPath', 'isPageHistorical', 'wikiUrl', 'historyUrl', 'error'],
|
||||
i18n: {
|
||||
alertText: s__('WikiHistoricalPage|This is an old version of this page.'),
|
||||
alertPrimaryButton: s__('WikiHistoricalPage|Go to most recent version'),
|
||||
alertSecondaryButton: s__('WikiHistoricalPage|Browse history'),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEditing: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
isEditing() {
|
||||
const url = new URL(window.location);
|
||||
|
||||
if (this.isEditing) {
|
||||
url.searchParams.set('edit', 'true');
|
||||
} else {
|
||||
url.searchParams.delete('edit');
|
||||
}
|
||||
|
||||
window.history.pushState({}, '', url);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const url = new URL(window.location);
|
||||
|
||||
if (url.searchParams.has('edit')) {
|
||||
this.setEditingMode(true);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setEditingMode(value) {
|
||||
this.isEditing = value;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -33,7 +67,9 @@ export default {
|
|||
>
|
||||
{{ $options.i18n.alertText }}
|
||||
</gl-alert>
|
||||
<wiki-header />
|
||||
<wiki-content v-if="contentApi" :get-wiki-content-url="contentApi" />
|
||||
<wiki-alert v-if="error" :error="error" :wiki-page-path="wikiUrl" class="gl-mt-5" />
|
||||
<wiki-header v-if="!isEditing" @is-editing="setEditingMode" />
|
||||
<wiki-edit-form v-if="isEditingPath || isEditing" @is-editing="setEditingMode" />
|
||||
<wiki-content v-else :is-editing="isEditing" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
export const mountApplications = async () => {
|
||||
const el = document.querySelector('.js-wiki-edit-page');
|
||||
|
||||
if (el) {
|
||||
const { mountApplications: mountEditApplications } = await import(
|
||||
/* webpackChunkName: 'wiki_edit' */ './edit'
|
||||
);
|
||||
|
||||
mountEditApplications();
|
||||
}
|
||||
};
|
||||
|
|
@ -14,7 +14,7 @@ export default {
|
|||
directives: {
|
||||
'gl-modal': GlModalDirective,
|
||||
},
|
||||
inject: ['wikiUrl', 'pageTitle', 'csrfToken', 'pagePersisted'],
|
||||
inject: ['wikiUrl', 'pageHeading', 'csrfToken', 'pagePersisted'],
|
||||
props: {
|
||||
showAsDropdownItem: {
|
||||
type: Boolean,
|
||||
|
|
@ -30,7 +30,7 @@ export default {
|
|||
? this.$options.i18n.deleteTemplateTitle
|
||||
: this.$options.i18n.deletePageTitle,
|
||||
{
|
||||
pageTitle: escape(this.pageTitle),
|
||||
pageTitle: escape(this.pageHeading),
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<script>
|
||||
import { GlSkeletonLoader, GlAlert } from '@gitlab/ui';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { createAlert } from '~/alert';
|
||||
import { __ } from '~/locale';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { handleLocationHash } from '~/lib/utils/common_utils';
|
||||
import { renderGFM } from '~/behaviors/markdown/render_gfm';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -15,17 +15,13 @@ export default {
|
|||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
props: {
|
||||
getWikiContentUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
inject: ['contentApi'],
|
||||
data() {
|
||||
return {
|
||||
content: '',
|
||||
isLoadingContent: false,
|
||||
loadingContentFailed: false,
|
||||
content: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -39,7 +35,7 @@ export default {
|
|||
try {
|
||||
const {
|
||||
data: { content },
|
||||
} = await axios.get(this.getWikiContentUrl, { params: { render_html: true } });
|
||||
} = await axios.get(this.contentApi, { params: { render_html: true } });
|
||||
this.content = content;
|
||||
|
||||
this.$nextTick()
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export default {
|
|||
DeleteWikiModal,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
inject: ['formatOptions', 'pageInfo', 'drawioUrl', 'templates'],
|
||||
inject: ['isEditingPath', 'formatOptions', 'pageInfo', 'drawioUrl', 'templates', 'pageHeading'],
|
||||
data() {
|
||||
const title = window.location.href.includes('random_title=true') ? '' : getTitle(this.pageInfo);
|
||||
return {
|
||||
|
|
@ -207,6 +207,13 @@ export default {
|
|||
drawioEnabled() {
|
||||
return typeof this.drawioUrl === 'string' && this.drawioUrl.length > 0;
|
||||
},
|
||||
cancelFormHref() {
|
||||
if (this.isEditingPath) {
|
||||
return this.cancelFormPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
title() {
|
||||
|
|
@ -312,6 +319,13 @@ export default {
|
|||
setTemplate(template) {
|
||||
this.$refs.markdownEditor.setTemplate(template);
|
||||
},
|
||||
cancelFormAction() {
|
||||
this.isFormDirty = false;
|
||||
|
||||
if (!this.isEditingPath) {
|
||||
this.$emit('is-editing', false);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -321,7 +335,8 @@ export default {
|
|||
ref="form"
|
||||
:action="formAction"
|
||||
method="post"
|
||||
class="wiki-form common-note-form gl-mt-3 js-quick-submit"
|
||||
class="wiki-form common-note-form js-quick-submit gl-mt-4"
|
||||
:class="{ 'gl-mt-5': !isEditingPath }"
|
||||
@submit="handleFormSubmit"
|
||||
@input="isFormDirty = true"
|
||||
>
|
||||
|
|
@ -336,7 +351,11 @@ export default {
|
|||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<gl-form-group :label="$options.i18n.title.label" label-for="wiki_title">
|
||||
<gl-form-group
|
||||
:label="$options.i18n.title.label"
|
||||
label-for="wiki_title"
|
||||
label-class="gl-sr-only"
|
||||
>
|
||||
<template v-if="!isTemplate" #description>
|
||||
<gl-icon name="bulb" />
|
||||
{{ titleHelpText }}
|
||||
|
|
@ -456,8 +475,8 @@ export default {
|
|||
>
|
||||
<gl-button
|
||||
data-testid="wiki-cancel-button"
|
||||
:href="cancelFormPath"
|
||||
@click="isFormDirty = false"
|
||||
:href="cancelFormHref"
|
||||
@click="cancelFormAction"
|
||||
>
|
||||
{{ $options.i18n.cancel }}</gl-button
|
||||
>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { GlButton, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import PageHeading from '~/vue_shared/components/page_heading.vue';
|
||||
import WikiMoreDropdown from './wiki_more_dropdown.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -11,18 +12,20 @@ export default {
|
|||
GlSprintf,
|
||||
WikiMoreDropdown,
|
||||
TimeAgo,
|
||||
PageHeading,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
inject: [
|
||||
'pageTitle',
|
||||
'pageHeading',
|
||||
'showEditButton',
|
||||
'isPageTemplate',
|
||||
'editButtonUrl',
|
||||
'lastVersion',
|
||||
'pageVersion',
|
||||
'authorUrl',
|
||||
'isEditingPath',
|
||||
],
|
||||
computed: {
|
||||
editTooltipText() {
|
||||
|
|
@ -43,11 +46,14 @@ export default {
|
|||
methods: {
|
||||
onKeyUp(event) {
|
||||
if (event.key === 'e') {
|
||||
window.location.href = this.editButtonUrl;
|
||||
this.$emit('is-editing', true);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
setEditingMode() {
|
||||
this.$emit('is-editing', true);
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
edit: __('Edit'),
|
||||
|
|
@ -60,22 +66,16 @@ export default {
|
|||
|
||||
<template>
|
||||
<div
|
||||
class="wiki-page-header has-sidebar-toggle detail-page-header border-bottom-0 gl-pt-5 gl-flex gl-flex-wrap"
|
||||
class="wiki-page-header has-sidebar-toggle detail-page-header border-bottom-0 !gl-pt-0 gl-flex gl-flex-wrap"
|
||||
>
|
||||
<div class="gl-flex gl-w-full">
|
||||
<h1
|
||||
class="gl-heading-1 !gl-my-0 gl-inline-block gl-grow gl-break-anywhere"
|
||||
data-testid="wiki-page-title"
|
||||
>
|
||||
{{ pageTitle }}
|
||||
</h1>
|
||||
<div class="detail-page-header-actions gl-self-start gl-flex gl-gap-3">
|
||||
<page-heading :heading="pageHeading" class="gl-w-full">
|
||||
<template v-if="!isEditingPath" #actions>
|
||||
<gl-button
|
||||
v-if="showEditButton"
|
||||
v-gl-tooltip.html
|
||||
:title="editTooltip"
|
||||
:href="editButtonUrl"
|
||||
data-testid="wiki-edit-button"
|
||||
@click="setEditingMode"
|
||||
>
|
||||
{{ $options.i18n.edit }}
|
||||
</gl-button>
|
||||
|
|
@ -85,8 +85,8 @@ export default {
|
|||
class="js-sidebar-wiki-toggle md:gl-hidden"
|
||||
/>
|
||||
<wiki-more-dropdown />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</page-heading>
|
||||
<div
|
||||
v-if="lastVersion"
|
||||
class="wiki-last-version gl-leading-20 gl-text-secondary gl-mt-3 gl-mb-5"
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export default {
|
|||
v-gl-tooltip="showDropdownTooltip"
|
||||
icon="ellipsis_v"
|
||||
category="tertiary"
|
||||
placement="right"
|
||||
placement="bottom-end"
|
||||
no-caret
|
||||
data-testid="wiki-more-dropdown"
|
||||
@shown="showDropdown"
|
||||
|
|
|
|||
|
|
@ -1,70 +1,60 @@
|
|||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createApolloClient from '~/lib/graphql';
|
||||
import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import GLForm from '~/gl_form';
|
||||
import ZenMode from '~/zen_mode';
|
||||
import WikiContentApp from './app.vue';
|
||||
|
||||
import wikiAlert from './components/wiki_alert.vue';
|
||||
import wikiForm from './components/wiki_form.vue';
|
||||
const mountWikiEditApp = () => {
|
||||
const el = document.querySelector('#js-vue-wiki-edit-app');
|
||||
|
||||
const createAlertVueApp = () => {
|
||||
const el = document.getElementById('js-wiki-error');
|
||||
if (el) {
|
||||
const { error, wikiPagePath } = el.dataset;
|
||||
if (!el) return false;
|
||||
const {
|
||||
pageHeading,
|
||||
pageInfo,
|
||||
isPageTemplate,
|
||||
wikiUrl,
|
||||
pagePersisted,
|
||||
templates,
|
||||
formatOptions,
|
||||
error,
|
||||
} = el.dataset;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(wikiAlert, {
|
||||
props: {
|
||||
error,
|
||||
wikiPagePath,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
Vue.use(VueApollo);
|
||||
const apolloProvider = new VueApollo({ defaultClient: createApolloClient() });
|
||||
|
||||
const createWikiFormApp = () => {
|
||||
const el = document.getElementById('js-wiki-form');
|
||||
|
||||
if (el) {
|
||||
const { pageInfo, formatOptions, templates, wikiUrl, pageTitle, pagePersisted } = el.dataset;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const apolloProvider = new VueApollo({ defaultClient: createApolloClient() });
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
formatOptions: JSON.parse(formatOptions),
|
||||
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
|
||||
drawioUrl: gon.diagramsnet_url,
|
||||
templates: JSON.parse(templates),
|
||||
pageTitle,
|
||||
wikiUrl,
|
||||
csrfToken: csrf.token,
|
||||
pagePersisted: parseBoolean(pagePersisted),
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(wikiForm);
|
||||
},
|
||||
});
|
||||
}
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
isEditingPath: true,
|
||||
pageHeading,
|
||||
contentApi: null,
|
||||
showEditButton: null,
|
||||
editButtonUrl: null,
|
||||
lastVersion: null,
|
||||
pageVersion: null,
|
||||
authorUrl: null,
|
||||
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
|
||||
isPageTemplate: parseBoolean(isPageTemplate),
|
||||
isPageHistorical: false,
|
||||
formatOptions: JSON.parse(formatOptions),
|
||||
csrfToken: csrf.token,
|
||||
templates: JSON.parse(templates),
|
||||
drawioUrl: gon.diagramsnet_url,
|
||||
historyUrl: '',
|
||||
wikiUrl,
|
||||
pagePersisted: parseBoolean(pagePersisted),
|
||||
error,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(WikiContentApp);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const mountApplications = () => {
|
||||
new ZenMode(); // eslint-disable-line no-new
|
||||
new GLForm($('.wiki-form')); // eslint-disable-line no-new
|
||||
|
||||
createAlertVueApp();
|
||||
createWikiFormApp();
|
||||
mountWikiEditApp();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createApolloClient from '~/lib/graphql';
|
||||
import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import Wikis from './wikis';
|
||||
import WikiContentApp from './app.vue';
|
||||
|
|
@ -9,9 +11,10 @@ const mountWikiContentApp = () => {
|
|||
|
||||
if (!el) return false;
|
||||
const {
|
||||
pageTitle,
|
||||
pageHeading,
|
||||
contentApi,
|
||||
showEditButton,
|
||||
pageInfo,
|
||||
isPageTemplate,
|
||||
isPageHistorical,
|
||||
editButtonUrl,
|
||||
|
|
@ -27,14 +30,22 @@ const mountWikiContentApp = () => {
|
|||
cloneLinkClass,
|
||||
wikiUrl,
|
||||
pagePersisted,
|
||||
templates,
|
||||
formatOptions,
|
||||
} = el.dataset;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
const apolloProvider = new VueApollo({ defaultClient: createApolloClient() });
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
pageTitle,
|
||||
isEditingPath: false,
|
||||
pageHeading,
|
||||
contentApi,
|
||||
showEditButton: parseBoolean(showEditButton),
|
||||
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
|
||||
isPageTemplate: parseBoolean(isPageTemplate),
|
||||
isPageHistorical: parseBoolean(isPageHistorical),
|
||||
editButtonUrl,
|
||||
|
|
@ -46,11 +57,15 @@ const mountWikiContentApp = () => {
|
|||
cloneHttpUrl,
|
||||
newUrl,
|
||||
historyUrl,
|
||||
csrfToken: csrf.token,
|
||||
templatesUrl,
|
||||
cloneLinkClass,
|
||||
wikiUrl,
|
||||
formatOptions: JSON.parse(formatOptions),
|
||||
csrfToken: csrf.token,
|
||||
templates: JSON.parse(templates),
|
||||
drawioUrl: gon.diagramsnet_url,
|
||||
pagePersisted: parseBoolean(pagePersisted),
|
||||
error: null,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(WikiContentApp);
|
||||
|
|
@ -60,6 +75,5 @@ const mountWikiContentApp = () => {
|
|||
|
||||
export const mountApplications = () => {
|
||||
mountWikiContentApp();
|
||||
// eslint-disable-next-line no-new
|
||||
new Wikis();
|
||||
new Wikis(); // eslint-disable-line no-new
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export default {
|
|||
</h1>
|
||||
<div
|
||||
v-if="$scopedSlots.actions"
|
||||
class="gl-flex gl-items-center gl-gap-5"
|
||||
class="gl-flex gl-items-center gl-gap-3"
|
||||
data-testid="page-heading-actions"
|
||||
>
|
||||
<slot name="actions"></slot>
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@ export default {
|
|||
updateDraft(this.autosaveKey, this.descriptionText);
|
||||
},
|
||||
handleDescriptionTextUpdated(newText) {
|
||||
this.disableTruncation = true;
|
||||
this.descriptionText = newText;
|
||||
this.$emit('updateDraft', this.descriptionText);
|
||||
this.updateWorkItem();
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ module WikiActions
|
|||
# Assign vars expected by MarkupHelper
|
||||
@ref = params[:version_id]
|
||||
@path = page.path
|
||||
@templates = templates_list
|
||||
|
||||
render 'shared/wikis/show'
|
||||
elsif file_blob
|
||||
|
|
@ -398,7 +399,7 @@ module WikiActions
|
|||
end
|
||||
|
||||
def load_content?
|
||||
skip_actions = Feature.enabled?(:wiki_front_matter_title, container) ? %w[history destroy diff] : %w[history destroy diff show]
|
||||
skip_actions = %w[history destroy diff]
|
||||
|
||||
return false if skip_actions.include?(params[:action])
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ module PersonalAccessTokens
|
|||
|
||||
# We _only_ want to update last_used_at and not also updated_at (which
|
||||
# would be updated when using #touch).
|
||||
without_sticky_writes { @personal_access_token.update_column(:last_used_at, Time.zone.now) } if update?
|
||||
return unless update?
|
||||
|
||||
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
|
||||
@personal_access_token.update_column(:last_used_at, Time.zone.now)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -26,15 +30,5 @@ module PersonalAccessTokens
|
|||
|
||||
last_used <= 10.minutes.ago
|
||||
end
|
||||
|
||||
def without_sticky_writes
|
||||
if Feature.enabled?(:disable_sticky_writes_for_pat_last_used, @personal_access_token.user)
|
||||
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
|
||||
yield
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- add_to_breadcrumbs _("Labels"), admin_labels_path
|
||||
- breadcrumb_title _("Edit Label")
|
||||
- breadcrumb_title _("Edit")
|
||||
- page_title _("Edit"), @label.name, _("Labels")
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _('Edit Label')
|
||||
%hr
|
||||
= _('Edit label')
|
||||
|
||||
= render 'shared/labels/form', url: admin_label_path(@label), back_path: admin_labels_path
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
- show_lock_on_merge = @group.supports_lock_on_merge?
|
||||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _('Edit Label')
|
||||
= _('Edit label')
|
||||
|
||||
= render 'shared/labels/form', url: group_label_path(@group, @label), back_path: @previous_labels_path, show_lock_on_merge: show_lock_on_merge
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
- render "header_title"
|
||||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _('Edit Milestone')
|
||||
%hr
|
||||
= _('Edit milestone')
|
||||
|
||||
= render "form"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
= render_if_exists 'groups/settings/wiki', f: f, group: @group
|
||||
= render 'groups/settings/lfs', f: f
|
||||
= render_if_exists 'groups/settings/auto_assign_duo_pro', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/code_suggestions', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/security/security_policy_management', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/duo_features_enabled', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/experimental_settings', f: f, group: @group
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
- show_lock_on_merge = @project.supports_lock_on_merge?
|
||||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _('Edit Label')
|
||||
= _('Edit label')
|
||||
|
||||
= render 'shared/labels/form', url: project_label_path(@project, @label), back_path: project_labels_path(@project), show_lock_on_merge: show_lock_on_merge
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
- page_title _('Edit'), @milestone.title, _('Milestones')
|
||||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _('Edit Milestone')
|
||||
|
||||
%hr
|
||||
= _('Edit milestone')
|
||||
|
||||
= render 'form'
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
.gl-mt-3
|
||||
= form_errors(@page, truncate: :title)
|
||||
|
||||
- templates = @templates.map { |t| wiki_page_basic_info(t) }
|
||||
|
||||
#js-wiki-form{ data: { page_info: wiki_page_info(@page, uploads_path: uploads_path).to_json,
|
||||
templates: templates.to_json,
|
||||
format_options: wiki_markup_hash_by_name_id.to_json,
|
||||
wiki_url: wiki_page_path(@wiki, @page),
|
||||
page_title: @page.human_title,
|
||||
page_persisted: (@page.persisted? && can?(current_user, :create_wiki, @wiki.container)).to_s,
|
||||
} }
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
- if can?(current_user, :create_wiki, @wiki)
|
||||
- edit_sidebar_url = wiki_page_path(@wiki, Wiki::SIDEBAR, action: :edit)
|
||||
- sidebar_link_class = (editing && @page&.slug == Wiki::SIDEBAR) ? 'active' : ''
|
||||
= render Pajamas::ButtonComponent.new(href: edit_sidebar_url, category: :tertiary, size: :small, icon: 'pencil', button_options: { class: "gl-border-l gl-pl-3 has-tooltip #{sidebar_link_class}", title: edit_sidebar_text, aria: { label: edit_sidebar_text }})
|
||||
= render Pajamas::ButtonComponent.new(href: edit_sidebar_url, category: :tertiary, size: :small, icon: 'settings', button_options: { class: "gl-border-l gl-pl-3 has-tooltip #{sidebar_link_class}", title: edit_sidebar_text, aria: { label: edit_sidebar_text }})
|
||||
|
||||
- if @sidebar_wiki_entries
|
||||
%ul.wiki-pages{ class: (@sidebar_page ? 'gl-border-b !gl-pb-3' : '' ) }
|
||||
|
|
|
|||
|
|
@ -3,19 +3,25 @@
|
|||
- add_page_specific_style 'page_bundles/wiki'
|
||||
- @gfm_form = true
|
||||
- @noteable_type = 'Wiki'
|
||||
- templates = @templates.map { |t| wiki_page_basic_info(t) }
|
||||
|
||||
- if @error
|
||||
#js-wiki-error{ data: { error: @error, wiki_page_path: wiki_page_path(@wiki, @page) } }
|
||||
- if @page.persisted?
|
||||
- page_title = @page.template? ? s_("Wiki|Edit template") : s_("Wiki|Edit page")
|
||||
- else
|
||||
- page_title = @page.template? ? s_("Wiki|New template") : s_("Wiki|New page")
|
||||
|
||||
.js-wiki-edit-page.wiki-page-header.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
= form_errors(@page, truncate: :title)
|
||||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
- if @page.persisted?
|
||||
= @page.template? ? s_("Wiki|Edit template") : s_("Wiki|Edit page")
|
||||
- else
|
||||
= @page.template? ? s_("Wiki|New template") : s_("Wiki|New page")
|
||||
|
||||
= render 'shared/wikis/form', uploads_path: wiki_attachment_upload_url
|
||||
#js-vue-wiki-edit-app{ data: {
|
||||
testid: 'wiki-page-edit-app',
|
||||
error: @error,
|
||||
page_heading: page_title,
|
||||
page_info: wiki_page_info(@page, uploads_path: wiki_attachment_upload_url).to_json,
|
||||
is_page_template: @page.template?.to_s,
|
||||
page_persisted: (@page.persisted? && can?(current_user, :create_wiki, @wiki.container)).to_s,
|
||||
wiki_url: wiki_page_path(@wiki, @page),
|
||||
format_options: wiki_markup_hash_by_name_id.to_json,
|
||||
templates: templates.to_json,
|
||||
} }
|
||||
|
||||
= render 'shared/wikis/sidebar', editing: true
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
.wiki-page-header.top-area.gl-flex-direction-column.gl-lg-flex-direction-row
|
||||
.gl-mt-5.gl-mb-3
|
||||
.gl-display-flex.gl-justify-content-space-between
|
||||
%h2.gl-mt-0.gl-mb-5{ data: { testid: 'wiki-page-title' } }= @page ? @page.human_title : _('Failed to retrieve page')
|
||||
%h2.gl-mt-0.gl-mb-5{ data: { testid: 'page-heading' } }= @page ? @page.human_title : _('Failed to retrieve page')
|
||||
.js-wiki-page-content.md.gl-pt-2{ data: { testid: 'wiki-page-content' } }
|
||||
= _('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 }
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
- wiki_page_title @page
|
||||
- add_page_specific_style 'page_bundles/wiki'
|
||||
- page_history = @page&.persisted? ? wiki_page_path(@wiki, @page, action: :history) : ''
|
||||
- @gfm_form = true
|
||||
- @noteable_type = 'Wiki'
|
||||
- templates = @templates.map { |t| wiki_page_basic_info(t) }
|
||||
|
||||
= form_errors(@page, truncate: :title)
|
||||
|
||||
#js-vue-wiki-content-app{ data: {
|
||||
testid: 'wiki-page-content-app',
|
||||
page_title: @page.human_title,
|
||||
page_heading: @page.human_title,
|
||||
content_api: wiki_page_render_api_endpoint(@page),
|
||||
show_edit_button: (can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding).to_s,
|
||||
page_info: wiki_page_info(@page, uploads_path: wiki_attachment_upload_url).to_json,
|
||||
is_page_template: @page.template?.to_s,
|
||||
is_page_historical: @page.historical?.to_s,
|
||||
last_version: @page.last_version,
|
||||
|
|
@ -22,6 +28,8 @@
|
|||
author_url: wiki_page_version_author_url(@page.version),
|
||||
history_url: page_history,
|
||||
templates_url: wiki_page_path(@wiki, Wiki::TEMPLATES_DIR),
|
||||
format_options: wiki_markup_hash_by_name_id.to_json,
|
||||
templates: templates.to_json,
|
||||
} }
|
||||
|
||||
= render 'shared/wikis/sidebar'
|
||||
|
|
|
|||
|
|
@ -5,5 +5,4 @@
|
|||
|
||||
%h1.page-title.gl-font-size-h-display
|
||||
= _("Edit snippet")
|
||||
%hr
|
||||
= render 'shared/snippets/form', url: gitlab_snippet_path(@snippet)
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: disable_sticky_writes_for_pat_last_used
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/462379
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153707
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/462823
|
||||
milestone: '17.1'
|
||||
group: group::package registry
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
- major: 8
|
||||
minor: 11
|
||||
|
||||
- major: 8
|
||||
minor: 12
|
||||
|
||||
- major: 8
|
||||
minor: 17
|
||||
|
||||
- major: 9
|
||||
minor: 5
|
||||
|
||||
- major: 10
|
||||
minor: 8
|
||||
|
||||
- major: 11
|
||||
minor: 11
|
||||
|
||||
- major: 12
|
||||
minor: 0
|
||||
|
||||
- major: 12
|
||||
minor: 1
|
||||
|
||||
- major: 12
|
||||
minor: 10
|
||||
|
||||
- major: 13
|
||||
minor: 0
|
||||
|
||||
- major: 13
|
||||
minor: 1
|
||||
|
||||
- major: 13
|
||||
minor: 8
|
||||
|
||||
- major: 13
|
||||
minor: 12
|
||||
|
||||
- major: 14
|
||||
minor: 0
|
||||
comments: "**Migrations can take a long time!**"
|
||||
|
||||
- major: 14
|
||||
minor: 3
|
||||
comments: "See [GitLab Issue #354211](https://gitlab.com/gitlab-org/gitlab/-/issues/354211#note_962223568)"
|
||||
|
||||
- major: 14
|
||||
minor: 9
|
||||
comments: "See [GitLab Issue #354211](https://gitlab.com/gitlab-org/gitlab/-/issues/354211#note_962223568)"
|
||||
|
||||
- major: 14
|
||||
minor: 10
|
||||
|
||||
- major: 15
|
||||
minor: 0
|
||||
|
||||
- major: 15
|
||||
minor: 4
|
||||
|
||||
- major: 15
|
||||
minor: 11
|
||||
|
||||
- major: 16
|
||||
minor: 3
|
||||
|
||||
- major: 16
|
||||
minor: 7
|
||||
|
||||
- major: 16
|
||||
minor: 11
|
||||
|
|
@ -156,3 +156,5 @@ but it does increase the overall size of your project's repository.
|
|||
- If the VM image does not include the specific software version you need for your job, the required software must be fetched and installed. This causes an increase in job execution time.
|
||||
- It is not possible to bring your own OS image.
|
||||
- The keychain for user `gitlab` is not publicly available. You must create a keychain instead.
|
||||
- Hosted runners on macOS run in headless mode.
|
||||
Any workloads that require UI interactions such as `testmanagerd` are not supported.
|
||||
|
|
|
|||
|
|
@ -157,12 +157,10 @@ Slack channel.
|
|||
Before releasing a known required stop, complete these steps. If the required stop
|
||||
is identified after release, the following steps must still be completed:
|
||||
|
||||
1. Update [upgrade paths](../update/index.md#upgrade-paths) documentation to include the new
|
||||
required stop.
|
||||
1. In the same MR, update the [upgrade paths](../update/index.md#upgrade-paths) documentation to include the new
|
||||
required stop, and the [`upgrade_path.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/upgrade_path.yml).
|
||||
The `upgrade_path.yml` is the single source of truth (SSoT) for all our required stops.
|
||||
1. Communicate the changes with the customer Support and Release management teams.
|
||||
1. Update the [`upgrade-path.yml`](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path/-/blob/main/upgrade-path.yml).
|
||||
GitLab Release Tools uses this file to update Omnibus GitLab with the required stop, as well as to feed the
|
||||
the Upgrade Path tool.
|
||||
1. If the required stops is database related, file an issue with the Database group to
|
||||
squash migrations to that version in the next release. Use this template for your issue:
|
||||
|
||||
|
|
@ -187,6 +185,18 @@ is identified after release, the following steps must still be completed:
|
|||
[upgrade check hook](https://gitlab.com/gitlab-org/charts/gitlab/-/blame/master/templates/_runcheck.tpl#L32)
|
||||
to the required stop version.
|
||||
|
||||
## GitLab-maintained projects which depend on `upgrade_path.yml`
|
||||
|
||||
We have multiple projects depending on the `upgrade_path.yml` SSoT. Therefore,
|
||||
any change to the structure of this file needs to take into consideration that
|
||||
it might affect one of the following projects:
|
||||
|
||||
- [Release Tools](https://gitlab.com/gitlab-org/release-tools)
|
||||
- [Support Upgrade Path](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path)
|
||||
- [Upgrade Tester](https://gitlab.com/gitlab-org/quality/upgrade-tester)
|
||||
- [GitLab QA](https://gitlab.com/gitlab-org/gitlab-qa)
|
||||
- [PostgreSQL Dump Generator](https://gitlab.com/gitlab-org/quality/pg-dump-generator)
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Documentation: Database required stops](database/required_stops.md)
|
||||
|
|
@ -198,4 +208,5 @@ is identified after release, the following steps must still be completed:
|
|||
- [Issue: Put in place measures to avoid addition/proliferation of GitLab upgrade path stops](https://gitlab.com/gitlab-org/gitlab/-/issues/375553)
|
||||
- [Issue: Brainstorm ways for background migrations to be finalized without introducing a required upgrade step](https://gitlab.com/gitlab-org/gitlab/-/issues/357561)
|
||||
- [Issue: Scheduled required paths for GitLab upgrades to improve UX](https://gitlab.com/gitlab-org/gitlab/-/issues/358417)
|
||||
- [Issue: Automate upgrade stop planning process](https://gitlab.com/gitlab-org/gitlab/-/issues/438921)
|
||||
- [Epic: GitLab Releases and Maintenance policies](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/988)
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ WARNING:
|
|||
[very specific cases](https://handbook.gitlab.com/handbook/engineering/workflow/#criteria-for-merging-during-broken-master).
|
||||
For other cases, follow these [handbook instructions](https://handbook.gitlab.com/handbook/engineering/workflow/#merging-during-broken-master).
|
||||
- If the latest pipeline was created before the merge request was approved, start a new pipeline to ensure that full RSpec suite has been run. You may skip this step only if the merge request does not contain any backend change.
|
||||
- If the **latest [merged results pipeline](../ci/pipelines/merged_results_pipelines.md)** was **created less than 4 hours ago**, you
|
||||
- If the **latest [merged results pipeline](../ci/pipelines/merged_results_pipelines.md)** was **created less than 8 hours ago**, you
|
||||
may merge without starting a new pipeline as the merge request is close enough to the target branch.
|
||||
- When you set the MR to auto-merge, you should take over
|
||||
subsequent revisions for anything that would be spotted after that.
|
||||
|
|
|
|||
|
|
@ -232,19 +232,39 @@ graph LR
|
|||
A --"artifact: list of test files"--> B & C
|
||||
```
|
||||
|
||||
## Merge Trains
|
||||
## Merge trains
|
||||
|
||||
### Why do we need to have a "stable" master branch to enable merge trains?
|
||||
### Current usage
|
||||
|
||||
If the master branch is unstable (i.e. CI/CD pipelines for the master branch are failing frequently), all of the merge requests pipelines that were added AFTER a faulty merge request pipeline would have to be **cancelled** and **added back to the train**, which would create a lot of delays if the merge train is long.
|
||||
[We started using merge trains in June 2024](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154540).
|
||||
|
||||
### How stable does the master branch have to be for us to enable merge trains?
|
||||
At the moment, **Merge train pipelines don't run any tests**: they only enforce the
|
||||
["Merging a merge request" guidelines](../code_review.md#merging-a-merge-request)
|
||||
that already existed before the enablement of merge trains, but that we couldn't easily enforce.
|
||||
|
||||
We don't have a specific number, but we need to have better numbers for flaky tests failures and infrastructure failures (see the [Master Broken Incidents RCA Dashboard](https://app.periscopedata.com/app/gitlab/1082465/Master-Broken-Incidents-Root-Cause-Analysis)).
|
||||
Merge train pipelines run a single `pre-merge-checks` job which ensures the latest pipeline before merge is:
|
||||
|
||||
### Could we gradually move to merge trains in our CI/CD configuration?
|
||||
1. A [Merged Results pipeline](../../ci/pipelines/merged_results_pipelines.md)
|
||||
1. A [`tier-3` pipeline](#pipeline-tiers) (i.e. full pipeline, not predictive one)
|
||||
1. Created at most 8 hours ago
|
||||
|
||||
There was a proposal from a contributor, but the approach is not without some downsides: [see the original proposal and discussion](https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/195#note_1117151994).
|
||||
We opened [a feedback issue](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/513)
|
||||
to iterate on this solution.
|
||||
|
||||
### Next iterations
|
||||
|
||||
We opened [a dedicated issue to discuss the next iteration for merge trains](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/516)
|
||||
to actually start running tests in merge train pipelines.
|
||||
|
||||
### Challenges for enabling merge trains running "full" test pipelines
|
||||
|
||||
#### Why do we need to have a "stable" default branch?
|
||||
|
||||
If the default branch is unstable (i.e. CI/CD pipelines for the default branch are failing frequently), all of the merge requests pipelines that were added AFTER a faulty merge request pipeline would have to be **cancelled** and **added back to the train**, which would create a lot of delays if the merge train is long.
|
||||
|
||||
#### How stable does the default branch have to be?
|
||||
|
||||
We don't have a specific number, but we need to have better numbers for flaky tests failures and infrastructure failures (see the [Master Broken Incidents RCA Dashboard](https://10az.online.tableau.com/#/site/gitlab/workbooks/2296993/views)).
|
||||
|
||||
## Faster feedback for some merge requests
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ This is a partial list of the [RSpec metadata](https://rspec.info/features/3-12/
|
|||
| `:skip_fips_env` | The test is excluded when run against an environment in FIPS mode. |
|
||||
| `:skip_signup_disabled` | The test uses UI to sign up a new user and is skipped in any environment that does not allow new user registration via the UI. |
|
||||
| `:smoke` | The test belongs to the test suite which verifies basic functionality of a GitLab instance. |
|
||||
| `:health_check` | The test belongs to the smallest test suite, a subset of smoke. Used to monitor the status and health of the application |
|
||||
| `:smtp` | The test requires a GitLab instance to be configured to use an SMTP server. Tests SMTP notification email delivery from GitLab by using MailHog. |
|
||||
| `:testcase` | The link to the test case issue in the [GitLab Project test cases](https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases). |
|
||||
| `:transient` | The test tests transient bugs. It is excluded by default. |
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ Our suite consists of this basic functionality coverage:
|
|||
|
||||
Smoke tests have the `:smoke` RSpec metadata.
|
||||
|
||||
## Health Check suite
|
||||
|
||||
This is a very small subset smoke tests with the `:health_check` RSpec metadata.
|
||||
Its function is to monitor the status and health of the application.
|
||||
|
||||
See [End-to-end Testing](end_to_end/index.md) for more details about
|
||||
end-to-end tests.
|
||||
|
||||
|
|
|
|||
|
|
@ -220,14 +220,14 @@ The following GitLab application features are not available:
|
|||
- View the [list of AI features to see which ones are supported](../../user/ai_features.md).
|
||||
- Refer to our [direction page](https://about.gitlab.com/direction/saas-platforms/dedicated/#supporting-ai-features-on-gitlab-dedicated) for more information.
|
||||
- Features other than [available features](#available-features) that must be configured outside of the GitLab user interface
|
||||
- Interacting with GitLab [Feature Flags](../../administration/feature_flags.md)
|
||||
- Any functionality or feature behind a Feature Flag that is toggled `off` by default
|
||||
- Any functionality or feature behind a Feature Flag that is toggled `off` by default.
|
||||
|
||||
The following features will not be supported:
|
||||
|
||||
- Mattermost
|
||||
- [Server-side Git hooks](../../administration/server_hooks.md).
|
||||
GitLab Dedicated is a SaaS service, and access to the underlying infrastructure is only available to GitLab Inc. team members. Due to the nature of server side configuration, there is a possible security concern of running arbitrary code on Dedicated services, as well as the possible impact that can have on the service SLA. Use the alternative [push rules](../../user/project/repository/push_rules.md) or [webhooks](../../user/project/integrations/webhooks.md) instead.
|
||||
- Interacting with GitLab [Feature Flags](../../administration/feature_flags.md). [Feature flags support the development and rollout of new or experimental features](https://handbook.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#when-to-use-feature-flags) on GitLab.com. Features behind feature flags are not considered ready for production use, are experimental and therefore unsafe for GitLab Dedicated. Stability and SLAs may be affected by changing default settings.
|
||||
|
||||
### GitLab Dedicated service features
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,13 @@ environments so that they do not attempt and fail to reach out to GitLab service
|
|||
|
||||
For more information, see [Enable or disable service ping](../../administration/settings/usage_statistics.md#enable-or-disable-service-ping).
|
||||
|
||||
### Disable runner version management
|
||||
|
||||
Runner version management retrieves the latest runner versions from GitLab to
|
||||
[determine which runners in your environment are out of date](../../ci/runners/runners_scope.md#determine-which-runners-need-to-be-upgraded).
|
||||
You must [disable runner version management](../../administration/settings/continuous_integration.md#disable-runner-version-management)
|
||||
for offline environments.
|
||||
|
||||
### Configure NTP
|
||||
|
||||
In GitLab 15.4 and 15.5, Gitaly Cluster assumes `pool.ntp.org` is accessible. If `pool.ntp.org` is not accessible, [customize the time server setting](../../administration/gitaly/praefect.md#customize-time-server-setting) on the Gitaly
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ use the file finder.
|
|||
For more information, see:
|
||||
|
||||
- [Code intelligence](../project/code_intelligence.md)
|
||||
- [File finder](../project/repository/file_finder.md)
|
||||
- [Files](../project/repository/files/index.md)
|
||||
|
||||
## Step 6: Migrate projects into GitLab
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ GitLab does not limit the number of private projects you can create.
|
|||
- [Search](../../user/search/index.md)
|
||||
- [Badges](../../user/project/badges.md)
|
||||
- [Code intelligence](../../user/project/code_intelligence.md)
|
||||
- [File finder](../../user/project/repository/file_finder.md)
|
||||
- [Files](../../user/project/repository/files/index.md)
|
||||
- [Migrate projects by using file exports](../../user/project/settings/import_export.md)
|
||||
- [System notes](../../user/project/system_notes.md)
|
||||
- [Transfer a project to another namespace](../../user/project/import/index.md)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,21 @@ plugin support. Refer to the JetBrains documentation for specifics on your IDE.
|
|||
|
||||
For languages not listed in the table, Code Suggestions might not function as expected.
|
||||
|
||||
## View Multiple Code Suggestions
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/1325) in GitLab 17.1.
|
||||
|
||||
For a code completion suggestion in VS Code, multiple suggestion options
|
||||
might be available. To view all available suggestions:
|
||||
|
||||
1. Hover over the code completion suggestion.
|
||||
1. Scroll through the alternatives. Either:
|
||||
- Use keyboard shortcuts. Press <kbd>Option</kbd> + <kbd>`]`</kbd> to view the
|
||||
next suggestion, and <kbd>Option</kbd> + <kbd>`[`</kbd> to view the previous
|
||||
suggestions.
|
||||
- Select the right or left arrow to see next or previous options.
|
||||
1. Press <kbd>Tab</kbd> to apply the suggestion you prefer.
|
||||
|
||||
## Experimental features
|
||||
|
||||
### Add support for more languages for Code Suggestions in VS Code
|
||||
|
|
|
|||
|
|
@ -1,34 +1,11 @@
|
|||
---
|
||||
stage: Create
|
||||
group: IDE
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: "Search for files in your GitLab repository directly from the GitLab user interface."
|
||||
redirect_to: 'files/index.md'
|
||||
remove_date: '2024-09-07'
|
||||
---
|
||||
|
||||
# File finder
|
||||
This document was moved to [another location](files/index.md).
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148025) to a dialog in GitLab 16.11.
|
||||
|
||||
With file finder, you can search for a file in a repository directly from the GitLab UI.
|
||||
|
||||
File finder is powered by the [`fuzzaldrin-plus`](https://github.com/jeancroy/fuzz-aldrin-plus) library, which uses fuzzy search and highlights results as you type.
|
||||
|
||||
## Search for a file
|
||||
|
||||
To search for a file, press <kbd>t</kbd> anywhere in your project, or:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Code > Repository**.
|
||||
1. In the upper right, select **Find file**.
|
||||
1. On the dialog, start entering the filename.
|
||||
1. From the dropdown list, select the file.
|
||||
|
||||
To go back to **Files**, press <kbd>Esc</kbd>.
|
||||
|
||||
To narrow down your results, include `/` in your search.
|
||||
|
||||

|
||||
<!-- This redirect file can be deleted after <2024-09-07>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
stage: Create
|
||||
group: IDE
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: "Search for files in your GitLab repository directly from the GitLab user interface."
|
||||
---
|
||||
|
||||
# File finder
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148025) to a dialog in GitLab 16.11.
|
||||
|
||||
With file finder, you can search for a file in a repository directly from the GitLab UI.
|
||||
|
||||
File finder is powered by the [`fuzzaldrin-plus`](https://github.com/jeancroy/fuzz-aldrin-plus) library, which uses fuzzy search and highlights results as you type.
|
||||
|
||||
## Search for a file
|
||||
|
||||
To search for a file, press <kbd>t</kbd> anywhere in your project, or:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Code > Repository**.
|
||||
1. In the upper right, select **Find file**.
|
||||
1. On the dialog, start entering the filename.
|
||||
1. From the dropdown list, select the file.
|
||||
|
||||
To go back to **Files**, press <kbd>Esc</kbd>.
|
||||
|
||||
To narrow down your results, include `/` in your search.
|
||||
|
||||

|
||||
|
|
@ -302,7 +302,7 @@ because they can't follow redirects:
|
|||
- [GitLab Workflow VS Code extension](../../../editor_extensions/visual_studio_code/index.md)
|
||||
- [Lock files and prevent change conflicts](../file_lock.md)
|
||||
- [Repository API](../../../api/repositories.md)
|
||||
- [Find files](file_finder.md)
|
||||
- [Files](files/index.md)
|
||||
- [Branches](branches/index.md)
|
||||
- [Create a directory](web_editor.md#create-a-directory)
|
||||
- [Find file history](git_history.md)
|
||||
|
|
|
|||
|
|
@ -34,15 +34,7 @@ module API
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def find_groups(params, parent_id = nil)
|
||||
find_params = params.slice(
|
||||
:all_available,
|
||||
:custom_attributes,
|
||||
:owned, :min_access_level,
|
||||
:include_parent_descendants,
|
||||
:repository_storage,
|
||||
:marked_for_deletion_on,
|
||||
:search, :visibility
|
||||
)
|
||||
find_params = params.slice(*allowable_find_params)
|
||||
|
||||
find_params[:parent] = if params[:top_level_only]
|
||||
[nil]
|
||||
|
|
@ -60,6 +52,13 @@ module API
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def allowable_find_params
|
||||
[:all_available,
|
||||
:custom_attributes,
|
||||
:owned, :min_access_level,
|
||||
:include_parent_descendants, :search, :visibility]
|
||||
end
|
||||
|
||||
# This is a separate method so that EE can extend its behaviour, without
|
||||
# having to modify this code directly.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -12661,21 +12661,6 @@ msgstr ""
|
|||
msgid "CodeOwner|Pattern"
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestionsSM|Code Suggestions free access has ended"
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestionsSM|Contact Sales"
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestionsSM|Manage user access for Code Suggestions on the %{subscription_details_link_start}subscription details page%{subscription_details_link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestionsSM|Manage user access for Code Suggestions on the %{usage_quotas_link_start}usage quotas%{usage_quotas_link_end} page."
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestionsSM|Purchase the Duo Pro add-on to use Code Suggestions."
|
||||
msgstr ""
|
||||
|
||||
msgid "CodeSuggestions|%{linkStart}Code Suggestions%{linkEnd} uses generative AI to suggest code while you're developing."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17543,6 +17528,9 @@ msgstr ""
|
|||
msgid "Dependencies|There was an error fetching the components for this group. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|There was an error fetching the package managers for this group. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|There was an error fetching the projects for this group. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19448,12 +19436,6 @@ msgstr ""
|
|||
msgid "Edit Identity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit Label"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit Milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit Password"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19535,12 +19517,18 @@ msgstr ""
|
|||
msgid "Edit inline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit label"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit link"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit page"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -37314,6 +37302,9 @@ msgstr ""
|
|||
msgid "PackageRegistry|published by %{author}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Packager"
|
||||
msgstr ""
|
||||
|
||||
msgid "Packages"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -53197,6 +53188,9 @@ msgstr ""
|
|||
msgid "There was a problem fetching the latest pipeline status."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was a problem fetching the pipeline mini graph."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was a problem fetching the pipeline stage."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ module QA
|
|||
end
|
||||
|
||||
def has_title?(title)
|
||||
has_element?('wiki-page-title', title)
|
||||
has_element?('page-heading', title)
|
||||
end
|
||||
|
||||
def has_content?(content)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Govern', :smoke, :mobile, product_group: :authentication do
|
||||
RSpec.describe 'Govern', :smoke, :health_check, :mobile, product_group: :authentication do
|
||||
describe 'basic user login' do
|
||||
it 'user logs in using basic credentials and logs out',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347880' do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Govern' do
|
||||
describe 'Project access tokens', :smoke, product_group: :authentication do
|
||||
describe 'Project access tokens', :smoke, :health_check, product_group: :authentication do
|
||||
let(:project_access_token) { QA::Resource::ProjectAccessToken.fabricate_via_browser_ui! }
|
||||
|
||||
after do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Plan', :smoke, product_group: :project_management do
|
||||
RSpec.describe 'Plan', :smoke, :health_check, product_group: :project_management do
|
||||
let!(:user) do
|
||||
create(:user,
|
||||
name: "QA User <img src=x onerror=alert(2)<img src=x onerror=alert(1)>",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Plan', :smoke, product_group: :project_management do
|
||||
RSpec.describe 'Plan', :smoke, :health_check, product_group: :project_management do
|
||||
describe 'Issue creation' do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api_unless_fips! do |project|
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module QA
|
|||
|
||||
it(
|
||||
'creates a basic merge request',
|
||||
:smoke, :skip_fips_env,
|
||||
:smoke, :skip_fips_env, :health_check,
|
||||
quarantine: {
|
||||
only: { job: 'update-ee-to-ce' },
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/412361',
|
||||
|
|
@ -35,7 +35,7 @@ module QA
|
|||
end
|
||||
|
||||
it(
|
||||
'creates a merge request with a milestone and label', :smoke,
|
||||
'creates a merge request with a milestone and label', :smoke, :health_check,
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347762'
|
||||
) do
|
||||
gitlab_account_user_name = Resource::User.default.reload!.name
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it 'pushes code to the repository via SSH', :smoke, :skip_fips_env,
|
||||
it 'pushes code to the repository via SSH', :smoke, :health_check, :skip_fips_env,
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347825' do
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = project
|
||||
|
|
@ -36,7 +36,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'pushes multiple branches and tags together', :smoke, :skip_fips_env,
|
||||
it 'pushes multiple branches and tags together', :smoke, :health_check, :skip_fips_env,
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347826' do
|
||||
branches = []
|
||||
tags = []
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ COMMIT_REF_NAME_DESTINATION="${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}"
|
|||
if skopeo inspect "docker://${COMMIT_ASSETS_HASH_DESTINATION}" > /dev/null; then
|
||||
echosuccess "Image ${COMMIT_ASSETS_HASH_DESTINATION} already exists, no need to rebuild it."
|
||||
|
||||
echo "Copying assets image for destinations: ${COMMIT_REF_SLUG_DESTINATION} and ${COMMIT_SHA_DESTINATION}"
|
||||
|
||||
skopeo copy "docker://${COMMIT_ASSETS_HASH_DESTINATION}" "docker://${COMMIT_REF_SLUG_DESTINATION}"
|
||||
skopeo copy "docker://${COMMIT_ASSETS_HASH_DESTINATION}" "docker://${COMMIT_SHA_DESTINATION}"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'UpgradePath', feature_category: :shared do
|
||||
it 'is parsed correctly' do
|
||||
upgrade_path = YAML.safe_load_file(Rails.root.join('config/upgrade_path.yml'))
|
||||
|
||||
expect(upgrade_path.first).to eq({ "major" => 8, "minor" => 11 })
|
||||
expect(upgrade_path[13]).to eq({ "major" => 14, "minor" => 0,
|
||||
"comments" => "**Migrations can take a long time!**" })
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
|
||||
include FilteredSearchHelpers
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:milestone) { create(:milestone, title: 'v1.0', project: project) }
|
||||
|
|
@ -12,10 +13,10 @@ RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
|
|||
|
||||
before do
|
||||
sign_in(user)
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
||||
def request_csv(params = {})
|
||||
visit project_issues_path(project, params)
|
||||
def request_csv
|
||||
click_button 'Actions'
|
||||
click_button 'Export as CSV'
|
||||
click_on 'Export issues'
|
||||
|
|
@ -63,7 +64,8 @@ RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
it 'uses filters from issue index', :sidekiq_inline do
|
||||
request_csv(state: :closed)
|
||||
click_link 'Closed'
|
||||
request_csv
|
||||
|
||||
expect(csv.count).to eq 0
|
||||
end
|
||||
|
|
@ -71,7 +73,8 @@ RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
|
|||
it 'ignores sorting from issue index', :sidekiq_inline do
|
||||
issue2 = create(:labeled_issue, project: project, author: user, labels: [feature_label])
|
||||
|
||||
request_csv(sort: :label_priority)
|
||||
change_sort_by("Label priority")
|
||||
request_csv
|
||||
|
||||
expected = [issue.iid.to_s, issue2.iid.to_s]
|
||||
expect(csv.map { |row| row['Issue ID'] }).to eq expected
|
||||
|
|
@ -80,8 +83,35 @@ RSpec.describe 'Issues csv', :js, feature_category: :team_planning do
|
|||
it 'uses array filters, such as label_name', :sidekiq_inline do
|
||||
issue.update!(labels: [idea_label])
|
||||
|
||||
request_csv("label_name[]" => 'Bug')
|
||||
select_tokens 'Label', '||', feature_label.title, idea_label.title, submit: true
|
||||
request_csv
|
||||
|
||||
expect(csv.count).to eq 0
|
||||
expect(csv.count).to eq 1
|
||||
end
|
||||
|
||||
context "with multiple issue authors" do
|
||||
let(:user2) { create(:user, developer_of: project) }
|
||||
let!(:issue2) { create(:issue, project: project, author: user2) }
|
||||
|
||||
it 'exports issues by selected author', :sidekiq_inline do
|
||||
select_tokens 'Author', '=', user2.username, submit: true
|
||||
request_csv
|
||||
|
||||
expect(csv.count).to eq 1
|
||||
end
|
||||
|
||||
it 'exports issues by selected multiple authors', :sidekiq_inline do
|
||||
select_tokens 'Author', '||', user2.username, user.username, submit: true
|
||||
request_csv
|
||||
|
||||
expect(csv.count).to eq 2
|
||||
end
|
||||
|
||||
it 'does not export issues by excluded multiple authors', :sidekiq_inline do
|
||||
select_tokens 'Author', '!=', user.username, user2.username, submit: true
|
||||
request_csv
|
||||
|
||||
expect(csv.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ RSpec.describe 'Work item linked items', :js, feature_category: :team_planning d
|
|||
verify_linked_item_added("##{task.iid}")
|
||||
end
|
||||
|
||||
it 'links a new item with work item reference', :aggregate_failures do
|
||||
it 'links a new item with work item reference', :aggregate_failures,
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/445635' do
|
||||
verify_linked_item_added(task.to_reference(full: true))
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/h
|
|||
import LegacyPipelineStage from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph/legacy_pipeline_stage.vue';
|
||||
import eventHub from '~/ci/event_hub';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { stageReply } from '../mock_data';
|
||||
import { legacyStageReply } from '../mock_data';
|
||||
|
||||
const dropdownPath = 'path.json';
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ describe('Pipelines stage component', () => {
|
|||
beforeEach(async () => {
|
||||
createComponent({ updateDropdown: true });
|
||||
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
|
||||
await openStageDropdown();
|
||||
});
|
||||
|
|
@ -112,7 +112,7 @@ describe('Pipelines stage component', () => {
|
|||
|
||||
describe('when user opens dropdown and stage request is successful', () => {
|
||||
beforeEach(async () => {
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
createComponent();
|
||||
|
||||
await openStageDropdown();
|
||||
|
|
@ -121,8 +121,8 @@ describe('Pipelines stage component', () => {
|
|||
});
|
||||
|
||||
it('renders the received data and emits the correct events', () => {
|
||||
expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name);
|
||||
expect(findDropdownMenuTitle().text()).toContain(stageReply.name);
|
||||
expect(findDropdownMenu().text()).toContain(legacyStageReply.latest_statuses[0].name);
|
||||
expect(findDropdownMenuTitle().text()).toContain(legacyStageReply.name);
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
|
||||
expect(wrapper.emitted('miniGraphStageClick')).toEqual([[]]);
|
||||
});
|
||||
|
|
@ -152,7 +152,7 @@ describe('Pipelines stage component', () => {
|
|||
|
||||
describe('update endpoint correctly', () => {
|
||||
beforeEach(async () => {
|
||||
const copyStage = { ...stageReply };
|
||||
const copyStage = { ...legacyStageReply };
|
||||
copyStage.latest_statuses[0].name = 'this is the updated content';
|
||||
mock.onGet('bar.json').reply(HTTP_STATUS_OK, copyStage);
|
||||
createComponent({
|
||||
|
|
@ -179,8 +179,10 @@ describe('Pipelines stage component', () => {
|
|||
|
||||
describe('job update in dropdown', () => {
|
||||
beforeEach(async () => {
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
|
||||
mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(HTTP_STATUS_OK);
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
mock
|
||||
.onPost(`${legacyStageReply.latest_statuses[0].status.action.path}.json`)
|
||||
.reply(HTTP_STATUS_OK);
|
||||
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
|
|
@ -205,7 +207,7 @@ describe('Pipelines stage component', () => {
|
|||
|
||||
describe('With merge trains enabled', () => {
|
||||
it('shows a warning on the dropdown', async () => {
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
createComponent({
|
||||
isMergeTrain: true,
|
||||
});
|
||||
|
|
@ -222,7 +224,7 @@ describe('Pipelines stage component', () => {
|
|||
|
||||
describe('With merge trains disabled', () => {
|
||||
beforeEach(async () => {
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
|
||||
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
createComponent();
|
||||
|
||||
await openStageDropdown();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const mockDownstreamPipelinesGraphql = ({ includeSourceJobRetried = true } = {}) => ({
|
||||
export const mockDownstreamPipelinesGraphql = () => ({
|
||||
nodes: [
|
||||
{
|
||||
id: 'gid://gitlab/Ci::Pipeline/612',
|
||||
|
|
@ -15,10 +15,6 @@ export const mockDownstreamPipelinesGraphql = ({ includeSourceJobRetried = true
|
|||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
sourceJob: {
|
||||
id: 'gid://gitlab/Ci::Bridge/532',
|
||||
retried: includeSourceJobRetried ? false : null,
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
{
|
||||
|
|
@ -36,10 +32,6 @@ export const mockDownstreamPipelinesGraphql = ({ includeSourceJobRetried = true
|
|||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
sourceJob: {
|
||||
id: 'gid://gitlab/Ci::Bridge/531',
|
||||
retried: includeSourceJobRetried ? true : null,
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
{
|
||||
|
|
@ -57,16 +49,24 @@ export const mockDownstreamPipelinesGraphql = ({ includeSourceJobRetried = true
|
|||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
sourceJob: {
|
||||
id: 'gid://gitlab/Ci::Bridge/530',
|
||||
retried: includeSourceJobRetried ? true : null,
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
],
|
||||
__typename: 'PipelineConnection',
|
||||
});
|
||||
|
||||
export const pipelineStage = {
|
||||
__typename: 'CiStage',
|
||||
id: 'gid://gitlab/Ci::Stage/409',
|
||||
name: 'build',
|
||||
detailedStatus: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: 'success-409-409',
|
||||
icon: 'status_success',
|
||||
group: 'success',
|
||||
},
|
||||
};
|
||||
|
||||
const upstream = {
|
||||
id: 'gid://gitlab/Ci::Pipeline/610',
|
||||
path: '/root/trigger-downstream/-/pipelines/610',
|
||||
|
|
@ -85,26 +85,16 @@ const upstream = {
|
|||
__typename: 'Pipeline',
|
||||
};
|
||||
|
||||
export const mockPipelineStagesQueryResponse = {
|
||||
export const mockPipelineMiniGraphQueryResponse = {
|
||||
data: {
|
||||
project: {
|
||||
id: 'gid://gitlab/Project/20',
|
||||
pipeline: {
|
||||
id: 'gid://gitlab/Ci::Pipeline/320',
|
||||
downstream: mockDownstreamPipelinesGraphql(),
|
||||
upstream,
|
||||
stages: {
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiStage',
|
||||
id: 'gid://gitlab/Ci::Stage/409',
|
||||
name: 'build',
|
||||
detailedStatus: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: 'success-409-409',
|
||||
icon: 'status_success',
|
||||
group: 'success',
|
||||
},
|
||||
},
|
||||
],
|
||||
nodes: [pipelineStage],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -146,10 +136,9 @@ export const mockUpstreamDownstreamQueryResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const linkedPipelinesFetchError = 'There was a problem fetching linked pipelines.';
|
||||
export const stagesFetchError = 'There was a problem fetching the pipeline stages.';
|
||||
export const pipelineMiniGraphFetchError = 'There was a problem fetching the pipeline mini graph.';
|
||||
|
||||
export const stageReply = {
|
||||
export const legacyStageReply = {
|
||||
name: 'deploy',
|
||||
title: 'deploy: running',
|
||||
latest_statuses: [
|
||||
|
|
|
|||
|
|
@ -7,38 +7,25 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
||||
import getLinkedPipelinesQuery from '~/ci/pipeline_details/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import getPipelineStagesQuery from '~/ci/pipeline_mini_graph/graphql/queries/get_pipeline_stages.query.graphql';
|
||||
import getPipelineMiniGraphQuery from '~/ci/pipeline_mini_graph/graphql/queries/get_pipeline_mini_graph.query.graphql';
|
||||
import PipelineMiniGraph from '~/ci/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import * as sharedGraphQlUtils from '~/graphql_shared/utils';
|
||||
|
||||
import {
|
||||
linkedPipelinesFetchError,
|
||||
stagesFetchError,
|
||||
mockPipelineStagesQueryResponse,
|
||||
mockUpstreamDownstreamQueryResponse,
|
||||
} from './mock_data';
|
||||
import { pipelineMiniGraphFetchError, mockPipelineMiniGraphQueryResponse } from './mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
jest.mock('~/alert');
|
||||
|
||||
describe('PipelineMiniGraph', () => {
|
||||
let wrapper;
|
||||
let linkedPipelinesResponse;
|
||||
let pipelineStagesResponse;
|
||||
let pipelineMiniGraphResponse;
|
||||
|
||||
const fullPath = 'gitlab-org/gitlab';
|
||||
const iid = '315';
|
||||
const pipelineEtag = '/api/graphql:pipelines/id/315';
|
||||
|
||||
const createComponent = ({
|
||||
pipelineStagesHandler = pipelineStagesResponse,
|
||||
linkedPipelinesHandler = linkedPipelinesResponse,
|
||||
} = {}) => {
|
||||
const handlers = [
|
||||
[getLinkedPipelinesQuery, linkedPipelinesHandler],
|
||||
[getPipelineStagesQuery, pipelineStagesHandler],
|
||||
];
|
||||
const createComponent = ({ pipelineMiniGraphHandler = pipelineMiniGraphResponse } = {}) => {
|
||||
const handlers = [[getPipelineMiniGraphQuery, pipelineMiniGraphHandler]];
|
||||
const mockApollo = createMockApollo(handlers);
|
||||
|
||||
wrapper = shallowMountExtended(PipelineMiniGraph, {
|
||||
|
|
@ -57,11 +44,10 @@ describe('PipelineMiniGraph', () => {
|
|||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
|
||||
beforeEach(() => {
|
||||
linkedPipelinesResponse = jest.fn().mockResolvedValue(mockUpstreamDownstreamQueryResponse);
|
||||
pipelineStagesResponse = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
|
||||
pipelineMiniGraphResponse = jest.fn().mockResolvedValue(mockPipelineMiniGraphQueryResponse);
|
||||
});
|
||||
|
||||
describe('when initial queries are loading', () => {
|
||||
describe('when initial query is loading', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
|
@ -72,7 +58,7 @@ describe('PipelineMiniGraph', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when queries have loaded', () => {
|
||||
describe('when query has loaded', () => {
|
||||
it('does not show a loading icon', async () => {
|
||||
await createComponent();
|
||||
|
||||
|
|
@ -85,11 +71,10 @@ describe('PipelineMiniGraph', () => {
|
|||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('fires the queries', async () => {
|
||||
it('fires the query', async () => {
|
||||
await createComponent();
|
||||
|
||||
expect(linkedPipelinesResponse).toHaveBeenCalledWith({ iid, fullPath });
|
||||
expect(pipelineStagesResponse).toHaveBeenCalledWith({ iid, fullPath });
|
||||
expect(pipelineMiniGraphResponse).toHaveBeenCalledWith({ iid, fullPath });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -101,22 +86,19 @@ describe('PipelineMiniGraph', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledTimes(2);
|
||||
expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when pipeline queries are unsuccessful', () => {
|
||||
describe('when the pipeline query is unsuccessful', () => {
|
||||
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
|
||||
it.each`
|
||||
query | handlerName | errorMessage
|
||||
${'pipeline stages'} | ${'pipelineStagesHandler'} | ${stagesFetchError}
|
||||
${'linked pipelines'} | ${'linkedPipelinesHandler'} | ${linkedPipelinesFetchError}
|
||||
`('throws an error for the $query query', async ({ errorMessage, handlerName }) => {
|
||||
await createComponent({ [handlerName]: failedHandler });
|
||||
|
||||
it('throws an error for the pipeline query', async () => {
|
||||
await createComponent({ pipelineMiniGraphHandler: failedHandler });
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: errorMessage });
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: pipelineMiniGraphFetchError });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
|
|||
|
||||
import getPipelineStageQuery from '~/ci/pipeline_mini_graph/graphql/queries/get_pipeline_stage.query.graphql';
|
||||
import PipelineStage from '~/ci/pipeline_mini_graph/pipeline_stage.vue';
|
||||
import { pipelineStage } from './mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ describe('PipelineStage', () => {
|
|||
|
||||
const defaultProps = {
|
||||
pipelineEtag: '/etag',
|
||||
stageId: '1',
|
||||
stage: pipelineStage,
|
||||
};
|
||||
|
||||
const createComponent = ({ pipelineStageHandler = pipelineStageResponse } = {}) => {
|
||||
|
|
@ -39,7 +40,7 @@ describe('PipelineStage', () => {
|
|||
createComponent();
|
||||
});
|
||||
|
||||
it('renders job item', () => {
|
||||
it('renders the pipeline stage', () => {
|
||||
expect(findPipelineStage().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import {
|
|||
setIdTypePreferenceMutationResponse,
|
||||
setIdTypePreferenceMutationResponseWithErrors,
|
||||
} from 'jest/issues/list/mock_data';
|
||||
import { stageReply } from 'jest/ci/pipeline_mini_graph/mock_data';
|
||||
import { legacyStageReply } from 'jest/ci/pipeline_mini_graph/mock_data';
|
||||
import { users, mockSearch, branches } from '../pipeline_details/mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
|
@ -769,7 +769,7 @@ describe('Pipelines', () => {
|
|||
|
||||
mock
|
||||
.onGet(mockPipelineWithStages.details.stages[0].dropdown_path)
|
||||
.reply(HTTP_STATUS_OK, stageReply);
|
||||
.reply(HTTP_STATUS_OK, legacyStageReply);
|
||||
|
||||
// cancelMock is getting overwritten in pipelines_service.js#L29
|
||||
// so we have to spy on it again here
|
||||
|
|
|
|||
|
|
@ -175,9 +175,9 @@ export const setIdTypePreferenceMutationResponseWithErrors = {
|
|||
export const locationSearch = [
|
||||
'?search=find+issues',
|
||||
'author_username=homer',
|
||||
'not[author_username]=marge',
|
||||
'or[author_username]=burns',
|
||||
'or[author_username]=smithers',
|
||||
'not[author_username][]=marge',
|
||||
'or[author_username][]=burns',
|
||||
'or[author_username][]=smithers',
|
||||
'assignee_username[]=bart',
|
||||
'assignee_username[]=lisa',
|
||||
'assignee_username[]=5',
|
||||
|
|
@ -376,8 +376,8 @@ export const apiParamsWithSpecialValues = {
|
|||
export const urlParams = {
|
||||
search: 'find issues',
|
||||
author_username: 'homer',
|
||||
'not[author_username]': 'marge',
|
||||
'or[author_username]': ['burns', 'smithers'],
|
||||
'not[author_username][]': 'marge',
|
||||
'or[author_username][]': ['burns', 'smithers'],
|
||||
'assignee_username[]': ['bart', 'lisa', '5'],
|
||||
'not[assignee_username][]': ['patty', 'selma'],
|
||||
'or[assignee_username][]': ['carl', 'lenny'],
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ describe('DeleteWikiModal', () => {
|
|||
},
|
||||
provide: {
|
||||
wikiUrl: 'delete-wiki-url',
|
||||
pageTitle: 'Page title',
|
||||
pageHeading: 'Page title',
|
||||
csrfToken: 'csrf-token',
|
||||
pagePersisted: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ describe('pages/shared/wikis/components/wiki_content', () => {
|
|||
|
||||
function buildWrapper(propsData = {}) {
|
||||
wrapper = shallowMount(WikiContent, {
|
||||
propsData: { getWikiContentUrl: PATH, ...propsData },
|
||||
provide: {
|
||||
contentApi: PATH,
|
||||
},
|
||||
propsData: { ...propsData },
|
||||
stubs: {
|
||||
GlSkeletonLoader,
|
||||
GlAlert,
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ describe('WikiForm', () => {
|
|||
wrapper = extendedWrapper(
|
||||
mountFn(WikiForm, {
|
||||
provide: {
|
||||
isEditingPath: true,
|
||||
templates,
|
||||
formatOptions,
|
||||
glFeatures,
|
||||
|
|
@ -87,7 +88,7 @@ describe('WikiForm', () => {
|
|||
...pageInfo,
|
||||
},
|
||||
wikiUrl: '',
|
||||
pageTitle: '',
|
||||
pageHeading: '',
|
||||
csrfToken: '',
|
||||
pagePersisted: false,
|
||||
...provide,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import PageHeading from '~/vue_shared/components/page_heading.vue';
|
||||
import WikiHeader from '~/pages/shared/wikis/components/wiki_header.vue';
|
||||
|
||||
describe('pages/shared/wikis/components/wiki_header', () => {
|
||||
|
|
@ -9,8 +10,9 @@ describe('pages/shared/wikis/components/wiki_header', () => {
|
|||
function buildWrapper(provide = {}) {
|
||||
wrapper = shallowMountExtended(WikiHeader, {
|
||||
provide: {
|
||||
pageTitle: 'Wiki page heading',
|
||||
pageHeading: 'Wiki page heading',
|
||||
isPageTemplate: false,
|
||||
isEditingPath: false,
|
||||
showEditButton: true,
|
||||
editButtonUrl: 'http://edit.url',
|
||||
lastVersion: '2024-06-03T01:53:28.000Z',
|
||||
|
|
@ -26,11 +28,12 @@ describe('pages/shared/wikis/components/wiki_header', () => {
|
|||
stubs: {
|
||||
GlSprintf,
|
||||
TimeAgo,
|
||||
PageHeading,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const findPageHeading = () => wrapper.findByTestId('wiki-page-title');
|
||||
const findPageHeading = () => wrapper.findByTestId('page-heading');
|
||||
const findEditButton = () => wrapper.findByTestId('wiki-edit-button');
|
||||
const findLastVersion = () => wrapper.findByTestId('wiki-page-last-version');
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ describe('pages/shared/wikis/components/wiki_more_dropdown', () => {
|
|||
provide: {
|
||||
newUrl: 'https://new.url/path',
|
||||
historyUrl: 'https://history.url/path',
|
||||
pageTitle: 'Wiki title',
|
||||
pageHeading: 'Wiki title',
|
||||
csrfToken: '',
|
||||
wikiUrl: 'https://delete.url/path',
|
||||
wikiPath: '',
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ describe('WorkItemDescription', () => {
|
|||
|
||||
const updatedDescription = `- [x] todo 1\n- [x] todo 2`;
|
||||
expect(wrapper.emitted('descriptionUpdated')).toEqual([[updatedDescription]]);
|
||||
expect(wrapper.find('[data-test-id="description-read-more"]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('disables checkbox while updating', async () => {
|
||||
|
|
@ -120,6 +121,7 @@ describe('WorkItemDescription', () => {
|
|||
|
||||
const updatedDescription = `- [ ] todo 1\n- [ ] todo 2`;
|
||||
expect(wrapper.emitted('descriptionUpdated')).toEqual([[updatedDescription]]);
|
||||
expect(wrapper.find('[data-test-id="description-read-more"]').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,21 +30,6 @@ RSpec.describe PersonalAccessTokens::LastUsedService, feature_category: :system_
|
|||
expect(::Gitlab::Database::LoadBalancing::Session.current).to be_performed_write
|
||||
expect(::Gitlab::Database::LoadBalancing::Session.current).not_to be_using_primary
|
||||
end
|
||||
|
||||
context 'with disable_sticky_writes_for_pat_last_used disabled' do
|
||||
before do
|
||||
stub_feature_flags(disable_sticky_writes_for_pat_last_used: false)
|
||||
end
|
||||
|
||||
it 'does stick to primary' do
|
||||
::Gitlab::Database::LoadBalancing::Session.clear_session
|
||||
|
||||
expect(::Gitlab::Database::LoadBalancing::Session.current).not_to be_performed_write
|
||||
expect { service.execute }.to change { personal_access_token.last_used_at }
|
||||
expect(::Gitlab::Database::LoadBalancing::Session.current).to be_performed_write
|
||||
expect(::Gitlab::Database::LoadBalancing::Session.current).to be_using_primary
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,14 @@ module FilteredSearchHelpers
|
|||
end
|
||||
end
|
||||
|
||||
def change_sort_by(value)
|
||||
within_element '.sort-dropdown-container' do
|
||||
find_by_testid('base-dropdown-toggle').click
|
||||
find('li', text: value).click
|
||||
wait_for_requests
|
||||
end
|
||||
end
|
||||
|
||||
def expect_visible_suggestions_list
|
||||
expect(page).to have_css('.gl-filtered-search-suggestion-list')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ RSpec.shared_examples 'wiki file attachments' do
|
|||
click_button 'Cancel'
|
||||
end
|
||||
|
||||
expect(page).to have_selector('[data-testid="button-attach-file"]')
|
||||
expect(page).not_to have_button('Cancel')
|
||||
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -43,12 +43,11 @@ RSpec.shared_examples 'User updates wiki page' do
|
|||
|
||||
first(:link, text: 'three').click
|
||||
|
||||
expect(find('[data-testid="wiki-page-title"]')).to have_content('three')
|
||||
expect(find('[data-testid="page-heading"]')).to have_content('three')
|
||||
|
||||
click_on('Edit')
|
||||
|
||||
expect(page).to have_current_path(%r{one/two/three-test}, ignore_query: true)
|
||||
expect(page).to have_content('Edit page')
|
||||
|
||||
fill_in('Content', with: 'Updated Wiki Content')
|
||||
click_on('Save changes')
|
||||
|
|
@ -65,7 +64,7 @@ RSpec.shared_examples 'User updates wiki page' do
|
|||
before do
|
||||
visit(wiki_path(wiki))
|
||||
|
||||
click_link('Edit')
|
||||
click_on('Edit')
|
||||
end
|
||||
|
||||
it 'updates a page', :js do
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ RSpec.shared_examples 'User uses wiki shortcuts' do
|
|||
it 'visit edit wiki page using "e" keyboard shortcut', :js do
|
||||
find('body').native.send_key('e')
|
||||
|
||||
expect(find('.page-title')).to have_content('Edit page')
|
||||
expect(find('#wiki_title').value).to eq('home')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -61,12 +61,12 @@ RSpec.shared_examples 'User views a wiki page' do
|
|||
|
||||
first(:link, text: 'three').click
|
||||
|
||||
expect(find('[data-testid="wiki-page-title"]')).to have_content('three')
|
||||
expect(find('[data-testid="page-heading"]')).to have_content('three')
|
||||
|
||||
click_on('Edit')
|
||||
|
||||
expect(page).to have_current_path(%r{one/two/three-test})
|
||||
expect(page).to have_content('Edit page')
|
||||
expect(page).to have_css('#wiki_title')
|
||||
|
||||
fill_in('Content', with: 'Updated Wiki Content')
|
||||
click_on('Save changes')
|
||||
|
|
@ -215,7 +215,7 @@ RSpec.shared_examples 'User views a wiki page' do
|
|||
it 'preserves the special characters' do
|
||||
visit(wiki_page_path(wiki, wiki_page))
|
||||
|
||||
expect(page).to have_css('[data-testid="wiki-page-title"]', text: title)
|
||||
expect(page).to have_css('[data-testid="page-heading"]', text: title)
|
||||
expect(page).to have_css('.wiki-pages li', text: title)
|
||||
end
|
||||
end
|
||||
|
|
@ -230,7 +230,7 @@ RSpec.shared_examples 'User views a wiki page' do
|
|||
it 'safely displays the page' do
|
||||
visit(wiki_page_path(wiki, wiki_page))
|
||||
|
||||
expect(page).to have_selector('[data-testid="wiki-page-title"]', text: title)
|
||||
expect(page).to have_selector('[data-testid="page-heading"]', text: title)
|
||||
expect(page).to have_content('foo bar')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue