Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9684836887
commit
181a4e02bb
|
|
@ -110,9 +110,7 @@ e2e:post-run-e2e-message:
|
|||
- install_gitlab_gem
|
||||
script:
|
||||
- scripts/generate-message-to-run-e2e-pipeline.rb
|
||||
needs:
|
||||
- e2e-test-pipeline-generate
|
||||
- build-assets-image
|
||||
needs: []
|
||||
artifacts:
|
||||
expire_in: 1 day
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -1629,12 +1629,6 @@
|
|||
changes: *code-patterns
|
||||
- <<: *if-merge-request-and-specific-devops-stage
|
||||
changes: *code-patterns
|
||||
- <<: *if-force-ci
|
||||
when: manual
|
||||
allow_failure: true
|
||||
# We used to have a rule at the end here that would catch any remaining code MRs and allow the job to be run
|
||||
# manually. That rule is now in ".qa:rules:code-merge-request-manual" so it can be included when needed and we can
|
||||
# still use ".qa:rules:package-and-test-common" in jobs we don't want to be manual.
|
||||
|
||||
# Like .qa:rules:package-and-test-common but not allowed to fail.
|
||||
# It's named `e2e` instead of `package-and-test` because it's used for e2e tests on GDK (and could be used
|
||||
|
|
@ -1705,7 +1699,6 @@
|
|||
rules:
|
||||
- !reference [".qa:rules:package-and-test-common", rules]
|
||||
- !reference [".qa:rules:e2e-schedule-blocking", rules]
|
||||
- !reference [".qa:rules:code-merge-request-manual", rules]
|
||||
|
||||
.qa:rules:package-and-test-ce:
|
||||
rules:
|
||||
|
|
@ -1770,7 +1763,9 @@
|
|||
# These provide a manual way to trigger follow-up:e2e:package-and-test-ee
|
||||
# It is fine if they're overlapping with the automatic ones.
|
||||
# It'll not hurt and it can simplify the rules, decoupling them.
|
||||
# If any changes are made to this rule, `.qa:rules:follow-up-e2e` should also be updated.
|
||||
# If any changes are made to this rule, the following should also be updated:
|
||||
# 1) .qa:rules:follow-up-e2e
|
||||
# 2) .qa:rules:post-run-e2e-message
|
||||
.qa:rules:manual-e2e:
|
||||
rules:
|
||||
- !reference [".qa:rules:package-and-test-never-run", rules]
|
||||
|
|
@ -1779,7 +1774,8 @@
|
|||
- !reference [".qa:rules:code-merge-request-manual", rules]
|
||||
|
||||
# These are based on `.qa:rules:manual-e2e` but with manual jobs changed to automatic.
|
||||
# If any changes are made to this rule, `.qa:rules:manual-e2e` should also be updated.
|
||||
# If any changes are made to this rule, the following should also be updated:
|
||||
# 1) .qa:rules:manual-e2e
|
||||
.qa:rules:follow-up-e2e:
|
||||
rules:
|
||||
- !reference [".qa:rules:package-and-test-never-run", rules]
|
||||
|
|
@ -1824,11 +1820,10 @@
|
|||
when: never
|
||||
- <<: *if-merge-request-and-specific-devops-stage
|
||||
when: never
|
||||
- <<: *if-force-ci
|
||||
when: never
|
||||
# From .qa:rules:package-and-test-schedule
|
||||
- <<: *if-dot-com-gitlab-org-schedule
|
||||
when: never
|
||||
# From .qa:rules:manual-e2e
|
||||
- !reference [".qa:rules:code-merge-request-allowed-to-fail", rules]
|
||||
|
||||
.qa:rules:fulfillment-e2e-quarantine-report:
|
||||
|
|
|
|||
|
|
@ -2187,7 +2187,6 @@ Layout/LineLength:
|
|||
- 'ee/spec/workers/repository_import_worker_spec.rb'
|
||||
- 'ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb'
|
||||
- 'ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb'
|
||||
- 'ee/spec/workers/store_security_reports_worker_spec.rb'
|
||||
- 'ee/spec/workers/sync_seat_link_request_worker_spec.rb'
|
||||
- 'ee/spec/workers/update_all_mirrors_worker_spec.rb'
|
||||
- 'ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -559,7 +559,7 @@ group :test do
|
|||
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
|
||||
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.28.0', require: false, feature_category: :tooling
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.29.0', require: false, feature_category: :tooling
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 8.1', feature_category: :importers
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@
|
|||
{"name":"gitlab-styles","version":"12.0.1","platform":"ruby","checksum":"d8a302b0ab0e1f18e2d11501760f1b85c5e70b5e5ca628828a0786c7984ed133"},
|
||||
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
|
||||
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.28.0","platform":"ruby","checksum":"d0b2c6bc304b7ccf9003fae851eff330c19ed182079310356a93b30e09fe51f3"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.29.0","platform":"ruby","checksum":"611be320785c9352b8ecdcacaefd271a19e3b76c75f5140ab544085a539eaede"},
|
||||
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
|
||||
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
|
||||
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},
|
||||
|
|
|
|||
|
|
@ -741,7 +741,7 @@ GEM
|
|||
omniauth (>= 1.3, < 3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
gitlab_quality-test_tooling (1.28.0)
|
||||
gitlab_quality-test_tooling (1.29.0)
|
||||
activesupport (>= 7.0, < 7.2)
|
||||
amatch (~> 0.4.1)
|
||||
gitlab (~> 4.19)
|
||||
|
|
@ -2022,7 +2022,7 @@ DEPENDENCIES
|
|||
gitlab-utils!
|
||||
gitlab_chronic_duration (~> 0.12)
|
||||
gitlab_omniauth-ldap (~> 2.2.0)
|
||||
gitlab_quality-test_tooling (~> 1.28.0)
|
||||
gitlab_quality-test_tooling (~> 1.29.0)
|
||||
gon (~> 6.4.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.34.0)
|
||||
google-apis-cloudbilling_v1 (~> 0.21.0)
|
||||
|
|
|
|||
|
|
@ -139,11 +139,9 @@ export default {
|
|||
this.$emit('refresh-pipelines-table');
|
||||
},
|
||||
onRetryPipeline(pipeline) {
|
||||
// This emit is only used by the `legacy_pipelines_table_wrapper`.
|
||||
this.$emit('retry-pipeline', pipeline);
|
||||
},
|
||||
onCancelPipeline(pipeline) {
|
||||
// This emit is only used by the `legacy_pipelines_table_wrapper`.
|
||||
this.$emit('cancel-pipeline', pipeline);
|
||||
},
|
||||
trackPipelineMiniGraph() {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default {
|
|||
{
|
||||
attrs: {
|
||||
href: chunk,
|
||||
class: '!gl-text-inherit gl-text-decoration-underline',
|
||||
class: '!gl-text-inherit gl-underline',
|
||||
rel: 'nofollow noopener noreferrer', // eslint-disable-line @gitlab/require-i18n-strings
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
<script>
|
||||
import { GlButton, GlEmptyState, GlLoadingIcon, GlModal, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { createAlert } from '~/alert';
|
||||
import Api from '~/api';
|
||||
import { getQueryHeaders } from '~/ci/pipeline_details/graph/utils';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import eventHub from '~/ci/event_hub';
|
||||
import PipelinesTableComponent from '~/ci/common/pipelines_table.vue';
|
||||
import { s__, __ } from '~/locale';
|
||||
import getMergeRequestPipelines from '~/ci/merge_requests/graphql/queries/get_merge_request_pipelines.query.graphql';
|
||||
import cancelPipelineMutation from '~/ci/pipeline_details/graphql/mutations/cancel_pipeline.mutation.graphql';
|
||||
import retryPipelineMutation from '~/ci/pipeline_details/graphql/mutations/retry_pipeline.mutation.graphql';
|
||||
import { TYPENAME_CI_PIPELINE } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status';
|
||||
import { formatPipelinesGraphQLDataToREST } from '../utils';
|
||||
|
||||
export default {
|
||||
|
|
@ -47,6 +53,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
hasError: false,
|
||||
isInitialLoading: true,
|
||||
isRunningMergeRequestPipeline: false,
|
||||
page: 1,
|
||||
pageInfo: {},
|
||||
pipelines: [],
|
||||
|
|
@ -72,6 +80,7 @@ export default {
|
|||
return formatPipelinesGraphQLDataToREST(data?.project) || [];
|
||||
},
|
||||
result({ data }) {
|
||||
this.isInitialLoading = false;
|
||||
this.pageInfo = data?.project?.mergeRequest?.pipelines?.pageInfo || {};
|
||||
},
|
||||
error() {
|
||||
|
|
@ -84,7 +93,7 @@ export default {
|
|||
return this.pipelines.length > 0;
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries.pipelines.loading;
|
||||
return this.isInitialLoading && this.$apollo.queries.pipelines.loading;
|
||||
},
|
||||
latestPipeline() {
|
||||
return this.pipelines[0];
|
||||
|
|
@ -114,9 +123,6 @@ export default {
|
|||
isLatestPipelineCreatedInTargetProject() {
|
||||
return this.latestPipeline?.project?.full_path === `/${this.targetProjectFullPath}`;
|
||||
},
|
||||
isRunningMergeRequestPipeline() {
|
||||
return false;
|
||||
},
|
||||
shouldShowSecurityWarning() {
|
||||
return (
|
||||
this.canCreatePipelineInTargetProject &&
|
||||
|
|
@ -139,6 +145,28 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
cancelPipeline(pipeline) {
|
||||
this.executePipelineAction(pipeline, cancelPipelineMutation);
|
||||
},
|
||||
retryPipeline(pipeline) {
|
||||
this.executePipelineAction(pipeline, retryPipelineMutation);
|
||||
},
|
||||
async executePipelineAction(pipeline, mutation) {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation,
|
||||
variables: {
|
||||
id: convertToGraphQLId(TYPENAME_CI_PIPELINE, pipeline.id),
|
||||
},
|
||||
});
|
||||
this.refreshPipelineTable();
|
||||
} catch {
|
||||
createAlert({ message: __('An error occurred while performing this action.') });
|
||||
}
|
||||
},
|
||||
refreshPipelineTable() {
|
||||
this.$apollo.queries.pipelines.refetch();
|
||||
},
|
||||
/**
|
||||
* When the user clicks on the "Run pipeline" button
|
||||
* we need to make a post request and
|
||||
|
|
@ -150,11 +178,34 @@ export default {
|
|||
*
|
||||
*/
|
||||
|
||||
onClickRunPipeline() {
|
||||
eventHub.$emit('runMergeRequestPipeline', {
|
||||
projectId: this.projectId,
|
||||
mergeRequestId: this.mergeRequestId,
|
||||
});
|
||||
async onClickRunPipeline() {
|
||||
try {
|
||||
this.isRunningMergeRequestPipeline = true;
|
||||
|
||||
await Api.postMergeRequestPipeline(this.projectId, {
|
||||
mergeRequestId: this.mergeRequestId,
|
||||
});
|
||||
this.$toast.show(s__('Pipeline|Creating pipeline.'));
|
||||
} catch (e) {
|
||||
const unauthorized = e.response.status === HTTP_STATUS_UNAUTHORIZED;
|
||||
let errorMessage = __(
|
||||
'An error occurred while trying to run a new pipeline for this merge request.',
|
||||
);
|
||||
|
||||
if (unauthorized) {
|
||||
errorMessage = __('You do not have permission to run a pipeline on this branch.');
|
||||
}
|
||||
|
||||
createAlert({
|
||||
message: errorMessage,
|
||||
primaryButton: {
|
||||
text: __('Learn more'),
|
||||
link: helpPagePath('ci/pipelines/merge_request_pipelines.md'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.isRunningMergeRequestPipeline = false;
|
||||
},
|
||||
tryRunPipeline() {
|
||||
if (!this.shouldShowSecurityWarning) {
|
||||
|
|
@ -281,6 +332,9 @@ export default {
|
|||
:pipelines="pipelines"
|
||||
:update-graph-dropdown="updateGraphDropdown"
|
||||
:source-project-full-path="sourceProjectFullPath"
|
||||
@cancel-pipeline="cancelPipeline"
|
||||
@retry-pipeline="retryPipeline"
|
||||
@refresh-pipelines-table="refreshPipelineTable"
|
||||
>
|
||||
<template #table-header-actions>
|
||||
<div v-if="canRenderPipelineButton" class="gl-text-right">
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ query getMergeRequestPipelines($mergeRequestIid: String!, $fullPath: ID!) {
|
|||
mergeRequestEventType
|
||||
failureReason
|
||||
yamlErrors
|
||||
retryable
|
||||
cancelable
|
||||
commit {
|
||||
id
|
||||
title
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const formatPipelinesGraphQLDataToREST = (project) => {
|
|||
return project?.mergeRequest?.pipelines?.nodes?.map((pipeline) => {
|
||||
return {
|
||||
...pipeline,
|
||||
id: getIdFromGraphQLId(mergeRequest.id),
|
||||
id: getIdFromGraphQLId(pipeline.id),
|
||||
commit: {
|
||||
...pipeline.commit,
|
||||
commit_path: pipeline.commit.webPath,
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export default {
|
|||
v-if="hasChanges"
|
||||
v-gl-tooltip.html="toggleFileBrowserTooltip"
|
||||
variant="default"
|
||||
icon="file-tree"
|
||||
icon="sidebar"
|
||||
class="gl-mr-3 js-toggle-tree-list btn-icon"
|
||||
data-testid="file-tree-button"
|
||||
:aria-label="toggleFileBrowserTitle"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'PagesDeployment',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
<script>
|
||||
import { GlToggle, GlLoadingIcon, GlAlert } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import getProjectPagesDeployments from '../queries/get_project_pages_deployments.graphql';
|
||||
import PagesDeployment from './deployment.vue';
|
||||
import LoadMoreDeployments from './load_more_deployments.vue';
|
||||
|
||||
export default {
|
||||
name: 'PagesDeployments',
|
||||
components: { LoadMoreDeployments, PagesDeployment, GlToggle, GlLoadingIcon, GlAlert },
|
||||
inject: ['projectFullPath'],
|
||||
i18n: {
|
||||
title: s__('Pages|Deployments'),
|
||||
environmentDeploymentsTitle: s__('Pages|Environment deployments'),
|
||||
noDeploymentsMessage: s__('Pages|No deployments yet'),
|
||||
loadErrorMessage: s__(
|
||||
'Pages|Some Pages deployments could not be loaded. Try reloading the page.',
|
||||
),
|
||||
showInactiveLabel: s__('Pages|Include stopped deployments'),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showInactive: false,
|
||||
requestBatchSize: 2,
|
||||
hasError: false,
|
||||
alerts: {},
|
||||
primaryDeployments: null,
|
||||
environmentDeployments: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
sharedQueryVariables() {
|
||||
return {
|
||||
active: this.showInactive ? undefined : true,
|
||||
fullPath: this.projectFullPath,
|
||||
first: this.requestBatchSize,
|
||||
};
|
||||
},
|
||||
hasMultipleDeployments() {
|
||||
return (
|
||||
this.primaryDeployments?.nodes.length > 1 ||
|
||||
(this.primaryDeployments?.nodes.length && this.environmentDeployments?.nodes.length > 0)
|
||||
);
|
||||
},
|
||||
primaryDeploymentsNotLoaded() {
|
||||
if (!this.primaryDeployments) return undefined;
|
||||
return this.primaryDeployments.count - this.primaryDeployments.nodes.length;
|
||||
},
|
||||
environmentDeploymentsNotLoaded() {
|
||||
if (!this.environmentDeployments) return 0;
|
||||
return this.environmentDeployments.count - this.environmentDeployments.nodes.length;
|
||||
},
|
||||
loadedPrimaryDeploymentsCount() {
|
||||
return this.primaryDeployments?.nodes.length || 0;
|
||||
},
|
||||
loadedEnvironmentDeploymentsCount() {
|
||||
return this.environmentDeployments?.nodes.length || 0;
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
primaryDeployments: {
|
||||
query: getProjectPagesDeployments,
|
||||
variables() {
|
||||
return {
|
||||
...this.sharedQueryVariables,
|
||||
versioned: false,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
return data.project.pagesDeployments;
|
||||
},
|
||||
error() {
|
||||
this.hasError = true;
|
||||
},
|
||||
},
|
||||
environmentDeployments: {
|
||||
query: getProjectPagesDeployments,
|
||||
variables() {
|
||||
return {
|
||||
...this.sharedQueryVariables,
|
||||
versioned: true,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
return data.project.pagesDeployments;
|
||||
},
|
||||
error() {
|
||||
this.hasError = true;
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleShowInactive() {
|
||||
this.showInactive = !this.showInactive;
|
||||
},
|
||||
fetchMorePrimaryDeployments() {
|
||||
this.$apollo.queries.primaryDeployments.fetchMore({
|
||||
variables: {
|
||||
after: this.primaryDeployments.pageInfo.endCursor,
|
||||
},
|
||||
updateQuery: this.fetchMoreUpdateResult,
|
||||
});
|
||||
},
|
||||
fetchMoreEnvironmentDeployments() {
|
||||
this.$apollo.queries.environmentDeployments.fetchMore({
|
||||
variables: {
|
||||
after: this.environmentDeployments.pageInfo.endCursor,
|
||||
},
|
||||
updateQuery: this.fetchMoreUpdateResult,
|
||||
});
|
||||
},
|
||||
fetchMoreUpdateResult(previousResult, { fetchMoreResult }) {
|
||||
return {
|
||||
project: {
|
||||
...previousResult.project,
|
||||
pagesDeployments: {
|
||||
...fetchMoreResult.project.pagesDeployments,
|
||||
nodes: [
|
||||
/*
|
||||
* This weird presence check makes this method resilient against
|
||||
* empty previousResults, which *should* not happen, but who knows?
|
||||
* */
|
||||
...(previousResult?.project?.pagesDeployments
|
||||
? previousResult.project.pagesDeployments.nodes
|
||||
: []),
|
||||
...fetchMoreResult.project.pagesDeployments.nodes,
|
||||
],
|
||||
pageInfo: fetchMoreResult.project.pagesDeployments.pageInfo,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
onChildError({ id, message }) {
|
||||
this.$set(this.alerts, id, message);
|
||||
},
|
||||
dismissAlert(id) {
|
||||
this.$delete(this.alerts, id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-alert
|
||||
v-for="(message, id) in alerts"
|
||||
:key="id"
|
||||
variant="danger"
|
||||
class="gl-mb-4"
|
||||
sticky
|
||||
data-testid="alert"
|
||||
@dismiss="dismissAlert(id)"
|
||||
>
|
||||
{{ message }}
|
||||
</gl-alert>
|
||||
<div class="gl-flex gl-flex-col md:gl-flex-row gl-mb-4 md:gl-mb-0 gl-justify-between">
|
||||
<h2 class="gl-text-h2">
|
||||
{{ $options.i18n.title }}
|
||||
</h2>
|
||||
<span class="gl-text-secondary">
|
||||
<gl-toggle
|
||||
v-model="showInactive"
|
||||
:label="$options.i18n.showInactiveLabel"
|
||||
label-position="left"
|
||||
data-testid="show-inactive-toggle"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="loadedPrimaryDeploymentsCount > 0"
|
||||
class="gl-flex gl-flex-col gl-gap-4"
|
||||
data-testid="primary-deployment-list"
|
||||
>
|
||||
<pages-deployment
|
||||
v-for="node in primaryDeployments.nodes"
|
||||
:key="node.id"
|
||||
:deployment="node"
|
||||
class="gl-mb-5"
|
||||
:query="$apollo.queries.primaryDeployments"
|
||||
data-testid="primary-deployment"
|
||||
@error="onChildError"
|
||||
/>
|
||||
<load-more-deployments
|
||||
v-if="primaryDeployments && primaryDeployments.pageInfo.hasNextPage"
|
||||
:total-deployment-count="primaryDeploymentsNotLoaded"
|
||||
:loading="$apollo.queries.primaryDeployments.loading"
|
||||
class="gl-mt-3"
|
||||
data-testid="load-more-primary-deployments"
|
||||
@load-more="fetchMorePrimaryDeployments"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="loadedEnvironmentDeploymentsCount > 0" data-testid="environment-deployment-list">
|
||||
<h3 class="gl-heading-3">{{ $options.i18n.environmentDeploymentsTitle }}</h3>
|
||||
<div class="gl-flex gl-flex-col gl-gap-4">
|
||||
<pages-deployment
|
||||
v-for="node in environmentDeployments.nodes"
|
||||
:key="node.id"
|
||||
:deployment="node"
|
||||
:query="$apollo.queries.environmentDeployments"
|
||||
data-testid="environment-deployment"
|
||||
@error="onChildError"
|
||||
/>
|
||||
</div>
|
||||
<load-more-deployments
|
||||
v-if="environmentDeployments && environmentDeployments.pageInfo.hasNextPage"
|
||||
:total-deployment-count="environmentDeploymentsNotLoaded"
|
||||
:loading="$apollo.queries.environmentDeployments.loading"
|
||||
class="gl-mt-3"
|
||||
data-testid="load-more-environment-deployments"
|
||||
@load-more="fetchMoreEnvironmentDeployments"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!primaryDeployments && !environmentDeployments && $apollo.loading">
|
||||
<gl-loading-icon size="md" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!loadedPrimaryDeploymentsCount && !loadedEnvironmentDeploymentsCount"
|
||||
class="gl-text-center gl-text-secondary"
|
||||
>
|
||||
{{ $options.i18n.noDeploymentsMessage }}
|
||||
</div>
|
||||
<gl-alert v-if="hasError" variant="danger" :dismissible="false">
|
||||
{{ $options.i18n.loadErrorMessage }}
|
||||
</gl-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import PagesDeployments from './deployments.vue';
|
||||
|
||||
export default {
|
||||
name: 'PagesEdit',
|
||||
components: { PagesDeployments },
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pages-deployments class="mb-5" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'LoadMoreDeployments',
|
||||
components: { GlButton },
|
||||
props: {
|
||||
totalDeploymentCount: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-flex gl-items-center gl-gap-2 gl-justify-center">
|
||||
<span>
|
||||
{{ sprintf(__('+ %{n} more deployments'), { n: totalDeploymentCount }) }}
|
||||
</span>
|
||||
<gl-button
|
||||
category="tertiary"
|
||||
variant="confirm"
|
||||
size="sm"
|
||||
:loading="loading"
|
||||
@click="$emit('load-more')"
|
||||
>
|
||||
{{ __('Load more') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
query GetProjectPagesDeployments(
|
||||
$fullPath: ID!
|
||||
$first: Int
|
||||
$last: Int
|
||||
$after: String
|
||||
$before: String
|
||||
$active: Boolean
|
||||
$versioned: Boolean
|
||||
$sort: Sort
|
||||
) {
|
||||
project(fullPath: $fullPath) {
|
||||
id
|
||||
pagesDeployments(
|
||||
first: $first
|
||||
last: $last
|
||||
after: $after
|
||||
before: $before
|
||||
active: $active
|
||||
versioned: $versioned
|
||||
sort: $sort
|
||||
) {
|
||||
count
|
||||
pageInfo {
|
||||
startCursor
|
||||
endCursor
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
nodes {
|
||||
id
|
||||
active
|
||||
rootDirectory
|
||||
ciBuildId
|
||||
createdAt
|
||||
deletedAt
|
||||
fileCount
|
||||
pathPrefix
|
||||
size
|
||||
updatedAt
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import PagesEdit from './components/pages_edit.vue';
|
||||
import PagesEdit from './components/edit.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
|
|
@ -20,12 +20,11 @@ export default function initPages() {
|
|||
el,
|
||||
name: 'GitlabPagesEditRoot',
|
||||
apolloProvider,
|
||||
provide: {
|
||||
projectFullPath: el.dataset.fullPath,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(PagesEdit, {
|
||||
props: {
|
||||
...el.dataset,
|
||||
},
|
||||
});
|
||||
return createElement(PagesEdit, {});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,56 @@
|
|||
<script>
|
||||
import produce from 'immer';
|
||||
import { GlLink, GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { createAlert } from '~/alert';
|
||||
import { sprintf, __, n__ } from '~/locale';
|
||||
import RelatedIssuableItem from '~/issuable/components/related_issuable_item.vue';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import relatedMergeRequestsQuery from '../queries/related_merge_requests.query.graphql';
|
||||
|
||||
export default {
|
||||
name: 'RelatedMergeRequests',
|
||||
apollo: {
|
||||
mergeRequests: {
|
||||
query: relatedMergeRequestsQuery,
|
||||
variables() {
|
||||
return {
|
||||
projectPath: this.projectPath,
|
||||
iid: this.iid,
|
||||
};
|
||||
},
|
||||
update: (d) => d?.project?.issue?.relatedMergeRequests?.nodes,
|
||||
result({ data }) {
|
||||
const pageInfo = data?.project?.issue?.relatedMergeRequests?.pageInfo;
|
||||
|
||||
this.totalCount = data?.project?.issue?.relatedMergeRequests?.count;
|
||||
|
||||
if (pageInfo?.hasNextPage) {
|
||||
this.$apollo.queries.mergeRequests.fetchMore({
|
||||
variables: {
|
||||
projectPath: this.projectPath,
|
||||
iid: this.iid,
|
||||
after: pageInfo.endCursor,
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
const newMergeRequests = fetchMoreResult.project.issue.relatedMergeRequests.nodes;
|
||||
const prevMergeRequests = previousResult.project.issue.relatedMergeRequests.nodes;
|
||||
|
||||
return produce(fetchMoreResult, (draftData) => {
|
||||
draftData.project.issue.relatedMergeRequests.nodes = prevMergeRequests.concat(
|
||||
newMergeRequests,
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
error() {
|
||||
createAlert({
|
||||
message: __('Something went wrong while fetching related merge requests.'),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
GlIcon,
|
||||
GlLink,
|
||||
|
|
@ -14,26 +58,30 @@ export default {
|
|||
RelatedIssuableItem,
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
hasClosingMergeRequest: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
projectNamespace: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
iid: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mergeRequests: null,
|
||||
totalCount: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['isFetchingMergeRequests', 'mergeRequests', 'totalCount']),
|
||||
isFetchingMergeRequests() {
|
||||
return this.$apollo.queries.mergeRequests.loading;
|
||||
},
|
||||
closingMergeRequestsText() {
|
||||
if (!this.hasClosingMergeRequest) {
|
||||
return '';
|
||||
|
|
@ -48,18 +96,9 @@ export default {
|
|||
return sprintf(__('%{mrText}, this issue will be closed automatically.'), { mrText });
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setInitialState({ apiEndpoint: this.endpoint });
|
||||
this.fetchMergeRequests();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setInitialState', 'fetchMergeRequests']),
|
||||
getAssignees(mr) {
|
||||
if (mr.assignees) {
|
||||
return mr.assignees;
|
||||
}
|
||||
|
||||
return mr.assignee ? [mr.assignee] : [];
|
||||
idKey(mergeRequest) {
|
||||
return getIdFromGraphQLId(mergeRequest.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -107,19 +146,18 @@ export default {
|
|||
class="list-item gl-m-0! gl-p-0! gl-border-b-0!"
|
||||
>
|
||||
<related-issuable-item
|
||||
:id-key="mr.id"
|
||||
:id-key="idKey(mr)"
|
||||
:display-reference="mr.reference"
|
||||
:title="mr.title"
|
||||
:milestone="mr.milestone"
|
||||
:assignees="getAssignees(mr)"
|
||||
:created-at="mr.created_at"
|
||||
:closed-at="mr.closed_at"
|
||||
:merged-at="mr.merged_at"
|
||||
:path="mr.web_url"
|
||||
:assignees="mr.assignees.nodes"
|
||||
:created-at="mr.createdAt"
|
||||
:merged-at="mr.mergedAt"
|
||||
:path="mr.webUrl"
|
||||
:state="mr.state"
|
||||
:is-merge-request="true"
|
||||
:pipeline-status="mr.head_pipeline && mr.head_pipeline.detailed_status"
|
||||
:pipeline-status="mr.headPipeline && mr.headPipeline.detailedStatus"
|
||||
path-id-separator="!"
|
||||
is-merge-request
|
||||
class="-gl-mx-2"
|
||||
/>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import RelatedMergeRequests from './components/related_merge_requests.vue';
|
||||
import createStore from './store';
|
||||
|
||||
export function initRelatedMergeRequests() {
|
||||
const el = document.querySelector('#js-related-merge-requests');
|
||||
|
|
@ -9,20 +10,23 @@ export function initRelatedMergeRequests() {
|
|||
if (!el) {
|
||||
return undefined;
|
||||
}
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const { endpoint, hasClosingMergeRequest, projectPath, projectNamespace } = el.dataset;
|
||||
const { hasClosingMergeRequest, projectPath, iid } = el.dataset;
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
return new Vue({
|
||||
apolloProvider,
|
||||
el,
|
||||
name: 'RelatedMergeRequestsRoot',
|
||||
store: createStore(),
|
||||
render: (createElement) =>
|
||||
createElement(RelatedMergeRequests, {
|
||||
props: {
|
||||
endpoint,
|
||||
hasClosingMergeRequest: parseBoolean(hasClosingMergeRequest),
|
||||
projectNamespace,
|
||||
projectPath,
|
||||
iid,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
#import "~/graphql_shared/fragments/ci_icon.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/user.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/milestone.fragment.graphql"
|
||||
|
||||
query fetchRelatedMergeRequests($projectPath: ID!, $iid: String!, $after: String = "") {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
issue(iid: $iid) {
|
||||
id
|
||||
relatedMergeRequests(after: $after) {
|
||||
count
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
nodes {
|
||||
id
|
||||
reference
|
||||
state
|
||||
title
|
||||
createdAt
|
||||
mergedAt
|
||||
webUrl
|
||||
milestone {
|
||||
...MilestoneFragment
|
||||
}
|
||||
assignees {
|
||||
nodes {
|
||||
...User
|
||||
}
|
||||
}
|
||||
headPipeline {
|
||||
id
|
||||
detailedStatus {
|
||||
...CiIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
const REQUEST_PAGE_COUNT = 100;
|
||||
|
||||
export const setInitialState = ({ commit }, props) => {
|
||||
commit(types.SET_INITIAL_STATE, props);
|
||||
};
|
||||
|
||||
export const requestData = ({ commit }) => commit(types.REQUEST_DATA);
|
||||
|
||||
export const receiveDataSuccess = ({ commit }, data) => commit(types.RECEIVE_DATA_SUCCESS, data);
|
||||
|
||||
export const receiveDataError = ({ commit }) => commit(types.RECEIVE_DATA_ERROR);
|
||||
|
||||
export const fetchMergeRequests = ({ state, dispatch }) => {
|
||||
dispatch('requestData');
|
||||
|
||||
return axios
|
||||
.get(`${state.apiEndpoint}?per_page=${REQUEST_PAGE_COUNT}`)
|
||||
.then((res) => {
|
||||
const { headers, data } = res;
|
||||
const total = Number(normalizeHeaders(headers)['X-TOTAL']) || 0;
|
||||
|
||||
dispatch('receiveDataSuccess', { data, total });
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch('receiveDataError');
|
||||
createAlert({
|
||||
message: __('Something went wrong while fetching related merge requests.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import * as actions from './actions';
|
||||
import mutations from './mutations';
|
||||
import createState from './state';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default () =>
|
||||
new Vuex.Store({
|
||||
state: createState(),
|
||||
actions,
|
||||
mutations,
|
||||
});
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
|
||||
export const REQUEST_DATA = 'REQUEST_DATA';
|
||||
export const RECEIVE_DATA_SUCCESS = 'RECEIVE_DATA_SUCCESS';
|
||||
export const RECEIVE_DATA_ERROR = 'RECEIVE_DATA_ERROR';
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_INITIAL_STATE](state, { apiEndpoint }) {
|
||||
state.apiEndpoint = apiEndpoint;
|
||||
},
|
||||
[types.REQUEST_DATA](state) {
|
||||
state.isFetchingMergeRequests = true;
|
||||
},
|
||||
[types.RECEIVE_DATA_SUCCESS](state, { data, total }) {
|
||||
state.isFetchingMergeRequests = false;
|
||||
state.mergeRequests = data;
|
||||
state.totalCount = total;
|
||||
},
|
||||
[types.RECEIVE_DATA_ERROR](state) {
|
||||
state.isFetchingMergeRequests = false;
|
||||
state.hasErrorFetchingMergeRequests = true;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
export default () => ({
|
||||
apiEndpoint: '',
|
||||
isFetchingMergeRequests: false,
|
||||
hasErrorFetchingMergeRequests: false,
|
||||
mergeRequests: [],
|
||||
totalCount: 0,
|
||||
});
|
||||
|
|
@ -120,7 +120,7 @@ export default {
|
|||
<template #status-badge>
|
||||
<gl-sprintf v-if="closedStatusLink" :message="statusText">
|
||||
<template #link>
|
||||
<gl-link class="!gl-text-inherit gl-text-decoration-underline" :href="closedStatusLink">{{
|
||||
<gl-link class="!gl-text-inherit gl-underline" :href="closedStatusLink">{{
|
||||
closedStatusText
|
||||
}}</gl-link>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default {
|
|||
<gl-link
|
||||
data-testid="pipeline-sha"
|
||||
:href="pipeline.commitPath"
|
||||
class="gl-mr-2 gl-text-decoration-underline"
|
||||
class="gl-mr-2 gl-underline"
|
||||
>{{ packageShaShort }}</gl-link
|
||||
>
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
<span>
|
||||
<gl-sprintf :message="publishedMessage">
|
||||
<template v-if="projectName" #projectName>
|
||||
<gl-link class="gl-text-decoration-underline" :href="projectUrl">{{ projectName }}</gl-link>
|
||||
<gl-link class="gl-underline" :href="projectUrl">{{ projectName }}</gl-link>
|
||||
</template>
|
||||
<template #date>
|
||||
<time-ago-tooltip :time="publishDate" />
|
||||
|
|
|
|||
|
|
@ -207,11 +207,9 @@ export default {
|
|||
id="peek-view-trace"
|
||||
class="view"
|
||||
>
|
||||
<gl-link
|
||||
class="gl-text-decoration-underline"
|
||||
:href="currentRequest.details.tracing.tracing_url"
|
||||
>{{ s__('PerformanceBar|Trace') }}</gl-link
|
||||
>
|
||||
<gl-link class="gl-underline" :href="currentRequest.details.tracing.tracing_url">{{
|
||||
s__('PerformanceBar|Trace')
|
||||
}}</gl-link>
|
||||
</div>
|
||||
<div v-if="showFlamegraphButtons" id="peek-flamegraph" class="view">
|
||||
<gl-link
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="labels-select-contents-create js-labels-create">
|
||||
<div class="labels-select-contents-create">
|
||||
<div class="dropdown-title gl-flex gl-items-center pt-0 pb-2 gl-mb-0">
|
||||
<gl-button
|
||||
:aria-label="__('Go back')"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { get } from 'lodash';
|
||||
import { GlAlert, GlTooltipDirective, GlButton, GlFormInput, GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlAlert, GlTooltipDirective, GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import produce from 'immer';
|
||||
import { createAlert } from '~/alert';
|
||||
import { WORKSPACE_GROUP } from '~/issues/constants';
|
||||
|
|
@ -15,8 +15,8 @@ export default {
|
|||
components: {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlLoadingIcon,
|
||||
SidebarColorPicker,
|
||||
},
|
||||
directives: {
|
||||
|
|
@ -135,43 +135,43 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="labels-select-contents-create js-labels-create">
|
||||
<div class="dropdown-input">
|
||||
<gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3">
|
||||
{{ error }}
|
||||
</gl-alert>
|
||||
<div class="gl-px-4">
|
||||
<gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3">
|
||||
{{ error }}
|
||||
</gl-alert>
|
||||
<gl-form-group
|
||||
class="gl-my-3"
|
||||
:label="__('Name new label')"
|
||||
label-for="label-title-input"
|
||||
label-sr-only
|
||||
>
|
||||
<gl-form-input
|
||||
id="label-title-input"
|
||||
v-model.trim="labelTitle"
|
||||
class="gl-mt-3"
|
||||
autofocus
|
||||
:placeholder="__('Name new label')"
|
||||
:autofocus="true"
|
||||
data-testid="label-title-input"
|
||||
/>
|
||||
</div>
|
||||
<sidebar-color-picker
|
||||
v-model.trim="selectedColor"
|
||||
:suggested-colors="suggestedColors"
|
||||
class="gl-px-4 gl-py-2"
|
||||
/>
|
||||
<div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3">
|
||||
<gl-button
|
||||
:disabled="disableCreate"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
class="gl-display-flex gl-align-items-center"
|
||||
data-testid="create-button"
|
||||
@click="createLabel"
|
||||
>
|
||||
<gl-loading-icon v-if="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
|
||||
{{ __('Create') }}
|
||||
</gl-button>
|
||||
</gl-form-group>
|
||||
<sidebar-color-picker v-model.trim="selectedColor" :suggested-colors="suggestedColors" />
|
||||
<div class="gl-flex gl-justify-end gl-gap-3 gl-mt-2">
|
||||
<gl-button
|
||||
class="js-btn-cancel-create"
|
||||
size="small"
|
||||
data-testid="cancel-button"
|
||||
@click.stop="$emit('hideCreateView')"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
:disabled="disableCreate"
|
||||
:loading="labelCreateInProgress"
|
||||
size="small"
|
||||
variant="confirm"
|
||||
data-testid="create-button"
|
||||
@click="createLabel"
|
||||
>
|
||||
{{ __('Create') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
<div data-testid="dropdown-header">
|
||||
<div
|
||||
v-if="!isStandalone"
|
||||
class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3! gl-mb-0"
|
||||
class="dropdown-title gl-display-flex gl-align-items-center gl-pt-2 gl-pb-4 gl-mb-0"
|
||||
data-testid="dropdown-header-title"
|
||||
>
|
||||
<gl-button
|
||||
|
|
@ -59,7 +59,7 @@ export default {
|
|||
:aria-label="__('Go back')"
|
||||
variant="link"
|
||||
size="small"
|
||||
class="js-btn-back dropdown-header-button gl-p-0"
|
||||
class="js-btn-back dropdown-header-button !gl-p-0"
|
||||
icon="arrow-left"
|
||||
data-testid="go-back-button"
|
||||
@click.stop="$emit('toggleDropdownContentsCreateView')"
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ export default {
|
|||
<gl-sprintf :message="$options.i18n.legal">
|
||||
<template #link="{ content }">
|
||||
<a
|
||||
class="gl-text-decoration-underline gl-text-gray-500"
|
||||
class="gl-underline gl-text-gray-500"
|
||||
:href="$options.privacyLink"
|
||||
target="_blank"
|
||||
rel="noreferrer nofollow"
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ export const generateText = (text) => {
|
|||
}
|
||||
if (typeof text === 'object' && typeof text.text === 'string' && typeof text.href === 'string') {
|
||||
return createText(
|
||||
`${
|
||||
text.prependText ? `${escapeText(text.prependText)} ` : ''
|
||||
}<a class="gl-text-decoration-underline" href="${text.href}">${escapeText(text.text)}</a>`,
|
||||
`${text.prependText ? `${escapeText(text.prependText)} ` : ''}<a class="gl-underline" href="${
|
||||
text.href
|
||||
}">${escapeText(text.text)}</a>`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,9 @@
|
|||
= render_if_exists 'projects/issues/linked_resources'
|
||||
= render 'projects/issues/related_issues'
|
||||
|
||||
#js-related-merge-requests{ data: { endpoint: expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: issuable.iid)),
|
||||
has_closing_merge_request: (issuable.merge_requests_count(current_user) != 0).to_s,
|
||||
project_namespace: @project.namespace.path,
|
||||
project_path: @project.path } }
|
||||
#js-related-merge-requests{ data: { has_closing_merge_request: (issuable.merge_requests_count(current_user) != 0).to_s,
|
||||
project_path: @project.full_path,
|
||||
iid: issuable.iid } }
|
||||
|
||||
- if can?(current_user, :read_code, @project)
|
||||
- add_page_startup_api_call related_branches_path
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
- if Feature.enabled?('new_pages_ui', @project)
|
||||
#js-pages
|
||||
- else
|
||||
- page_title s_('GitLabPages|Pages')
|
||||
- page_title s_('GitLabPages|Pages')
|
||||
|
||||
- unless @project.pages_deployed?
|
||||
= render 'waiting'
|
||||
- unless @project.pages_deployed?
|
||||
= render 'waiting'
|
||||
- else
|
||||
= render 'header'
|
||||
- if Feature.enabled?('new_pages_ui', @project)
|
||||
#js-pages{ data: { full_path: @project.full_path } }
|
||||
%h2.gl-heading-2
|
||||
= s_('Settings')
|
||||
= render 'pages_settings'
|
||||
= render 'ssl_limitations_warning' if pages_subdomain(@project).include?(".")
|
||||
= render 'access'
|
||||
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
|
||||
= render 'list'
|
||||
- else
|
||||
= render 'header'
|
||||
= render 'pages_settings'
|
||||
= render 'ssl_limitations_warning' if pages_subdomain(@project).include?(".")
|
||||
= render 'access'
|
||||
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
|
||||
= render 'list'
|
||||
- else
|
||||
= render 'no_domains'
|
||||
= render 'destroy'
|
||||
= render 'no_domains'
|
||||
= render 'destroy'
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
= s_("Profiles|This email will be displayed on your public profile.")
|
||||
|
||||
.form-group.gl-form-group
|
||||
- commit_email_link_url = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits')
|
||||
- commit_email_link_url = help_page_path('user/profile/index', anchor: 'use-an-automatically-generated-private-commit-email')
|
||||
- commit_email_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: commit_email_link_url }
|
||||
- commit_email_docs_link = s_('Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}.').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
|
||||
- commit_email_docs_link = s_('Profiles|This email is used for web-based operations, such as edits and merges. %{commit_email_link_start}What is a private commit email?%{commit_email_link_end}').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
|
||||
= form.label :commit_email, s_('Profiles|Commit email')
|
||||
.gl-md-form-input-lg
|
||||
= form.select :commit_email,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- form = local_assigns.fetch(:form)
|
||||
|
||||
- private_profile_help_link = link_to _("Learn more"), help_page_path('user/profile/index', anchor: 'make-your-user-profile-page-private')
|
||||
- private_profile_label = safe_format(s_("Profiles|Don't display activity-related personal information on your profile. %{private_profile_help_link_start}Learn more%{private_profile_help_link_end}."), tag_pair(private_profile_help_link, :private_profile_help_link_start, :private_profile_help_link_end))
|
||||
- private_profile_help_link = link_to _("What information is hidden?"), help_page_path('user/profile/index', anchor: 'make-your-user-profile-page-private')
|
||||
- private_profile_label = safe_format(s_("Profiles|Don't display activity-related personal information on your profile. %{private_profile_help_link_start}What information is hidden?%{private_profile_help_link_end}"), tag_pair(private_profile_help_link, :private_profile_help_link_start, :private_profile_help_link_end))
|
||||
|
||||
= form.gitlab_ui_checkbox_component :private_profile, private_profile_label
|
||||
|
|
|
|||
|
|
@ -110,9 +110,7 @@
|
|||
= f.label :twitter, _('X (formerly Twitter)')
|
||||
= f.text_field :twitter, class: 'gl-form-input form-control gl-md-form-input-lg', placeholder: s_("Profiles|@username")
|
||||
.form-group.gl-form-group
|
||||
- external_accounts_help_url = help_page_path('user/profile/index', anchor: 'add-external-accounts-to-your-user-profile-page')
|
||||
- external_accounts_link = link_to '', external_accounts_help_url, target: "_blank", rel: "noopener noreferrer"
|
||||
- external_accounts_docs_link = safe_format(s_('Profiles|Your Discord user ID. %{external_accounts_link_start}Learn more%{external_accounts_link_end}.'), tag_pair(external_accounts_link, :external_accounts_link_start, :external_accounts_link_end))
|
||||
- external_accounts_docs_link = safe_format(s_('Profiles|Your Discord user ID.'))
|
||||
- min_discord_length = 17
|
||||
- max_discord_length = 20
|
||||
= f.label :discord
|
||||
|
|
|
|||
|
|
@ -794,7 +794,7 @@ Gitlab.ee do
|
|||
Settings.cron_jobs['elastic_index_initial_bulk_cron_worker']['cron'] ||= '*/1 * * * *'
|
||||
Settings.cron_jobs['elastic_index_initial_bulk_cron_worker']['job_class'] ||= 'ElasticIndexInitialBulkCronWorker'
|
||||
Settings.cron_jobs['elastic_cluster_reindexing_cron_worker'] ||= {}
|
||||
Settings.cron_jobs['elastic_cluster_reindexing_cron_worker']['cron'] ||= '*/10 * * * *'
|
||||
Settings.cron_jobs['elastic_cluster_reindexing_cron_worker']['cron'] ||= '*/5 * * * *'
|
||||
Settings.cron_jobs['elastic_cluster_reindexing_cron_worker']['job_class'] ||= 'ElasticClusterReindexingCronWorker'
|
||||
Settings.cron_jobs['elastic_remove_expired_namespace_subscriptions_from_index_cron_worker'] ||= {}
|
||||
Settings.cron_jobs['elastic_remove_expired_namespace_subscriptions_from_index_cron_worker']['cron'] ||= '10 3 * * *'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "apiFuzzingCiConfigurationCreate GraphQL mutation"
|
||||
- title: "`apiFuzzingCiConfigurationCreate` GraphQL mutation"
|
||||
announcement_milestone: "14.6"
|
||||
removal_milestone: "15.0"
|
||||
breaking_change: true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "GraphQL networkPolicies resource deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters."
|
||||
- title: "GraphQL `networkPolicies` resource deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters."
|
||||
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
|
||||
announcement_milestone: "14.8" # (required) The milestone when this feature was first announced as deprecated.
|
||||
breaking_change: true # (required) Change to false if this is not a breaking change.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "PipelineSecurityReportFinding name GraphQL field" # (required) The name of the feature to be deprecated
|
||||
- title: "`name` field for `PipelineSecurityReportFinding` GraphQL type" # (required) The name of the feature to be deprecated
|
||||
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
|
||||
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
|
||||
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346335 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
Previously, the [PipelineSecurityReportFinding GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field will be removed from the PipelineSecurityReportFinding type in GitLab 16.0.
|
||||
Previously, the [`PipelineSecurityReportFinding` GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field will be removed from the `PipelineSecurityReportFinding` type in GitLab 16.0.
|
||||
# The following items are not published on the docs page, but may be used in the future.
|
||||
tiers: Ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
documentation_url: # (optional) This is a link to the current documentation page
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "PipelineSecurityReportFinding projectFingerprint GraphQL field" # (required) The name of the feature to be deprecated
|
||||
- title: "`projectFingerprint` GraphQL field" # (required) The name of the feature to be deprecated
|
||||
announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated.
|
||||
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
|
||||
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343475 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
The [`project_fingerprint`](https://gitlab.com/groups/gitlab-org/-/epics/2791) attribute of vulnerability findings is being deprecated in favor of a `uuid` attribute. By using UUIDv5 values to identify findings, we can easily associate any related entity with a finding. The `project_fingerprint` attribute is no longer being used to track findings, and will be removed in GitLab 17.0. Starting in 16.1, the output of `project_fingerprint` returns the same value as the `uuid` field.
|
||||
The [`project_fingerprint`](https://gitlab.com/groups/gitlab-org/-/epics/2791) attribute of vulnerability findings is being deprecated in favor of a `uuid` attribute. By using UUIDv5 values to identify findings, we can easily associate any related entity with a finding. The `project_fingerprint` attribute is no longer being used to track findings, and will be removed in GitLab 17.0. Starting in 16.1, the output of `project_fingerprint` returns the same value as the `uuid` field.
|
||||
# The following items are not published on the docs page, but may be used in the future.
|
||||
tiers: Ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
documentation_url: # (optional) This is a link to the current documentation page
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
# REQUIRED FIELDS
|
||||
#
|
||||
- title: "Use of `id` field in vulnerabilityFindingDismiss mutation" # (required) The name of the feature to be deprecated
|
||||
- title: "Use of `id` field in `vulnerabilityFindingDismiss` mutation" # (required) The name of the feature to be deprecated
|
||||
announcement_milestone: "15.3" # (required) The milestone when this feature was first announced as deprecated.
|
||||
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
|
||||
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367166 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
|
||||
You can use the `vulnerabilityFindingDismiss` GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
|
||||
#
|
||||
# OPTIONAL FIELDS
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "vulnerabilityFindingDismiss GraphQL mutation"
|
||||
- title: "`vulnerabilityFindingDismiss` GraphQL mutation"
|
||||
announcement_milestone: "15.5"
|
||||
removal_milestone: "16.0"
|
||||
breaking_change: true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
# REQUIRED FIELDS
|
||||
#
|
||||
- title: "GraphQL: The `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` enum is deprecated. Use `DISABLED_AND_OVERRIDABLE` instead" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
|
||||
- title: "GraphQL: The `DISABLED_WITH_OVERRIDE` value for the `SharedRunnersSetting` enum is deprecated" # (required) Actionable title. e.g., The `confidential` field for a `Note` is deprecated. Use `internal` instead.
|
||||
announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated.
|
||||
announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
|
||||
|
|
@ -11,7 +11,8 @@
|
|||
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385636 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
In GitLab 17.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be replaced with the value, `DISABLED_AND_OVERRIDABLE`.
|
||||
In GitLab 17.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be removed.
|
||||
Use `DISABLED_AND_OVERRIDABLE` instead.
|
||||
#
|
||||
# OTHER OPTIONAL FIELDS
|
||||
#
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#
|
||||
# REQUIRED FIELDS
|
||||
#
|
||||
- title: "HashiCorp Vault integration will no longer use CI_JOB_JWT by default"
|
||||
- title: "HashiCorp Vault integration will no longer use the `CI_JOB_JWT` CI/CD job token by default"
|
||||
announcement_milestone: "15.9" # (required) The milestone when this feature was first announced as deprecated.
|
||||
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
|
||||
breaking_change: true # (required) Change to false if this is not a breaking change.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
stage: stage # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: "https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28165" # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
The GitLab Runner Kubernetes executor setting, `terminationGracePeriodSeconds`, is deprecated and will be removed in GitLab 17.0. To manage the cleanup and termination of GitLab Runner worker pods on Kubernetes, customers should instead configure `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`. For information about how to use the `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds, see the [GitLab Runner Executor documentation](https://docs.gitlab.com/runner/executors/kubernetes.html#other-configtoml-settings).
|
||||
The GitLab Runner Kubernetes executor setting, `terminationGracePeriodSeconds`, is deprecated and will be removed in GitLab 17.0. To manage the cleanup and termination of GitLab Runner worker pods on Kubernetes, customers should instead configure `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`. For information about how to use the `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`, see the [GitLab Runner Executor documentation](https://docs.gitlab.com/runner/executors/kubernetes.html#other-configtoml-settings).
|
||||
|
||||
#
|
||||
# OPTIONAL END OF SUPPORT FIELDS
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title: "Rename the 'require_password_to_approve' field"
|
||||
- title: "Rename the `require_password_to_approve` field"
|
||||
removal_milestone: "17.0"
|
||||
announcement_milestone: "16.9"
|
||||
breaking_change: true
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@ flow of how we construct a Chat prompt:
|
|||
GitLab Duo Chat has error codes with specified meanings to assist in debugging.
|
||||
Currently, they are only logged, but in the future, they will be displayed on the UI.
|
||||
|
||||
When developing for GitLab Duo Chat, please include these error codes when returning an error, especially a user-facing error.
|
||||
When developing for GitLab Duo Chat, please include these error codes when returning an error and [document them](../../user/gitlab_duo_chat/troubleshooting.md), especially for user-facing errors.
|
||||
|
||||
### Error Code Format
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ You can use the [important modifier](https://tailwindcss.com/docs/configuration#
|
|||
Spacing and sizing CSS utility classes (e.g. `margin`, `padding`, `width`, `height`) use our spacing scale defined in
|
||||
Color CSS utility classes (e.g. `color` and `background-color`) use colors defined in
|
||||
[src/tokens/build/tailwind/tokens.cjs](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/24a08b50da6bd3d34fb3f8d24f84436d90d165f6/src/tokens/build/tailwind/tokens.cjs).
|
||||
They will use the naming conventions documented in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/installation)
|
||||
They will use the naming conventions documented in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/padding)
|
||||
but the color names will not match. When using the [Tailwind CSS autocomplete](#tailwind-css-autocomplete)
|
||||
our configured colors will be shown.
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ our configured colors will be shown.
|
|||
|
||||
Color CSS utility classes (e.g. `color` and `background-color`) use colors defined in
|
||||
[src/tokens/build/tailwind/tokens.cjs](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/24a08b50da6bd3d34fb3f8d24f84436d90d165f6/src/tokens/build/tailwind/tokens.cjs).
|
||||
They will use the naming conventions documented in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/installation)
|
||||
They will use the naming conventions documented in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/text-color)
|
||||
but the color names will not match. When using the [Tailwind CSS autocomplete](#tailwind-css-autocomplete)
|
||||
our configured colors will be shown.
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ For full HAML and custom `*-class` prop support these are the recommended update
|
|||
|
||||
GitLab defines its own Tailwind CSS config in [tailwind.defaults.js](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/6612eaee37cdb4dd0258468c9f415be28c1053f0/tailwind.defaults.js)
|
||||
to match the Pajamas design system and to prefix CSS utility classes with `gl-`.
|
||||
This means that in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/installation)
|
||||
This means that in the [official Tailwind CSS documentation](https://tailwindcss.com/docs/padding)
|
||||
the spacing, sizing, and color CSS utility classes may not match. Also, the `gl-`
|
||||
prefix will not be shown. Here is our
|
||||
[spacing scale](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/6612eaee37cdb4dd0258468c9f415be28c1053f0/tailwind.defaults.js#L4)
|
||||
|
|
|
|||
|
|
@ -358,6 +358,16 @@ Check the [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules) for mor
|
|||
<my-component />
|
||||
```
|
||||
|
||||
## `<style>` tags
|
||||
|
||||
We don't use `<style>` tags in Vue components for a few reasons:
|
||||
|
||||
1. You cannot use SCSS variables and mixins or [Tailwind CSS](scss.md#tailwind-css) `@apply` directive.
|
||||
1. These styles get inserted at runtime.
|
||||
1. We already have a few other ways to define CSS.
|
||||
|
||||
Instead of using a `<style>` tag you should use [Tailwind CSS utility classes](scss.md#tailwind-css) or [page specific CSS](https://gitlab.com/groups/gitlab-org/-/epics/3694).
|
||||
|
||||
## Ordering
|
||||
|
||||
1. Tag order in `.vue` file
|
||||
|
|
@ -371,7 +381,7 @@ Check the [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules) for mor
|
|||
// ...
|
||||
</template>
|
||||
|
||||
// We don't use scoped styles but there are few instances of this
|
||||
// We don't use `<style>` tags but there are few instances of this
|
||||
<style>
|
||||
// ...
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1137,7 +1137,7 @@ The message field was removed from security reports schema in GitLab 16.0 and is
|
|||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/28165).
|
||||
</div>
|
||||
|
||||
The GitLab Runner Kubernetes executor setting, `terminationGracePeriodSeconds`, is deprecated and will be removed in GitLab 17.0. To manage the cleanup and termination of GitLab Runner worker pods on Kubernetes, customers should instead configure `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`. For information about how to use the `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds, see the [GitLab Runner Executor documentation](https://docs.gitlab.com/runner/executors/kubernetes.html#other-configtoml-settings).
|
||||
The GitLab Runner Kubernetes executor setting, `terminationGracePeriodSeconds`, is deprecated and will be removed in GitLab 17.0. To manage the cleanup and termination of GitLab Runner worker pods on Kubernetes, customers should instead configure `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`. For information about how to use the `cleanupGracePeriodSeconds` and `podTerminationGracePeriodSeconds`, see the [GitLab Runner Executor documentation](https://docs.gitlab.com/runner/executors/kubernetes.html#other-configtoml-settings).
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -1381,6 +1381,20 @@ For customers already using documented and supported token types, there are no b
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### GraphQL `networkPolicies` resource deprecated
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.8</span>
|
||||
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/421440).
|
||||
</div>
|
||||
|
||||
The `networkPolicies` [GraphQL resource](https://docs.gitlab.com/ee/api/graphql/reference/#projectnetworkpolicies) has been deprecated and will be removed in GitLab 17.0. Since GitLab 15.0 this field has returned no data.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### GraphQL deprecation of `dependencyProxyTotalSizeInBytes` field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -1443,20 +1457,6 @@ Use `totalIssueWeight` instead, introduced in GitLab 16.2.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### GraphQL networkPolicies resource deprecated
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.8</span>
|
||||
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/421440).
|
||||
</div>
|
||||
|
||||
The `networkPolicies` [GraphQL resource](https://docs.gitlab.com/ee/api/graphql/reference/#projectnetworkpolicies) has been deprecated and will be removed in GitLab 17.0. Since GitLab 15.0 this field has returned no data.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### GraphQL type, `RunnerMembershipFilter` renamed to `CiRunnerMembershipFilter`
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -1472,7 +1472,7 @@ the aliasing for the `RunnerMembershipFilter` type will be removed.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### GraphQL: The `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` enum is deprecated. Use `DISABLED_AND_OVERRIDABLE` instead
|
||||
### GraphQL: The `DISABLED_WITH_OVERRIDE` value for the `SharedRunnersSetting` enum is deprecated
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.8</span>
|
||||
|
|
@ -1480,7 +1480,8 @@ the aliasing for the `RunnerMembershipFilter` type will be removed.
|
|||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/385636).
|
||||
</div>
|
||||
|
||||
In GitLab 17.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be replaced with the value, `DISABLED_AND_OVERRIDABLE`.
|
||||
In GitLab 17.0, the `DISABLED_WITH_OVERRIDE` value of the `SharedRunnersSetting` GraphQL enum type will be removed.
|
||||
Use `DISABLED_AND_OVERRIDABLE` instead.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -1504,7 +1505,7 @@ This is a breaking change that will be completed in 17.0.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### HashiCorp Vault integration will no longer use CI_JOB_JWT by default
|
||||
### HashiCorp Vault integration will no longer use the `CI_JOB_JWT` CI/CD job token by default
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.9</span>
|
||||
|
|
@ -1839,20 +1840,6 @@ In milestone 17.0, we will remove the `pipelines` attribute from the API respons
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### PipelineSecurityReportFinding projectFingerprint GraphQL field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.1</span>
|
||||
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/343475).
|
||||
</div>
|
||||
|
||||
The [`project_fingerprint`](https://gitlab.com/groups/gitlab-org/-/epics/2791) attribute of vulnerability findings is being deprecated in favor of a `uuid` attribute. By using UUIDv5 values to identify findings, we can easily associate any related entity with a finding. The `project_fingerprint` attribute is no longer being used to track findings, and will be removed in GitLab 17.0. Starting in 16.1, the output of `project_fingerprint` returns the same value as the `uuid` field.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### PostgreSQL 13 no longer supported
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -1926,7 +1913,7 @@ For more information, see [Removing tags from our small SaaS runner on Linux](ht
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### Rename the 'require_password_to_approve' field
|
||||
### Rename the `require_password_to_approve` field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">16.9</span>
|
||||
|
|
@ -2421,6 +2408,20 @@ enable `postgres_exporter['flags']['collector.stat_user_tables']` instead.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### `projectFingerprint` GraphQL field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.1</span>
|
||||
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/343475).
|
||||
</div>
|
||||
|
||||
The [`project_fingerprint`](https://gitlab.com/groups/gitlab-org/-/epics/2791) attribute of vulnerability findings is being deprecated in favor of a `uuid` attribute. By using UUIDv5 values to identify findings, we can easily associate any related entity with a finding. The `project_fingerprint` attribute is no longer being used to track findings, and will be removed in GitLab 17.0. Starting in 16.1, the output of `project_fingerprint` returns the same value as the `uuid` field.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### npm package uploads now occur asynchronously
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -3535,20 +3536,6 @@ The option to delete groups and projects immediately by default was deprecated t
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### PipelineSecurityReportFinding name GraphQL field
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.1</span>
|
||||
- Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/346335).
|
||||
</div>
|
||||
|
||||
Previously, the [PipelineSecurityReportFinding GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field will be removed from the PipelineSecurityReportFinding type in GitLab 16.0.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### PostgreSQL 12 deprecated
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -3934,7 +3921,7 @@ In order to make the behavior of toggling the draft status of a merge request mo
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### Use of `id` field in vulnerabilityFindingDismiss mutation
|
||||
### Use of `id` field in `vulnerabilityFindingDismiss` mutation
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.3</span>
|
||||
|
|
@ -3942,7 +3929,7 @@ In order to make the behavior of toggling the draft status of a merge request mo
|
|||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/367166).
|
||||
</div>
|
||||
|
||||
You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
|
||||
You can use the `vulnerabilityFindingDismiss` GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -4048,6 +4035,20 @@ To avoid confusion and duplication, the `environment_tier` parameter is deprecat
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### `name` field for `PipelineSecurityReportFinding` GraphQL type
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.1</span>
|
||||
- Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/346335).
|
||||
</div>
|
||||
|
||||
Previously, the [`PipelineSecurityReportFinding` GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field will be removed from the `PipelineSecurityReportFinding` type in GitLab 16.0.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### `started` iteration state
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -4068,7 +4069,7 @@ We plan to continue to support the `started` state in REST API version until the
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="16.0">
|
||||
|
||||
### vulnerabilityFindingDismiss GraphQL mutation
|
||||
### `vulnerabilityFindingDismiss` GraphQL mutation
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">15.5</span>
|
||||
|
|
@ -5544,6 +5545,22 @@ In milestone 15.0, we will completely remove `Version` from `PackageType`.
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="15.0">
|
||||
|
||||
### `apiFuzzingCiConfigurationCreate` GraphQL mutation
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.6</span>
|
||||
- Removal in GitLab <span class="milestone">15.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/333233).
|
||||
</div>
|
||||
|
||||
The API Fuzzing configuration snippet is now being generated client-side and does not require an
|
||||
API request anymore. We are therefore deprecating the `apiFuzzingCiConfigurationCreate` mutation
|
||||
which isn't being used in GitLab anymore.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="15.0">
|
||||
|
||||
### `artifacts:reports:cobertura` keyword
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -5667,22 +5684,6 @@ The `type` and `types` CI/CD keywords will be removed in GitLab 15.0. Pipelines
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="15.0">
|
||||
|
||||
### apiFuzzingCiConfigurationCreate GraphQL mutation
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">14.6</span>
|
||||
- Removal in GitLab <span class="milestone">15.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/333233).
|
||||
</div>
|
||||
|
||||
The API Fuzzing configuration snippet is now being generated client-side and does not require an
|
||||
API request anymore. We are therefore deprecating the `apiFuzzingCiConfigurationCreate` mutation
|
||||
which isn't being used in GitLab anymore.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="15.0">
|
||||
|
||||
### bundler-audit Dependency Scanning tool
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Beta
|
||||
|
||||
- Helps populate a merge request more quickly by generating a description based on the contents of the template.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text)
|
||||
|
|
@ -105,6 +106,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Beta
|
||||
|
||||
- Helps populate a merge request more quickly by generating a description based on the code changes.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text)
|
||||
|
|
@ -115,6 +117,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Beta
|
||||
|
||||
- Helps you understand vulnerabilities, how they can be exploited, and how to fix them.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text). If degraded performance, then Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison).
|
||||
|
|
@ -128,6 +131,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps populate an issue more quickly by generating a more in-depth description, based on a short summary you provide.
|
||||
- LLM: Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison)
|
||||
|
|
@ -138,6 +142,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps everyone get up to speed by summarizing the lengthy conversations in an issue.
|
||||
- LLM: Anthropic [`Claude-2.1`](https://docs.anthropic.com/claude/docs/models-overview#model-comparison)
|
||||
|
|
@ -149,6 +154,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps you understand the selected code by explaining it more clearly.
|
||||
- LLM: Anthropic: [`claude-3-haiku-20240307`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
|
|
@ -161,6 +167,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- `glab duo ask` helps you discover or recall `git` commands when and where you need them.
|
||||
- LLM: Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat)
|
||||
|
|
@ -171,6 +178,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps make merge request handover to reviewers easier by summarizing all the comments in a merge request review.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text)
|
||||
|
|
@ -182,6 +190,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps you merge more quickly by generating meaningful commit messages.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text).
|
||||
|
|
@ -192,6 +201,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps you determine the root cause for a CI/CD job failure by analyzing the logs.
|
||||
- LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text)
|
||||
|
|
@ -202,6 +212,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Help resolve a vulnerability by generating a merge request that addresses it.
|
||||
- LLM: Vertex AI Codey [`code-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-generation)
|
||||
|
|
@ -212,6 +223,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
|
||||
**Offering:** GitLab.com
|
||||
**Status:** Experiment
|
||||
|
||||
- Processes and responds to your questions about your application's usage data.
|
||||
- LLM: Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/code-chat)
|
||||
|
|
@ -222,6 +234,7 @@ DETAILS:
|
|||
DETAILS:
|
||||
**Tier:** GitLab.com and Self-managed: For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Status:** Experiment
|
||||
|
||||
- Helps you improve planning and decision-making by predicting productivity metrics and identifying anomalies across your software development lifecycle.
|
||||
- LLM: Statistical forecasting
|
||||
|
|
|
|||
|
|
@ -107,11 +107,20 @@ Because of LLM limits and performance reasons, the content of the currently
|
|||
opened file is truncated:
|
||||
|
||||
- For code completion: to 2048 tokens (roughly 8192 characters).
|
||||
- For code generation: to 50,000 characters.
|
||||
- For code generation: to 142,856 tokens (roughly 500,000 characters).
|
||||
|
||||
Content above the cursor is prioritized over content below the cursor. The content
|
||||
above the cursor is truncated from the left side, and content below the cursor
|
||||
is truncated from the right side.
|
||||
is truncated from the right side. These numbers represent the maximum input context
|
||||
size for Code Suggestions.
|
||||
|
||||
## Output length
|
||||
|
||||
Because of LLM limits and for performance reasons, the output of Code Suggestions
|
||||
is limited:
|
||||
|
||||
- For code completion: to 64 tokens (roughly 256 characters).
|
||||
- For code generation: to 2048 tokens (roughly 7168 characters).
|
||||
|
||||
## Accuracy of results
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ The storage management and cleanup automation methods described in this page use
|
|||
|
||||
- The [`python-gitlab`](https://python-gitlab.readthedocs.io/en/stable/) library, which provides
|
||||
a feature-rich programming interface.
|
||||
- The `get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py` script in the [GitLab API with Python](https://gitlab.com/gitlab-de/use-cases/gitlab-api/gitlab-api-python/) project.
|
||||
- The `get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py` script in the [GitLab API with Python](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/) project.
|
||||
|
||||
For more information about use cases for the `python-gitlab` library,
|
||||
see [Efficient DevSecOps workflows: Hands-on `python-gitlab` API automation](https://about.gitlab.com/blog/2023/02/01/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation/).
|
||||
|
|
@ -212,7 +212,7 @@ To implement this algorithm:
|
|||
:::TabTitle GitLab CLI
|
||||
|
||||
```shell
|
||||
export GROUP_NAME="gitlab-de"
|
||||
export GROUP_NAME="gitlab-da"
|
||||
|
||||
# Return sub group IDs
|
||||
glab api groups/$GROUP_NAME/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
|
||||
|
|
@ -431,12 +431,12 @@ $ python3 get_all_projects_top_level_namespace_storage_analysis_cleanup_example.
|
|||
|
||||
|Project|Job|Artifact name|Artifact type|Artifact size|
|
||||
|-|-|-|-|-|
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | artifacts.zip | archive | 50.0154 |
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | metadata.gz | metadata | 0.0001 |
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | job.log | trace | 0.0030 |
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | artifacts.zip | archive | 20.0063 |
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | metadata.gz | metadata | 0.0001 |
|
||||
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | job.log | trace | 0.0030 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | artifacts.zip | archive | 50.0154 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | metadata.gz | metadata | 0.0001 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | job.log | trace | 0.0030 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | artifacts.zip | archive | 20.0063 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | metadata.gz | metadata | 0.0001 |
|
||||
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | job.log | trace | 0.0030 |
|
||||
```
|
||||
|
||||
### Delete job artifacts in bulk
|
||||
|
|
@ -607,7 +607,7 @@ In the following example that uses a Bash script:
|
|||
- `jq` and the GitLab CLI are installed and authorized.
|
||||
- The exported environment variable `GL_PROJECT_ID`.
|
||||
|
||||
The full script `get_cicd_pipelines_compare_age_threshold_example.sh` is located in the [GitLab API with Linux Shell](https://gitlab.com/gitlab-de/use-cases/gitlab-api/gitlab-api-linux-shell) project.
|
||||
The full script `get_cicd_pipelines_compare_age_threshold_example.sh` is located in the [GitLab API with Linux Shell](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-linux-shell) project.
|
||||
|
||||
```shell
|
||||
#/bin/bash
|
||||
|
|
@ -749,21 +749,21 @@ python3 get_all_cicd_config_artifacts_expiry.py
|
|||
|
||||
|Project|Job|Artifact expiry|
|
||||
|-|-|-|
|
||||
| [Gen Job Artifacts 4](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4) | generator | 30 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job10 | 10 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job1 | 1 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job30 | 30 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | generator | 30 days |
|
||||
| [Gen Job Artifacts 2](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-2) | generator | ❌ N/A |
|
||||
| [Gen Job Artifacts 1](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-1) | generator | ❌ N/A |
|
||||
| [Gen Job Artifacts 4](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4) | generator | 30 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job10 | 10 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job1 | 1 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job30 | 30 days |
|
||||
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | generator | 30 days |
|
||||
| [Gen Job Artifacts 2](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-2) | generator | ❌ N/A |
|
||||
| [Gen Job Artifacts 1](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-1) | generator | ❌ N/A |
|
||||
```
|
||||
|
||||
The `get_all_cicd_config_artifacts_expiry.py` script is located in the [GitLab API with Python project](https://gitlab.com/gitlab-de/use-cases/gitlab-api/gitlab-api-python/).
|
||||
The `get_all_cicd_config_artifacts_expiry.py` script is located in the [GitLab API with Python project](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/).
|
||||
|
||||
Alternatively, you can use [advanced search](search/advanced_search.md) with API requests. The following example uses the [scope: blobs](../api/search.md#scope-blobs) to searches for the string `artifacts` in all `*.yml` files:
|
||||
|
||||
```shell
|
||||
# https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs
|
||||
# https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs
|
||||
export GL_PROJECT_ID=48349263
|
||||
|
||||
glab api --method GET projects/$GL_PROJECT_ID/search --field "scope=blobs" --field "search=expire_in filename:*.yml"
|
||||
|
|
@ -798,11 +798,11 @@ export GL_PROJECT_ID=48057080
|
|||
|
||||
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$GL_PROJECT_ID/registry/repositories" | jq --compact-output '.[]' | jq --compact-output '.id,.location' | jq
|
||||
4435617
|
||||
"registry.gitlab.com/gitlab-de/playground/container-package-gen-group/docker-alpine-generator"
|
||||
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
|
||||
|
||||
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/registry/repositories/4435617?size=true" | jq --compact-output '.id,.location,.size'
|
||||
4435617
|
||||
"registry.gitlab.com/gitlab-de/playground/container-package-gen-group/docker-alpine-generator"
|
||||
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
|
||||
3401613
|
||||
```
|
||||
|
||||
|
|
@ -813,11 +813,11 @@ export GL_PROJECT_ID=48057080
|
|||
|
||||
glab api --method GET projects/$GL_PROJECT_ID/registry/repositories | jq --compact-output '.[]' | jq --compact-output '.id,.location'
|
||||
4435617
|
||||
"registry.gitlab.com/gitlab-de/playground/container-package-gen-group/docker-alpine-generator"
|
||||
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
|
||||
|
||||
glab api --method GET registry/repositories/4435617 --field='size=true' | jq --compact-output '.id,.location,.size'
|
||||
4435617
|
||||
"registry.gitlab.com/gitlab-de/playground/container-package-gen-group/docker-alpine-generator"
|
||||
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
|
||||
3401613
|
||||
|
||||
glab api --method GET projects/$GL_PROJECT_ID/registry/repositories/4435617/tags | jq --compact-output '.[]' | jq --compact-output '.name'
|
||||
|
|
@ -904,7 +904,7 @@ Package registries are available [for projects](../api/packages.md#for-a-project
|
|||
The following example shows fetching packages from a defined project ID using the GitLab CLI. The result set is an array of dictionary items that can be filtered with the `jq` command chain.
|
||||
|
||||
```shell
|
||||
# https://gitlab.com/gitlab-de/playground/container-package-gen-group/generic-package-generator
|
||||
# https://gitlab.com/gitlab-da/playground/container-package-gen-group/generic-package-generator
|
||||
export GL_PROJECT_ID=48377643
|
||||
|
||||
glab api --method GET projects/$GL_PROJECT_ID/packages | jq --compact-output '.[]' | jq --compact-output '.id,.name,.package_type'
|
||||
|
|
@ -1060,7 +1060,7 @@ Create a test project to generate fake artifact blobs using CI/CD job matrix bui
|
|||
|
||||
```yaml
|
||||
include:
|
||||
- remote: https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
|
||||
- remote: https://gitlab.com/gitlab-da/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
|
||||
```
|
||||
|
||||
1. [Configure pipeline schedules](../ci/pipelines/schedules.md#add-a-pipeline-schedule).
|
||||
|
|
@ -1070,7 +1070,7 @@ Alternatively, reduce the 86 MB daily generated MB to different values in the `M
|
|||
|
||||
```yaml
|
||||
include:
|
||||
- remote: https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
|
||||
- remote: https://gitlab.com/gitlab-da/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
|
||||
|
||||
generator:
|
||||
parallel:
|
||||
|
|
@ -1079,7 +1079,7 @@ generator:
|
|||
|
||||
```
|
||||
|
||||
For more information, see the [Job Artifact Generator README](https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator), with an [example group](https://gitlab.com/gitlab-de/playground/artifact-gen-group).
|
||||
For more information, see the [Job Artifact Generator README](https://gitlab.com/gitlab-da/use-cases/efficiency/job-artifact-generator), with an [example group](https://gitlab.com/gitlab-da/playground/artifact-gen-group).
|
||||
|
||||
### Generate job artifacts with expiry
|
||||
|
||||
|
|
@ -1089,7 +1089,7 @@ The project CI/CD configuration specifies job definitions in:
|
|||
- The `artifacts:expire_in` setting.
|
||||
- Project files and templates.
|
||||
|
||||
To test the analysis scripts, the [`gen-job-artifacts-expiry-included-jobs`](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) project provides an example configuration.
|
||||
To test the analysis scripts, the [`gen-job-artifacts-expiry-included-jobs`](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) project provides an example configuration.
|
||||
|
||||
```yaml
|
||||
# .gitlab-ci.yml
|
||||
|
|
@ -1150,7 +1150,7 @@ included-job30:
|
|||
|
||||
### Generate container images
|
||||
|
||||
The example group [`container-package-gen-group`](https://gitlab.com/gitlab-de/playground/container-package-gen-group) provides projects that:
|
||||
The example group [`container-package-gen-group`](https://gitlab.com/gitlab-da/playground/container-package-gen-group) provides projects that:
|
||||
|
||||
- Use a base image in Dockerfile to build a new image.
|
||||
- Include the `Docker.gitlab-ci.yml` template to build images on GitLab.com SaaS.
|
||||
|
|
@ -1158,12 +1158,12 @@ The example group [`container-package-gen-group`](https://gitlab.com/gitlab-de/p
|
|||
|
||||
Example projects available to fork:
|
||||
|
||||
- [`docker-alpine-generator`](https://gitlab.com/gitlab-de/playground/container-package-gen-group/docker-alpine-generator)
|
||||
- [`docker-python-generator`](https://gitlab.com/gitlab-de/playground/container-package-gen-group/docker-python-generator)
|
||||
- [`docker-alpine-generator`](https://gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator)
|
||||
- [`docker-python-generator`](https://gitlab.com/gitlab-da/playground/container-package-gen-group/docker-python-generator)
|
||||
|
||||
### Generate generic packages
|
||||
|
||||
The example project [`generic-package-generator`](https://gitlab.com/gitlab-de/playground/container-package-gen-group/generic-package-generator) provides projects that:
|
||||
The example project [`generic-package-generator`](https://gitlab.com/gitlab-da/playground/container-package-gen-group/generic-package-generator) provides projects that:
|
||||
|
||||
- Generate a random text blob, and create a tarball with the current Unix timestamp as release version.
|
||||
- Upload the tarball into the generic package registry, using the Unix timestamp as release version.
|
||||
|
|
@ -1200,4 +1200,4 @@ Use the following projects to test storage usage with [cost factors for forks](u
|
|||
The following resources are not officially supported. Ensure to test scripts and tutorials before running destructive cleanup commands that may not be reverted.
|
||||
|
||||
- Forum topic: [Storage management automation resources](https://forum.gitlab.com/t/storage-management-automation-resources/91184)
|
||||
- Script: [GitLab Storage Analyzer](https://gitlab.com/gitlab-de/use-cases/gitlab-api/gitlab-storage-analyzer), unofficial project by the [GitLab Developer Evangelism team](https://gitlab.com/gitlab-de/). You find similar code examples in this documentation how-to here.
|
||||
- Script: [GitLab Storage Analyzer](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-storage-analyzer), unofficial project by the [GitLab Developer Evangelism team](https://gitlab.com/gitlab-da/). You find similar code examples in this documentation how-to here.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module Banzai
|
|||
end
|
||||
|
||||
def data_attributes_for(text, parent, object, **data)
|
||||
additional_attributes = { iid: object.iid }
|
||||
additional_attributes = { iid: object.iid, namespace_path: parent.full_path }
|
||||
if parent.is_a?(Namespaces::ProjectNamespace) || parent.is_a?(Project)
|
||||
additional_attributes[:project_path] = parent.full_path
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1617,6 +1617,9 @@ msgstr ""
|
|||
msgid "+ %{numberOfHiddenAssignees} more"
|
||||
msgstr ""
|
||||
|
||||
msgid "+ %{n} more deployments"
|
||||
msgstr ""
|
||||
|
||||
msgid "+%d more"
|
||||
msgid_plural "+%d more"
|
||||
msgstr[0] ""
|
||||
|
|
@ -37689,6 +37692,21 @@ msgstr ""
|
|||
msgid "PagesDomain|Certificate Key is too long. (Max %d bytes)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pages|Deployments"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pages|Environment deployments"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pages|Include stopped deployments"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pages|No deployments yet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pages|Some Pages deployments could not be loaded. Try reloading the page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pagination|First"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40413,7 +40431,7 @@ msgstr ""
|
|||
msgid "Profiles|Don't display activity-related personal information on your profile."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Don't display activity-related personal information on your profile. %{private_profile_help_link_start}Learn more%{private_profile_help_link_end}."
|
||||
msgid "Profiles|Don't display activity-related personal information on your profile. %{private_profile_help_link_start}What information is hidden?%{private_profile_help_link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Edit Profile"
|
||||
|
|
@ -40599,10 +40617,10 @@ msgstr ""
|
|||
msgid "Profiles|The maximum file size allowed is 200 KiB."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|This email will be displayed on your public profile."
|
||||
msgid "Profiles|This email is used for web-based operations, such as edits and merges. %{commit_email_link_start}What is a private commit email?%{commit_email_link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}."
|
||||
msgid "Profiles|This email will be displayed on your public profile."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
|
||||
|
|
@ -40683,7 +40701,7 @@ msgstr ""
|
|||
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Your Discord user ID. %{external_accounts_link_start}Learn more%{external_accounts_link_end}."
|
||||
msgid "Profiles|Your Discord user ID."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Your LinkedIn profile name from linkedin.com/in/profilename"
|
||||
|
|
@ -59294,6 +59312,9 @@ msgstr ""
|
|||
msgid "What happens during repository cleanup?"
|
||||
msgstr ""
|
||||
|
||||
msgid "What information is hidden?"
|
||||
msgstr ""
|
||||
|
||||
msgid "What is GitLab Runner?"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -575,17 +575,9 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
|
|||
|
||||
within_testid 'sidebar-labels' do
|
||||
click_button _('Create project label')
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
page.within '.js-labels-create' do
|
||||
find_by_testid('label-title-input').fill_in with: 'test label'
|
||||
fill_in _('Name new label'), with: 'test label'
|
||||
first('.suggest-colors-dropdown a').click
|
||||
|
||||
click_button 'Create'
|
||||
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
page.within '.js-labels-list' do
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ describe('Job Log Line', () => {
|
|||
it('renders a link with corresponding styles', () => {
|
||||
createComponent(mockProps({ text: httpsUrl }));
|
||||
|
||||
expect(findLink().classes()).toEqual(['!gl-text-inherit', 'gl-text-decoration-underline']);
|
||||
expect(findLink().classes()).toEqual(['!gl-text-inherit', 'gl-underline']);
|
||||
});
|
||||
|
||||
it('renders links with queries, surrounded by questions marks', () => {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,39 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlModal } from '@gitlab/ui';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { createAlert } from '~/alert';
|
||||
import Api from '~/api';
|
||||
import PipelinesTable from '~/ci/common/pipelines_table.vue';
|
||||
import PipelinesTableWrapper from '~/ci/merge_requests/components/pipelines_table_wrapper.vue';
|
||||
import { MR_PIPELINE_TYPE_DETACHED } from '~/ci/merge_requests/constants';
|
||||
import getMergeRequestsPipelines from '~/ci/merge_requests/graphql/queries/get_merge_request_pipelines.query.graphql';
|
||||
|
||||
import cancelPipelineMutation from '~/ci/pipeline_details/graphql/mutations/cancel_pipeline.mutation.graphql';
|
||||
import retryPipelineMutation from '~/ci/pipeline_details/graphql/mutations/retry_pipeline.mutation.graphql';
|
||||
import {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_UNAUTHORIZED,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { generateMRPipelinesResponse } from '../mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
||||
const $toast = {
|
||||
show: jest.fn(),
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
let mergeRequestPipelinesRequest;
|
||||
let cancelPipelineMutationRequest;
|
||||
let retryPipelineMutationRequest;
|
||||
let apolloMock;
|
||||
const showMock = jest.fn();
|
||||
|
||||
const defaultProvide = {
|
||||
graphqlPath: '/api/graphql/',
|
||||
|
|
@ -34,7 +50,11 @@ const defaultProps = {
|
|||
};
|
||||
|
||||
const createComponent = ({ mountFn = shallowMountExtended, props = {} } = {}) => {
|
||||
const handlers = [[getMergeRequestsPipelines, mergeRequestPipelinesRequest]];
|
||||
const handlers = [
|
||||
[getMergeRequestsPipelines, mergeRequestPipelinesRequest],
|
||||
[cancelPipelineMutation, cancelPipelineMutationRequest],
|
||||
[retryPipelineMutation, retryPipelineMutationRequest],
|
||||
];
|
||||
|
||||
apolloMock = createMockApollo(handlers);
|
||||
|
||||
|
|
@ -47,6 +67,15 @@ const createComponent = ({ mountFn = shallowMountExtended, props = {} } = {}) =>
|
|||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
mocks: {
|
||||
$toast,
|
||||
},
|
||||
stubs: {
|
||||
GlModal: stubComponent(GlModal, {
|
||||
template: '<div />',
|
||||
methods: { show: showMock },
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
return waitForPromises();
|
||||
|
|
@ -55,6 +84,7 @@ const createComponent = ({ mountFn = shallowMountExtended, props = {} } = {}) =>
|
|||
const findEmptyState = () => wrapper.findByTestId('pipeline-empty-state');
|
||||
const findErrorEmptyState = () => wrapper.findByTestId('pipeline-error-empty-state');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findModal = () => wrapper.findComponent(GlModal);
|
||||
const findMrPipelinesDocsLink = () => wrapper.findByTestId('mr-pipelines-docs-link');
|
||||
const findPipelinesList = () => wrapper.findComponent(PipelinesTable);
|
||||
const findRunPipelineBtn = () => wrapper.findByTestId('run_pipeline_button');
|
||||
|
|
@ -65,6 +95,12 @@ const findUserPermissionsDocsLink = () => wrapper.findByTestId('user-permissions
|
|||
beforeEach(() => {
|
||||
mergeRequestPipelinesRequest = jest.fn();
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(generateMRPipelinesResponse({ count: 1 }));
|
||||
|
||||
cancelPipelineMutationRequest = jest.fn();
|
||||
cancelPipelineMutationRequest.mockResolvedValue({ data: { pipelineCancel: { errors: [] } } });
|
||||
|
||||
retryPipelineMutationRequest = jest.fn();
|
||||
retryPipelineMutationRequest.mockResolvedValue({ data: { pipelineRetry: { errors: [] } } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -163,23 +199,200 @@ describe('PipelinesTableWrapper component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when latest pipeline has detached flag', () => {
|
||||
beforeEach(async () => {
|
||||
const response = generateMRPipelinesResponse({
|
||||
mergeRequestEventType: MR_PIPELINE_TYPE_DETACHED,
|
||||
});
|
||||
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(response);
|
||||
|
||||
await createComponent({ mountFn: mountExtended });
|
||||
});
|
||||
|
||||
it('renders the run pipeline button', () => {
|
||||
expect(findRunPipelineBtn().exists()).toBe(true);
|
||||
expect(findRunPipelineBtnMobile().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('run pipeline button', () => {
|
||||
describe('when latest pipeline has detached flag', () => {
|
||||
describe('on click', () => {
|
||||
beforeEach(() => {
|
||||
const response = generateMRPipelinesResponse({
|
||||
mergeRequestEventType: MR_PIPELINE_TYPE_DETACHED,
|
||||
});
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(response);
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(Api, 'postMergeRequestPipeline').mockResolvedValue();
|
||||
await createComponent({ mountFn: mountExtended });
|
||||
await findRunPipelineBtn().trigger('click');
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays a toast message during pipeline creation', () => {
|
||||
expect($toast.show).toHaveBeenCalledWith('Creating pipeline.');
|
||||
});
|
||||
|
||||
it('on desktop, shows a loading button', async () => {
|
||||
await findRunPipelineBtn().trigger('click');
|
||||
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(true);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(false);
|
||||
});
|
||||
|
||||
it('on mobile, shows a loading button', async () => {
|
||||
await findRunPipelineBtnMobile().trigger('click');
|
||||
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(true);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findRunPipelineBtn().props('disabled')).toBe(false);
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure', () => {
|
||||
const permissionsMsg = 'You do not have permission to run a pipeline on this branch.';
|
||||
const defaultMsg =
|
||||
'An error occurred while trying to run a new pipeline for this merge request.';
|
||||
|
||||
it.each`
|
||||
status | message
|
||||
${HTTP_STATUS_BAD_REQUEST} | ${defaultMsg}
|
||||
${HTTP_STATUS_UNAUTHORIZED} | ${permissionsMsg}
|
||||
${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${defaultMsg}
|
||||
`('displays permissions error message', async ({ status, message }) => {
|
||||
const response = { response: { status } };
|
||||
jest.spyOn(Api, 'postMergeRequestPipeline').mockRejectedValue(response);
|
||||
await createComponent({ mountFn: mountExtended });
|
||||
|
||||
await findRunPipelineBtn().trigger('click');
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message,
|
||||
primaryButton: {
|
||||
text: 'Learn more',
|
||||
link: '/help/ci/pipelines/merge_request_pipelines.md',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on click for fork merge request', () => {
|
||||
beforeEach(async () => {
|
||||
const response = generateMRPipelinesResponse({
|
||||
mergeRequestEventType: MR_PIPELINE_TYPE_DETACHED,
|
||||
});
|
||||
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(response);
|
||||
await createComponent({
|
||||
props: {
|
||||
canCreatePipelineInTargetProject: true,
|
||||
sourceProjectFullPath: 'test/parent-project',
|
||||
targetProjectFullPath: 'test/fork-project',
|
||||
},
|
||||
});
|
||||
|
||||
createComponent({ mountFn: mountExtended });
|
||||
|
||||
await waitForPromises();
|
||||
jest.spyOn(Api, 'postMergeRequestPipeline').mockResolvedValue();
|
||||
});
|
||||
|
||||
it('renders the run pipeline button', () => {
|
||||
expect(findRunPipelineBtn().exists()).toBe(true);
|
||||
expect(findRunPipelineBtnMobile().exists()).toBe(true);
|
||||
it('on desktop, shows a security warning modal', async () => {
|
||||
await findRunPipelineBtn().trigger('click');
|
||||
|
||||
expect(findModal()).not.toBeNull();
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(false);
|
||||
});
|
||||
|
||||
it('on mobile, shows a security warning modal', async () => {
|
||||
await findRunPipelineBtnMobile().trigger('click');
|
||||
|
||||
expect(findModal()).not.toBeNull();
|
||||
expect(findRunPipelineBtn().props('loading')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no pipelines were created on a forked merge request', () => {
|
||||
beforeEach(async () => {
|
||||
const response = generateMRPipelinesResponse({ count: 0 });
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(response);
|
||||
|
||||
await createComponent({
|
||||
mountFn: mountExtended,
|
||||
props: {
|
||||
canCreatePipelineInTargetProject: true,
|
||||
sourceProjectFullPath: 'test/parent-project',
|
||||
targetProjectFullPath: 'test/fork-project',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should show security modal from empty state run pipeline button', async () => {
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
expect(findModal().exists()).toBe(true);
|
||||
|
||||
await findRunPipelineBtn().trigger('click');
|
||||
|
||||
expect(showMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('events', () => {
|
||||
const response = generateMRPipelinesResponse();
|
||||
const pipeline = response.data.project.mergeRequest.pipelines.nodes[0];
|
||||
|
||||
beforeEach(async () => {
|
||||
mergeRequestPipelinesRequest.mockResolvedValue(response);
|
||||
await createComponent();
|
||||
});
|
||||
|
||||
describe('When cancelling a pipeline', () => {
|
||||
it('execute the cancel graphql mutation', async () => {
|
||||
expect(cancelPipelineMutationRequest.mock.calls).toHaveLength(0);
|
||||
|
||||
findPipelinesList().vm.$emit('cancel-pipeline', pipeline);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(cancelPipelineMutationRequest.mock.calls[0]).toEqual([
|
||||
{ id: 'gid://gitlab/Ci::Pipeline/0' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When retrying a pipeline', () => {
|
||||
it('sends the retry action graphql mutation', async () => {
|
||||
expect(retryPipelineMutationRequest.mock.calls).toHaveLength(0);
|
||||
|
||||
findPipelinesList().vm.$emit('retry-pipeline', pipeline);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(retryPipelineMutationRequest.mock.calls[0]).toEqual([
|
||||
{ id: 'gid://gitlab/Ci::Pipeline/0' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When refreshing a pipeline', () => {
|
||||
it('calls the apollo query again', async () => {
|
||||
expect(mergeRequestPipelinesRequest.mock.calls).toHaveLength(1);
|
||||
|
||||
findPipelinesList().vm.$emit('refresh-pipelines-table');
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(mergeRequestPipelinesRequest.mock.calls).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ const createMergeRequestPipelines = ({ mergeRequestEventType = 'MERGE_TRAIN', co
|
|||
failureReason: null,
|
||||
yamlErrors: false,
|
||||
latest: true,
|
||||
retryable: true,
|
||||
cancelable: false,
|
||||
commit: {
|
||||
id: 'gid://gitlab/Ci::Commit/1',
|
||||
title:
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ describe('CompareVersions', () => {
|
|||
|
||||
expect(treeListBtn.exists()).toBe(true);
|
||||
expect(treeListBtn.attributes('aria-label')).toBe('Hide file browser');
|
||||
expect(treeListBtn.props('icon')).toBe('file-tree');
|
||||
expect(treeListBtn.props('icon')).toBe('sidebar');
|
||||
});
|
||||
|
||||
it('should render comparison dropdowns with correct values', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import PagesDeployments from '~/gitlab_pages/components/deployments.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import getProjectPagesDeploymentsQuery from '~/gitlab_pages/queries/get_project_pages_deployments.graphql';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
describe('PagesDeployments', () => {
|
||||
Vue.use(VueApollo);
|
||||
let wrapper;
|
||||
|
||||
const getProjectPagesDeploymentsQueryHandler = jest
|
||||
.fn()
|
||||
.mockImplementation(({ versioned, after }) => {
|
||||
return {
|
||||
data: {
|
||||
project: {
|
||||
id: 'gid://gitlab/Project/1',
|
||||
pagesDeployments: {
|
||||
__typename: 'PagesDeploymentConnection',
|
||||
count: 14,
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
startCursor:
|
||||
'eyJjcmVhdGVkX2F0IjoiMjAyNC0wNS0yMiAxMzozNzoyMi40MTk4MzcwMDAgKzAwMDAiLCJpZCI6IjQwIn0',
|
||||
endCursor:
|
||||
'eyJjcmVhdGVkX2F0IjoiMjAyNC0wNS0yMiAxMzozNzoyMi40MTk4MzcwMDAgKzAwMDAiLCJpZCI6IjQwIn0',
|
||||
hasNextPage: !after, // This mimics this result being a second page
|
||||
hasPreviousPage: Boolean(after),
|
||||
},
|
||||
nodes: Array(after ? 4 : 10)
|
||||
.fill(null)
|
||||
.map((_, i) => ({
|
||||
__typename: 'PagesDeployment',
|
||||
id: `gid://gitlab/PagesDeployment/${!after ? i + 1 : i + 11}`,
|
||||
active: true,
|
||||
rootDirectory: 'public',
|
||||
ciBuildId: '499',
|
||||
createdAt: '2024-05-22T13:37:22Z',
|
||||
deletedAt: null,
|
||||
fileCount: 3,
|
||||
pathPrefix: versioned ? '_stg' : '',
|
||||
size: 1082,
|
||||
updatedAt: '2024-05-23T11:48:34Z',
|
||||
url: 'http://abc.pages.io/',
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const findAllPrimaryDeployments = () => wrapper.findAllByTestId('primary-deployment');
|
||||
const findAllEnvironmentDeployments = () => wrapper.findAllByTestId('environment-deployment');
|
||||
const findPrimaryDeploymentsLoadMoreComponent = () =>
|
||||
wrapper.findByTestId('load-more-primary-deployments');
|
||||
const findEnvironmentDeploymentsLoadMoreComponent = () =>
|
||||
wrapper.findByTestId('load-more-environment-deployments');
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(PagesDeployments, {
|
||||
apolloProvider: createMockApollo([
|
||||
[getProjectPagesDeploymentsQuery, getProjectPagesDeploymentsQueryHandler],
|
||||
]),
|
||||
provide: {
|
||||
projectFullPath: 'my-group/my-project',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
describe('default behaviour', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('calls the query 2 times', () => {
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('calls the primary deployments query', () => {
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
active: true,
|
||||
fullPath: 'my-group/my-project',
|
||||
versioned: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('calls the environments deployments query', () => {
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
active: true,
|
||||
fullPath: 'my-group/my-project',
|
||||
versioned: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders a list for all primary deployments', () => {
|
||||
expect(findAllPrimaryDeployments().length).toBe(10);
|
||||
});
|
||||
|
||||
it('renders a list for all environment deployments', () => {
|
||||
expect(findAllEnvironmentDeployments().length).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user loads more deployments', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('does include the "load more" component for primary deployments', () => {
|
||||
expect(findPrimaryDeploymentsLoadMoreComponent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does include the "load more" component for environment deployments', () => {
|
||||
expect(findEnvironmentDeploymentsLoadMoreComponent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('fetches more primary deployments', async () => {
|
||||
findPrimaryDeploymentsLoadMoreComponent().vm.$emit('load-more');
|
||||
await nextTick();
|
||||
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
versioned: false,
|
||||
after: expect.any(String),
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findPrimaryDeploymentsLoadMoreComponent().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('fetches more environment deployments', async () => {
|
||||
findEnvironmentDeploymentsLoadMoreComponent().vm.$emit('load-more');
|
||||
await nextTick();
|
||||
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
versioned: true,
|
||||
after: expect.any(String),
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findEnvironmentDeploymentsLoadMoreComponent().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the "Include stopped deployments" toggle is switched on', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
wrapper.findByTestId('show-inactive-toggle').vm.$emit('change', true);
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('fetches the primaryDeployments with the "active" filter set to undefined', () => {
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
versioned: false,
|
||||
active: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('fetches the environmentDeployments with the "active" filter set to undefined', () => {
|
||||
expect(getProjectPagesDeploymentsQueryHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
versioned: true,
|
||||
active: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
type | testid
|
||||
${'primary'} | ${'primary-deployment'}
|
||||
${'environment'} | ${'environment-deployment'}
|
||||
`('if a $type PagesDeployment child emits an error', ({ testid }) => {
|
||||
const errorMessage = 'Foo';
|
||||
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
wrapper
|
||||
.findByTestId(testid)
|
||||
.vm.$emit('error', { id: 'gid://gitlab/PagesDeployment/1', message: errorMessage });
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
it('shows an alert with this message', () => {
|
||||
expect(wrapper.findByTestId('alert').text()).toBe(errorMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if there are no deployments yet', () => {
|
||||
beforeEach(async () => {
|
||||
getProjectPagesDeploymentsQueryHandler.mockImplementation(() => ({
|
||||
data: {
|
||||
project: {
|
||||
id: 'gid://gitlab/Project/1',
|
||||
pagesDeployments: {
|
||||
count: 0,
|
||||
pageInfo: {
|
||||
startCursor: null,
|
||||
endCursor: null,
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
},
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays an empty state text', () => {
|
||||
expect(wrapper.text()).toContain('No deployments yet');
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading state', () => {
|
||||
beforeEach(() => {
|
||||
getProjectPagesDeploymentsQueryHandler.mockImplementation(() => Promise);
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('displays the loading icon', () => {
|
||||
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import PagesEdit from '~/gitlab_pages/components/edit.vue';
|
||||
import PagesDeployments from '~/gitlab_pages/components/deployments.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
describe('PagesEdit', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(PagesEdit);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('mounts the PagesDeployments component', () => {
|
||||
expect(wrapper.findComponent(PagesDeployments).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,25 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import PagesEdit from '~/gitlab_pages/components/pages_edit.vue';
|
||||
import PagesEdit from '~/gitlab_pages/components/edit.vue';
|
||||
import PagesDeployments from '~/gitlab_pages/components/deployments.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
describe('PagesEdit', () => {
|
||||
let wrapper;
|
||||
const props = {};
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(PagesEdit, {
|
||||
propsData: props,
|
||||
});
|
||||
wrapper = shallowMountExtended(PagesEdit);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('shows the page header', () => {
|
||||
expect(wrapper.find('h1').text()).toBe('Pages');
|
||||
it('mounts the PagesDeployments component', () => {
|
||||
expect(wrapper.findComponent(PagesDeployments).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
export const primaryDeployment = {
|
||||
id: 'gid://gitlab/PagesDeployment/1',
|
||||
active: true,
|
||||
pathPrefix: null,
|
||||
url: 'https://example.com',
|
||||
createdAt: '2023-04-01T12:00:00Z',
|
||||
ciBuildId: 123,
|
||||
rootDirectory: 'public',
|
||||
fileCount: 100,
|
||||
size: 1024,
|
||||
updatedAt: '2023-04-02T12:05:00Z',
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
export const environmentDeployment = {
|
||||
...primaryDeployment,
|
||||
pathPrefix: '_stg',
|
||||
};
|
||||
|
||||
export const deleteDeploymentResult = {
|
||||
deletePagesDeployment: {
|
||||
errors: [],
|
||||
pagesDeployment: {
|
||||
id: 1,
|
||||
active: false,
|
||||
deletedAt: '2023-04-02T12:10:00Z',
|
||||
updatedAt: '2023-04-02T12:10:00Z',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const restoreDeploymentResult = {
|
||||
restorePagesDeployment: {
|
||||
errors: [],
|
||||
pagesDeployment: {
|
||||
id: 1,
|
||||
active: true,
|
||||
deletedAt: null,
|
||||
updatedAt: '2023-04-02T12:15:00Z',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,65 +1,121 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import mockData from 'test_fixtures/issues/related_merge_requests.json';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import RelatedMergeRequests from '~/issues/related_merge_requests/components/related_merge_requests.vue';
|
||||
import createStore from '~/issues/related_merge_requests/store/index';
|
||||
import relatedMergeRequestsQuery from '~/issues/related_merge_requests/queries/related_merge_requests.query.graphql';
|
||||
import RelatedIssuableItem from '~/issuable/components/related_issuable_item.vue';
|
||||
|
||||
const API_ENDPOINT = '/api/v4/projects/2/issues/33/related_merge_requests';
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const mockData = {
|
||||
data: {
|
||||
project: {
|
||||
id: 1,
|
||||
issue: {
|
||||
id: 1,
|
||||
relatedMergeRequests: {
|
||||
count: 2,
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: 'eyJpZCI6IjEwNjEwIn0',
|
||||
endCursor: 'eyJpZCI6IjM0In0',
|
||||
__typename: 'PageInfo',
|
||||
},
|
||||
nodes: [
|
||||
{
|
||||
id: 'gid://gitlab/MergeRequest/42',
|
||||
reference: '!18',
|
||||
state: 'merged',
|
||||
title: ':star: hello world',
|
||||
createdAt: '2023-03-03T12:25:40Z',
|
||||
mergedAt: '2023-04-27T12:01:04Z',
|
||||
webUrl: 'http://gdk.test:3000/gitlab-org/gitlab-test/-/merge_requests/18',
|
||||
milestone: {
|
||||
expired: false,
|
||||
id: 'gid://gitlab/Milestone/5',
|
||||
state: 'active',
|
||||
title: 'v4.0',
|
||||
__typename: 'Milestone',
|
||||
},
|
||||
assignees: {
|
||||
nodes: [],
|
||||
__typename: 'MergeRequestAssigneeConnection',
|
||||
},
|
||||
headPipeline: null,
|
||||
__typename: 'MergeRequest',
|
||||
},
|
||||
{
|
||||
id: 'gid://gitlab/MergeRequest/34',
|
||||
reference: '!10',
|
||||
state: 'opened',
|
||||
title: 'Draft: :parrot: Title with custom emoji',
|
||||
createdAt: '2022-09-23T14:33:32Z',
|
||||
mergedAt: null,
|
||||
webUrl: 'http://gdk.test:3000/gitlab-org/gitlab-test/-/merge_requests/10',
|
||||
milestone: {
|
||||
expired: false,
|
||||
id: 'gid://gitlab/Milestone/5',
|
||||
state: 'active',
|
||||
title: 'v4.0',
|
||||
__typename: 'Milestone',
|
||||
},
|
||||
assignees: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'gid://gitlab/User/1',
|
||||
avatarUrl:
|
||||
'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80\u0026d=identicon',
|
||||
name: 'Administrator',
|
||||
username: 'root',
|
||||
webUrl: 'http://gdk.test:3000/root',
|
||||
webPath: '/root',
|
||||
__typename: 'MergeRequestAssignee',
|
||||
},
|
||||
],
|
||||
__typename: 'MergeRequestAssigneeConnection',
|
||||
},
|
||||
headPipeline: {
|
||||
id: 1,
|
||||
detailedStatus: {
|
||||
id: 'success-66-66',
|
||||
icon: 'status_success',
|
||||
text: 'Passed',
|
||||
detailsPath: '/root/test-ci/-/pipelines/66',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
__typename: 'MergeRequest',
|
||||
},
|
||||
],
|
||||
__typename: 'MergeRequestConnection',
|
||||
},
|
||||
__typename: 'Issue',
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('RelatedMergeRequests', () => {
|
||||
let wrapper;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// put the fixture in DOM as the component expects
|
||||
document.body.innerHTML = `<div id="js-issuable-app"></div>`;
|
||||
document.getElementById('js-issuable-app').dataset.initial = JSON.stringify(mockData);
|
||||
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(HTTP_STATUS_OK, mockData, { 'x-total': 2 });
|
||||
|
||||
beforeEach(async () => {
|
||||
const apolloProvider = createMockApollo([
|
||||
[relatedMergeRequestsQuery, jest.fn().mockResolvedValue(mockData)],
|
||||
]);
|
||||
wrapper = shallowMount(RelatedMergeRequests, {
|
||||
store: createStore(),
|
||||
apolloProvider,
|
||||
propsData: {
|
||||
endpoint: API_ENDPOINT,
|
||||
projectNamespace: 'gitlab-org',
|
||||
projectPath: 'gitlab-ce',
|
||||
iid: '1',
|
||||
},
|
||||
});
|
||||
|
||||
return axios.waitForAll();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
describe('getAssignees', () => {
|
||||
const assignees = [{ name: 'foo' }, { name: 'bar' }];
|
||||
|
||||
describe('when there is assignees array', () => {
|
||||
it('should return assignees array', () => {
|
||||
const mr = { assignees };
|
||||
|
||||
expect(wrapper.vm.getAssignees(mr)).toEqual(assignees);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an array with single assignee', () => {
|
||||
const mr = { assignee: assignees[0] };
|
||||
|
||||
expect(wrapper.vm.getAssignees(mr)).toEqual([assignees[0]]);
|
||||
});
|
||||
|
||||
it('should return empty array when assignee is not set', () => {
|
||||
expect(wrapper.vm.getAssignees({})).toEqual([]);
|
||||
expect(wrapper.vm.getAssignees({ assignee: null })).toEqual([]);
|
||||
});
|
||||
});
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
|
|
@ -68,17 +124,16 @@ describe('RelatedMergeRequests', () => {
|
|||
expect(wrapper.findAllComponents(RelatedIssuableItem)).toHaveLength(2);
|
||||
|
||||
const props = wrapper.findAllComponents(RelatedIssuableItem).at(1).props();
|
||||
const data = mockData[1];
|
||||
const data = mockData.data.project.issue.relatedMergeRequests.nodes[1];
|
||||
|
||||
expect(props.idKey).toEqual(data.id);
|
||||
expect(props.idKey).toEqual(34);
|
||||
expect(props.pathIdSeparator).toEqual('!');
|
||||
expect(props.pipelineStatus).toBe(data.head_pipeline.detailed_status);
|
||||
expect(props.assignees).toEqual([data.assignee]);
|
||||
expect(props.assignees).toEqual(data.assignees.nodes);
|
||||
expect(props.isMergeRequest).toBe(true);
|
||||
expect(props.confidential).toEqual(false);
|
||||
expect(props.title).toEqual(data.title);
|
||||
expect(props.state).toEqual(data.state);
|
||||
expect(props.createdAt).toEqual(data.created_at);
|
||||
expect(props.createdAt).toEqual(data.createdAt);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import * as actions from '~/issues/related_merge_requests/store/actions';
|
||||
import * as types from '~/issues/related_merge_requests/store/mutation_types';
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
||||
describe('RelatedMergeRequest store actions', () => {
|
||||
let state;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = {
|
||||
apiEndpoint: '/api/related_merge_requests',
|
||||
};
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('setInitialState', () => {
|
||||
it('commits types.SET_INITIAL_STATE with given props', () => {
|
||||
const props = { a: 1, b: 2 };
|
||||
|
||||
return testAction(
|
||||
actions.setInitialState,
|
||||
props,
|
||||
{},
|
||||
[{ type: types.SET_INITIAL_STATE, payload: props }],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestData', () => {
|
||||
it('commits types.REQUEST_DATA', () => {
|
||||
return testAction(actions.requestData, null, {}, [{ type: types.REQUEST_DATA }], []);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveDataSuccess', () => {
|
||||
it('commits types.RECEIVE_DATA_SUCCESS with data', () => {
|
||||
const data = { a: 1, b: 2 };
|
||||
|
||||
return testAction(
|
||||
actions.receiveDataSuccess,
|
||||
data,
|
||||
{},
|
||||
[{ type: types.RECEIVE_DATA_SUCCESS, payload: data }],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveDataError', () => {
|
||||
it('commits types.RECEIVE_DATA_ERROR', () => {
|
||||
return testAction(
|
||||
actions.receiveDataError,
|
||||
null,
|
||||
{},
|
||||
[{ type: types.RECEIVE_DATA_ERROR }],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchMergeRequests', () => {
|
||||
describe('for a successful request', () => {
|
||||
it('should dispatch success action', () => {
|
||||
const data = { a: 1 };
|
||||
mock
|
||||
.onGet(`${state.apiEndpoint}?per_page=100`)
|
||||
.replyOnce(HTTP_STATUS_OK, data, { 'x-total': 2 });
|
||||
|
||||
return testAction(
|
||||
actions.fetchMergeRequests,
|
||||
null,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'requestData' }, { type: 'receiveDataSuccess', payload: { data, total: 2 } }],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a failing request', () => {
|
||||
it('should dispatch error action', async () => {
|
||||
mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(HTTP_STATUS_BAD_REQUEST);
|
||||
|
||||
await testAction(
|
||||
actions.fetchMergeRequests,
|
||||
null,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'requestData' }, { type: 'receiveDataError' }],
|
||||
);
|
||||
expect(createAlert).toHaveBeenCalledTimes(1);
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: expect.stringMatching('Something went wrong'),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import * as types from '~/issues/related_merge_requests/store/mutation_types';
|
||||
import mutations from '~/issues/related_merge_requests/store/mutations';
|
||||
|
||||
describe('RelatedMergeRequests Store Mutations', () => {
|
||||
describe('SET_INITIAL_STATE', () => {
|
||||
it('should set initial state according to given data', () => {
|
||||
const apiEndpoint = '/api';
|
||||
const state = {};
|
||||
|
||||
mutations[types.SET_INITIAL_STATE](state, { apiEndpoint });
|
||||
|
||||
expect(state.apiEndpoint).toEqual(apiEndpoint);
|
||||
});
|
||||
});
|
||||
|
||||
describe('REQUEST_DATA', () => {
|
||||
it('should set loading flag', () => {
|
||||
const state = {};
|
||||
|
||||
mutations[types.REQUEST_DATA](state);
|
||||
|
||||
expect(state.isFetchingMergeRequests).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_DATA_SUCCESS', () => {
|
||||
it('should set loading flag and data', () => {
|
||||
const state = {};
|
||||
const mrs = [1, 2, 3];
|
||||
|
||||
mutations[types.RECEIVE_DATA_SUCCESS](state, { data: mrs, total: mrs.length });
|
||||
|
||||
expect(state.isFetchingMergeRequests).toEqual(false);
|
||||
expect(state.mergeRequests).toEqual(mrs);
|
||||
expect(state.totalCount).toEqual(mrs.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_DATA_ERROR', () => {
|
||||
it('should set loading and error flags', () => {
|
||||
const state = {};
|
||||
|
||||
mutations[types.RECEIVE_DATA_ERROR](state);
|
||||
|
||||
expect(state.isFetchingMergeRequests).toEqual(false);
|
||||
expect(state.hasErrorFetchingMergeRequests).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -23,7 +23,7 @@ exports[`publish_method renders 1`] = `
|
|||
variant="current"
|
||||
/>
|
||||
<gl-link-stub
|
||||
class="gl-mr-2 gl-text-decoration-underline"
|
||||
class="gl-mr-2 gl-underline"
|
||||
data-testid="pipeline-sha"
|
||||
href="/namespace14/project14/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlAlert, GlFormInput, GlLoadingIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
|
@ -7,7 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import { createAlert } from '~/alert';
|
||||
import { workspaceLabelsQueries, workspaceCreateLabelMutation } from '~/sidebar/queries/constants';
|
||||
import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue';
|
||||
import SibebarColorPicker from '~/sidebar/components/sidebar_color_picker.vue';
|
||||
import SidebarColorPicker from '~/sidebar/components/sidebar_color_picker.vue';
|
||||
import { DEFAULT_LABEL_COLOR } from '~/sidebar/components/labels/labels_select_widget/constants';
|
||||
import {
|
||||
mockCreateLabelResponse as createAbuseReportLabelSuccessfulResponse,
|
||||
|
|
@ -50,16 +50,16 @@ const createLabelErrorHandler = jest.fn().mockRejectedValue('Houston, we have a
|
|||
describe('DropdownContentsCreateView', () => {
|
||||
let wrapper;
|
||||
|
||||
const findSibebarColorPicker = () => wrapper.findComponent(SibebarColorPicker);
|
||||
const findSidebarColorPicker = () => wrapper.findComponent(SidebarColorPicker);
|
||||
const findCreateButton = () => wrapper.find('[data-testid="create-button"]');
|
||||
const findCancelButton = () => wrapper.find('[data-testid="cancel-button"]');
|
||||
const findLabelTitleInput = () => wrapper.find('[data-testid="label-title-input"]');
|
||||
const findLabelTitleInput = () => wrapper.findComponent(GlFormInput);
|
||||
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
|
||||
const fillLabelAttributes = () => {
|
||||
findLabelTitleInput().vm.$emit('input', 'Test title');
|
||||
findSibebarColorPicker().vm.$emit('input', '#009966');
|
||||
findSidebarColorPicker().vm.$emit('input', '#009966');
|
||||
};
|
||||
|
||||
const createComponent = ({
|
||||
|
|
@ -97,7 +97,7 @@ describe('DropdownContentsCreateView', () => {
|
|||
|
||||
it('disables a Create button if label title is not set', async () => {
|
||||
createComponent();
|
||||
findSibebarColorPicker().vm.$emit('input', '#fff');
|
||||
findSidebarColorPicker().vm.$emit('input', '#fff');
|
||||
await nextTick();
|
||||
|
||||
expect(findCreateButton().props('disabled')).toBe(true);
|
||||
|
|
@ -106,7 +106,7 @@ describe('DropdownContentsCreateView', () => {
|
|||
it('disables a Create button if color is not set', async () => {
|
||||
createComponent();
|
||||
findLabelTitleInput().vm.$emit('input', 'Test title');
|
||||
findSibebarColorPicker().vm.$emit('input', '');
|
||||
findSidebarColorPicker().vm.$emit('input', '');
|
||||
await nextTick();
|
||||
|
||||
expect(findCreateButton().props('disabled')).toBe(true);
|
||||
|
|
@ -144,17 +144,17 @@ describe('DropdownContentsCreateView', () => {
|
|||
findCreateButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
expect(findCreateButton().props('loading')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not loader spinner after mutation is resolved', async () => {
|
||||
findCreateButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
expect(findCreateButton().props('loading')).toBe(true);
|
||||
await waitForPromises();
|
||||
|
||||
expect(findLoadingIcon().exists()).toBe(false);
|
||||
expect(findCreateButton().props('loading')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ describe('generateText', () => {
|
|||
${'%{small_start}Hello world%{small_end}'} | ${'<span class="gl-font-sm gl-text-gray-700">Hello world</span>'}
|
||||
${'%{strong_start}%{danger_start}Hello world%{danger_end}%{strong_end}'} | ${'<span class="gl-font-bold"><span class="gl-font-bold gl-text-red-500">Hello world</span></span>'}
|
||||
${'%{no_exist_start}Hello world%{no_exist_end}'} | ${'Hello world'}
|
||||
${{ text: 'Hello world', href: 'http://www.example.com' }} | ${'<a class="gl-text-decoration-underline" href="http://www.example.com">Hello world</a>'}
|
||||
${{ prependText: 'Hello', text: 'world', href: 'http://www.example.com' }} | ${'Hello <a class="gl-text-decoration-underline" href="http://www.example.com">world</a>'}
|
||||
${{ text: 'Hello world', href: 'http://www.example.com' }} | ${'<a class="gl-underline" href="http://www.example.com">Hello world</a>'}
|
||||
${{ prependText: 'Hello', text: 'world', href: 'http://www.example.com' }} | ${'Hello <a class="gl-underline" href="http://www.example.com">world</a>'}
|
||||
${['array']} | ${null}
|
||||
`('generates $expectedText from $text', ({ text, expectedText }) => {
|
||||
expect(generateText(text)).toBe(expectedText);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor
|
|||
IssuesHelper
|
||||
end
|
||||
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be_with_reload(:issue) { create(:issue, project: project) }
|
||||
let(:issue_path) { "/#{issue.project.namespace.path}/#{issue.project.path}/-/issues/#{issue.iid}" }
|
||||
let(:issue_url) { "http://#{Gitlab.config.gitlab.host}#{issue_path}" }
|
||||
|
||||
|
|
@ -102,6 +102,14 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor
|
|||
expect(link.attr('data-project')).to eq project.id.to_s
|
||||
end
|
||||
|
||||
it 'includes a data-namespace-path attribute' do
|
||||
doc = reference_filter("Issue #{written_reference}")
|
||||
link = doc.css('a').first
|
||||
|
||||
expect(link).to have_attribute('data-namespace-path')
|
||||
expect(link.attr('data-namespace-path')).to eq(project.full_path)
|
||||
end
|
||||
|
||||
it 'includes a data-issue attribute' do
|
||||
doc = reference_filter("See #{written_reference}")
|
||||
link = doc.css('a').first
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ RSpec.describe Banzai::Filter::References::WorkItemReferenceFilter, feature_cate
|
|||
link = doc.css('a').first
|
||||
|
||||
expect(link.attr('data-project-path')).to eq cross_project.full_path
|
||||
expect(link.attr('data-namespace-path')).to eq cross_project.full_path
|
||||
expect(link.attr('data-iid')).to eq work_item.iid.to_s
|
||||
end
|
||||
end
|
||||
|
|
@ -242,6 +243,7 @@ RSpec.describe Banzai::Filter::References::WorkItemReferenceFilter, feature_cate
|
|||
link = doc.css('a').first
|
||||
|
||||
expect(link.attr('data-project-path')).to eq cross_project.full_path
|
||||
expect(link.attr('data-namespace-path')).to eq cross_project.full_path
|
||||
expect(link.attr('data-iid')).to eq work_item.iid.to_s
|
||||
end
|
||||
end
|
||||
|
|
@ -253,6 +255,14 @@ RSpec.describe Banzai::Filter::References::WorkItemReferenceFilter, feature_cate
|
|||
context 'when work item exists at the group level' do
|
||||
let_it_be(:work_item) { create(:work_item, :group_level, namespace: group) }
|
||||
|
||||
it 'includes data attributes for issuable popover' do
|
||||
doc = reference_filter("See #{work_item_url}", context)
|
||||
link = doc.css('a').first
|
||||
|
||||
expect(link.attr('data-namespace-path')).to eq(group.full_path)
|
||||
expect(link.attr('data-iid')).to eq(work_item.iid.to_s)
|
||||
end
|
||||
|
||||
it 'links to a valid group level work item by URL' do
|
||||
doc = reference_filter("See #{work_item_url}", context)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ RSpec.describe 'Timeline Events', feature_category: :incident_management do
|
|||
"<a href=\"/#{project.full_path}/-/issues/#{incident.iid}\" data-reference-type=\"issue\" " \
|
||||
"data-original=\"##{incident.iid}\" data-link=\"false\" data-link-reference=\"false\" " \
|
||||
"data-issue=\"#{incident.id}\" data-project=\"#{project.id}\" data-iid=\"#{incident.iid}\" " \
|
||||
"data-project-path=\"#{project.full_path}\" " \
|
||||
"data-namespace-path=\"#{project.full_path}\" data-project-path=\"#{project.full_path}\" " \
|
||||
"data-issue-type=\"incident\" data-container=\"body\" data-placement=\"top\" " \
|
||||
"title=\"#{incident.title}\" class=\"gfm gfm-issue\">##{incident.iid}</a>"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2951,7 +2951,6 @@
|
|||
- './ee/spec/workers/security/sync_scan_policies_worker_spec.rb'
|
||||
- './ee/spec/workers/security/track_secure_scans_worker_spec.rb'
|
||||
- './ee/spec/workers/set_user_status_based_on_user_cap_setting_worker_spec.rb'
|
||||
- './ee/spec/workers/store_security_reports_worker_spec.rb'
|
||||
- './ee/spec/workers/sync_seat_link_request_worker_spec.rb'
|
||||
- './ee/spec/workers/sync_seat_link_worker_spec.rb'
|
||||
- './ee/spec/workers/todos_destroyer/confidential_epic_worker_spec.rb'
|
||||
|
|
|
|||
|
|
@ -109,13 +109,10 @@ RSpec.shared_examples 'labels sidebar widget' do
|
|||
it 'shows error message if label title is taken' do
|
||||
page.within(labels_widget) do
|
||||
fill_in 'Name new label', with: development.title
|
||||
page.find('.suggested-colors a', match: :first).click
|
||||
page.find('button', text: 'Create').click
|
||||
wait_for_requests
|
||||
click_link 'Magenta-pink'
|
||||
click_button 'Create'
|
||||
|
||||
page.within('.dropdown-input') do
|
||||
expect(page.find('.gl-alert')).to have_content 'Title'
|
||||
end
|
||||
expect(page).to have_css '.gl-alert', text: 'Title'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ RSpec.describe 'user_settings/profiles/show', feature_category: :user_profile do
|
|||
expect(rendered).to have_field('user_name', with: user.name)
|
||||
expect(rendered).to have_field('user_id', with: user.id)
|
||||
|
||||
expected_link = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits')
|
||||
expected_link = help_page_path('user/profile/index',
|
||||
anchor: 'use-an-automatically-generated-private-commit-email')
|
||||
expected_link_html = "<a href=\"#{expected_link}\" target=\"_blank\" " \
|
||||
"rel=\"noopener noreferrer\">#{_('Learn more')}</a>."
|
||||
"rel=\"noopener noreferrer\">#{s_('Profiles|What is a private commit email?')}</a>"
|
||||
|
||||
expect(rendered.include?(expected_link_html)).to eq(true)
|
||||
end
|
||||
|
||||
|
|
@ -61,7 +63,7 @@ RSpec.describe 'user_settings/profiles/show', feature_category: :user_profile do
|
|||
render
|
||||
|
||||
expect(rendered).to render_template('user_settings/profiles/_private_profile')
|
||||
expect(rendered).to have_link 'Learn more',
|
||||
expect(rendered).to have_link 'What information is hidden?',
|
||||
href: help_page_path('user/profile/index', anchor: 'make-your-user-profile-page-private')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue