Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
31522c5182
commit
c7b2529418
58 changed files with 1770 additions and 692 deletions
|
@ -234,7 +234,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/integration/kroki.md @msedlakjakubowski
|
||||
/doc/administration/integration/mailgun.md @kpaizee
|
||||
/doc/administration/integration/plantuml.md @aqualls
|
||||
/doc/administration/integration/terminal.md @kpaizee
|
||||
/doc/administration/integration/terminal.md @ashrafkhamis
|
||||
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
|
||||
/doc/administration/issue_closing_pattern.md @aqualls
|
||||
/doc/administration/job_artifacts.md @marcel.amirault
|
||||
|
@ -278,11 +278,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/smime_signing_email.md @axil
|
||||
/doc/administration/snippets/index.md @aqualls
|
||||
/doc/administration/static_objects_external_storage.md @aqualls
|
||||
/doc/administration/system_hooks.md @kpaizee
|
||||
/doc/administration/system_hooks.md @ashrafkhamis
|
||||
/doc/administration/terraform_state.md @sselhorn
|
||||
/doc/administration/timezone.md @axil
|
||||
/doc/administration/troubleshooting/ @axil
|
||||
/doc/administration/troubleshooting/elasticsearch.md @sselhorn
|
||||
/doc/administration/troubleshooting/elasticsearch.md @ashrafkhamis
|
||||
/doc/administration/troubleshooting/postgresql.md @aqualls
|
||||
/doc/administration/uploads.md @axil
|
||||
/doc/administration/user_settings.md @eread
|
||||
|
@ -291,7 +291,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/access_requests.md @eread
|
||||
/doc/api/admin_sidekiq_queues.md @axil
|
||||
/doc/api/alert_management_alerts.md @msedlakjakubowski
|
||||
/doc/api/api_resources.md @kpaizee
|
||||
/doc/api/api_resources.md @ashrafkhamis
|
||||
/doc/api/appearance.md @eread
|
||||
/doc/api/applications.md @eread
|
||||
/doc/api/audit_events.md @eread
|
||||
|
@ -304,7 +304,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/cluster_agents.md @sselhorn
|
||||
/doc/api/commits.md @aqualls
|
||||
/doc/api/container_registry.md @claytoncornell
|
||||
/doc/api/custom_attributes.md @kpaizee
|
||||
/doc/api/custom_attributes.md @ashrafkhamis
|
||||
/doc/api/dependencies.md @rdickenson
|
||||
/doc/api/dependency_proxy.md @claytoncornell
|
||||
/doc/api/deploy_keys.md @rdickenson
|
||||
|
@ -326,7 +326,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/features.md @rdickenson
|
||||
/doc/api/freeze_periods.md @rdickenson
|
||||
/doc/api/geo_nodes.md @axil
|
||||
/doc/api/graphql/ @kpaizee
|
||||
/doc/api/graphql/ @ashrafkhamis
|
||||
/doc/api/graphql/custom_emoji.md @msedlakjakubowski
|
||||
/doc/api/graphql/sample_issue_boards.md @msedlakjakubowski
|
||||
/doc/api/group_access_tokens.md @eread
|
||||
|
@ -346,10 +346,10 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/group_wikis.md @aqualls
|
||||
/doc/api/groups.md @fneill
|
||||
/doc/api/import.md @eread
|
||||
/doc/api/index.md @kpaizee
|
||||
/doc/api/index.md @ashrafkhamis
|
||||
/doc/api/instance_clusters.md @sselhorn
|
||||
/doc/api/instance_level_ci_variables.md @marcel.amirault
|
||||
/doc/api/integrations.md @kpaizee
|
||||
/doc/api/integrations.md @ashrafkhamis
|
||||
/doc/api/invitations.md @kpaizee
|
||||
/doc/api/issue_links.md @msedlakjakubowski
|
||||
/doc/api/issues_statistics.md @msedlakjakubowski
|
||||
|
@ -376,7 +376,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/notes.md @msedlakjakubowski
|
||||
/doc/api/notification_settings.md @msedlakjakubowski
|
||||
/doc/api/oauth2.md @eread
|
||||
/doc/api/openapi/openapi_interactive.md @kpaizee
|
||||
/doc/api/openapi/openapi_interactive.md @ashrafkhamis
|
||||
/doc/api/packages.md @claytoncornell
|
||||
/doc/api/packages/ @claytoncornell
|
||||
/doc/api/pages_domains.md @aqualls
|
||||
|
@ -425,7 +425,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/statistics.md @eread
|
||||
/doc/api/status_checks.md @eread
|
||||
/doc/api/suggestions.md @aqualls
|
||||
/doc/api/system_hooks.md @kpaizee
|
||||
/doc/api/system_hooks.md @ashrafkhamis
|
||||
/doc/api/tags.md @aqualls
|
||||
/doc/api/templates/dockerfiles.md @aqualls
|
||||
/doc/api/templates/gitignores.md @aqualls
|
||||
|
@ -435,7 +435,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/topics.md @fneill
|
||||
/doc/api/usage_data.md @claytoncornell
|
||||
/doc/api/users.md @eread
|
||||
/doc/api/version.md @kpaizee
|
||||
/doc/api/version.md @ashrafkhamis
|
||||
/doc/api/visual_review_discussions.md @marcel.amirault
|
||||
/doc/api/vulnerabilities.md @claytoncornell
|
||||
/doc/api/vulnerability_exports.md @claytoncornell
|
||||
|
@ -521,7 +521,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/development/documentation/redirects.md @dianalogan
|
||||
/doc/development/documentation/review_apps.md @dianalogan
|
||||
/doc/development/documentation/testing.md @dianalogan
|
||||
/doc/development/elasticsearch.md @sselhorn
|
||||
/doc/development/elasticsearch.md @ashrafkhamis
|
||||
/doc/development/experiment_guide/gitlab_experiment.md @kpaizee
|
||||
/doc/development/experiment_guide/index.md @kpaizee
|
||||
/doc/development/export_csv.md @eread
|
||||
|
@ -539,14 +539,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/development/gitaly.md @eread
|
||||
/doc/development/gitlab_flavored_markdown/index.md @aqualls
|
||||
/doc/development/gitlab_flavored_markdown/specification_guide/index.md @aqualls
|
||||
/doc/development/graphql_guide/ @kpaizee
|
||||
/doc/development/graphql_guide/ @ashrafkhamis
|
||||
/doc/development/graphql_guide/batchloader.md @aqualls
|
||||
/doc/development/hash_indexes.md @aqualls
|
||||
/doc/development/i18n/ @eread
|
||||
/doc/development/image_scaling.md @sselhorn
|
||||
/doc/development/import_export.md @eread
|
||||
/doc/development/index.md @sselhorn
|
||||
/doc/development/integrations/ @kpaizee
|
||||
/doc/development/integrations/ @ashrafkhamis
|
||||
/doc/development/integrations/codesandbox.md @sselhorn
|
||||
/doc/development/integrations/secure_partner_integration.md @rdickenson
|
||||
/doc/development/integrations/secure.md @claytoncornell
|
||||
|
@ -584,9 +584,9 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/downgrade_ee_to_ce/index.md @axil
|
||||
/doc/gitlab-basics/ @aqualls
|
||||
/doc/install/ @axil
|
||||
/doc/integration/ @kpaizee
|
||||
/doc/integration/advanced_search/ @sselhorn
|
||||
/doc/integration/elasticsearch.md @sselhorn
|
||||
/doc/integration/ @ashrafkhamis
|
||||
/doc/integration/advanced_search/ @ashrafkhamis
|
||||
/doc/integration/elasticsearch.md @ashrafkhamis
|
||||
/doc/integration/gitpod.md @aqualls
|
||||
/doc/integration/kerberos.md @eread
|
||||
/doc/integration/mattermost/index.md @axil
|
||||
|
@ -645,7 +645,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/admin_area/settings/index.md @aqualls
|
||||
/doc/user/admin_area/settings/instance_template_repository.md @aqualls
|
||||
/doc/user/admin_area/settings/package_registry_rate_limits.md @claytoncornell
|
||||
/doc/user/admin_area/settings/project_integration_management.md @kpaizee
|
||||
/doc/user/admin_area/settings/project_integration_management.md @ashrafkhamis
|
||||
/doc/user/admin_area/settings/push_event_activities_limit.md @aqualls
|
||||
/doc/user/admin_area/settings/rate_limit_on_issues_creation.md @msedlakjakubowski
|
||||
/doc/user/admin_area/settings/rate_limit_on_notes_creation.md @msedlakjakubowski
|
||||
|
@ -732,7 +732,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/import/ @eread
|
||||
/doc/user/project/import/jira.md @msedlakjakubowski
|
||||
/doc/user/project/index.md @fneill
|
||||
/doc/user/project/integrations/ @kpaizee
|
||||
/doc/user/project/integrations/ @ashrafkhamis
|
||||
/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
|
||||
/doc/user/project/integrations/prometheus.md @msedlakjakubowski
|
||||
/doc/user/project/issue_board.md @msedlakjakubowski
|
||||
|
@ -774,9 +774,9 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/working_with_projects.md @fneill
|
||||
/doc/user/public_access.md @fneill
|
||||
/doc/user/reserved_names.md @fneill
|
||||
/doc/user/search/advanced_search.md @sselhorn
|
||||
/doc/user/search/global_search/advanced_search_syntax.md @sselhorn
|
||||
/doc/user/search/index.md @sselhorn
|
||||
/doc/user/search/advanced_search.md @ashrafkhamis
|
||||
/doc/user/search/global_search/advanced_search_syntax.md @ashrafkhamis
|
||||
/doc/user/search/index.md @ashrafkhamis
|
||||
/doc/user/shortcuts.md @aqualls
|
||||
/doc/user/snippets.md @aqualls
|
||||
/doc/user/ssh.md @eread
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
.if-auto-deploy-branches: &if-auto-deploy-branches
|
||||
if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
|
||||
|
||||
.if-tag: &if-tag
|
||||
if: '$CI_COMMIT_TAG'
|
||||
|
||||
.if-default-branch-or-tag: &if-default-branch-or-tag
|
||||
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG'
|
||||
|
||||
|
@ -636,6 +639,7 @@
|
|||
changes: *code-qa-patterns
|
||||
- <<: *if-dot-com-gitlab-org-default-branch
|
||||
changes: *code-qa-patterns
|
||||
- <<: *if-tag
|
||||
- <<: *if-dot-com-gitlab-org-schedule
|
||||
- <<: *if-force-ci
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ import {
|
|||
REACHED_LIMIT_MESSAGE,
|
||||
REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
|
||||
CLOSE_TO_LIMIT_MESSAGE,
|
||||
CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE,
|
||||
DANGER_ALERT_TITLE_PERSONAL_NAMESPACE,
|
||||
} from '../constants';
|
||||
|
||||
export default {
|
||||
|
@ -52,13 +50,6 @@ export default {
|
|||
});
|
||||
},
|
||||
dangerAlertTitle() {
|
||||
if (this.usersLimitDataset.userNamespace) {
|
||||
return sprintf(DANGER_ALERT_TITLE_PERSONAL_NAMESPACE, {
|
||||
count: this.freeUsersLimit,
|
||||
members: this.pluralMembers(this.freeUsersLimit),
|
||||
});
|
||||
}
|
||||
|
||||
return sprintf(DANGER_ALERT_TITLE, {
|
||||
count: this.freeUsersLimit,
|
||||
members: this.pluralMembers(this.freeUsersLimit),
|
||||
|
@ -71,20 +62,9 @@ export default {
|
|||
title() {
|
||||
return this.reachedLimit ? this.dangerAlertTitle : this.warningAlertTitle;
|
||||
},
|
||||
reachedLimitMessage() {
|
||||
if (this.usersLimitDataset.userNamespace) {
|
||||
return this.$options.i18n.reachedLimitMessage;
|
||||
}
|
||||
|
||||
return this.$options.i18n.reachedLimitUpgradeSuggestionMessage;
|
||||
},
|
||||
message() {
|
||||
if (this.reachedLimit) {
|
||||
return this.reachedLimitMessage;
|
||||
}
|
||||
|
||||
if (this.usersLimitDataset.userNamespace) {
|
||||
return this.$options.i18n.closeToLimitMessagePersonalNamespace;
|
||||
return this.$options.i18n.reachedLimitUpgradeSuggestionMessage;
|
||||
}
|
||||
|
||||
return this.$options.i18n.closeToLimitMessage;
|
||||
|
@ -99,7 +79,6 @@ export default {
|
|||
reachedLimitMessage: REACHED_LIMIT_MESSAGE,
|
||||
reachedLimitUpgradeSuggestionMessage: REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
|
||||
closeToLimitMessage: CLOSE_TO_LIMIT_MESSAGE,
|
||||
closeToLimitMessagePersonalNamespace: CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -146,10 +146,6 @@ export const DANGER_ALERT_TITLE = s__(
|
|||
"InviteMembersModal|You've reached your %{count} %{members} limit for %{name}",
|
||||
);
|
||||
|
||||
export const DANGER_ALERT_TITLE_PERSONAL_NAMESPACE = s__(
|
||||
"InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects",
|
||||
);
|
||||
|
||||
export const REACHED_LIMIT_MESSAGE = s__(
|
||||
'InviteMembersModal|You cannot add more members, but you can remove members who no longer need access.',
|
||||
);
|
||||
|
@ -163,6 +159,3 @@ export const REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE = REACHED_LIMIT_MESSAGE.co
|
|||
export const CLOSE_TO_LIMIT_MESSAGE = s__(
|
||||
'InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier.',
|
||||
);
|
||||
export const CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE = s__(
|
||||
'InviteMembersModal|To make more space, you can remove members who no longer need access.',
|
||||
);
|
||||
|
|
|
@ -20,8 +20,6 @@ export default (function initInviteMembersModal() {
|
|||
return false;
|
||||
}
|
||||
|
||||
const usersLimitDataset = JSON.parse(el.dataset.usersLimitDataset || '{}');
|
||||
|
||||
inviteMembersModal = new Vue({
|
||||
el,
|
||||
name: 'InviteMembersModalRoot',
|
||||
|
@ -40,10 +38,9 @@ export default (function initInviteMembersModal() {
|
|||
projects: JSON.parse(el.dataset.projects || '[]'),
|
||||
usersFilter: el.dataset.usersFilter,
|
||||
filterId: parseInt(el.dataset.filterId, 10),
|
||||
usersLimitDataset: convertObjectPropsToCamelCase({
|
||||
...usersLimitDataset,
|
||||
user_namespace: parseBoolean(usersLimitDataset.user_namespace),
|
||||
}),
|
||||
usersLimitDataset: convertObjectPropsToCamelCase(
|
||||
JSON.parse(el.dataset.usersLimitDataset || '{}'),
|
||||
),
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import { PIPELINE_FAILURE } from '../../constants';
|
||||
|
||||
|
@ -10,8 +10,6 @@ export default {
|
|||
},
|
||||
components: {
|
||||
PipelineMiniGraph,
|
||||
LinkedPipelinesMiniList: () =>
|
||||
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
|
||||
},
|
||||
inject: ['projectFullPath'],
|
||||
props: {
|
||||
|
@ -47,9 +45,6 @@ export default {
|
|||
downstreamPipelines() {
|
||||
return this.linkedPipelines?.downstream?.nodes || [];
|
||||
},
|
||||
hasDownstreamPipelines() {
|
||||
return this.downstreamPipelines.length > 0;
|
||||
},
|
||||
hasPipelineStages() {
|
||||
return this.pipelineStages.length > 0;
|
||||
},
|
||||
|
@ -87,23 +82,11 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
<pipeline-mini-graph
|
||||
v-if="hasPipelineStages"
|
||||
class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5"
|
||||
>
|
||||
<linked-pipelines-mini-list
|
||||
v-if="upstreamPipeline"
|
||||
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
upstreamPipeline,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
data-testid="pipeline-editor-mini-graph-upstream"
|
||||
/>
|
||||
<pipeline-mini-graph :stages="pipelineStages" />
|
||||
<linked-pipelines-mini-list
|
||||
v-if="hasDownstreamPipelines"
|
||||
:triggered="downstreamPipelines"
|
||||
:pipeline-path="pipelinePath"
|
||||
data-testid="pipeline-editor-mini-graph-downstream"
|
||||
/>
|
||||
</div>
|
||||
:downstream-pipelines="downstreamPipelines"
|
||||
:pipeline-path="pipelinePath"
|
||||
:stages="pipelineStages"
|
||||
:upstream-pipeline="upstreamPipeline"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -174,7 +174,7 @@ export default {
|
|||
<div class="gl-display-flex gl-flex-wrap">
|
||||
<pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" />
|
||||
<gl-button
|
||||
class="gl-mt-2 gl-md-mt-0"
|
||||
class="gl-ml-3"
|
||||
category="secondary"
|
||||
variant="confirm"
|
||||
:href="status.detailsPath"
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { get } from 'lodash';
|
||||
|
||||
export const accessors = {
|
||||
rest: {
|
||||
detailedStatus: ['details', 'status'],
|
||||
},
|
||||
graphql: {
|
||||
detailedStatus: 'detailedStatus',
|
||||
},
|
||||
};
|
||||
|
||||
export const accessValue = (pipeline, dataMethod, path) => {
|
||||
return get(pipeline, accessors[dataMethod][path]);
|
||||
};
|
|
@ -0,0 +1,132 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import { accessValue } from './accessors/linked_pipelines_accessors';
|
||||
/**
|
||||
* Renders the upstream/downstream portions of the pipeline mini graph.
|
||||
*/
|
||||
export default {
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
components: {
|
||||
CiIcon,
|
||||
},
|
||||
inject: {
|
||||
dataMethod: {
|
||||
default: 'rest',
|
||||
},
|
||||
},
|
||||
props: {
|
||||
triggeredBy: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
triggered: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
pipelinePath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maxRenderedPipelines: 3,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// Exactly one of these (triggeredBy and triggered) must be truthy. Never both. Never neither.
|
||||
isUpstream() {
|
||||
return Boolean(this.triggeredBy.length) && !this.triggered.length;
|
||||
},
|
||||
isDownstream() {
|
||||
return !this.triggeredBy.length && Boolean(this.triggered.length);
|
||||
},
|
||||
linkedPipelines() {
|
||||
return this.isUpstream ? this.triggeredBy : this.triggered;
|
||||
},
|
||||
totalPipelineCount() {
|
||||
return this.linkedPipelines.length;
|
||||
},
|
||||
linkedPipelinesTrimmed() {
|
||||
return this.totalPipelineCount > this.maxRenderedPipelines
|
||||
? this.linkedPipelines.slice(0, this.maxRenderedPipelines)
|
||||
: this.linkedPipelines;
|
||||
},
|
||||
shouldRenderCounter() {
|
||||
return this.isDownstream && this.linkedPipelines.length > this.maxRenderedPipelines;
|
||||
},
|
||||
counterLabel() {
|
||||
return `+${this.linkedPipelines.length - this.maxRenderedPipelines}`;
|
||||
},
|
||||
counterTooltipText() {
|
||||
return sprintf(s__('LinkedPipelines|%{counterLabel} more downstream pipelines'), {
|
||||
counterLabel: this.counterLabel,
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
pipelineTooltipText(pipeline) {
|
||||
const { label } = accessValue(pipeline, this.dataMethod, 'detailedStatus');
|
||||
|
||||
return `${pipeline.project.name} - ${label}`;
|
||||
},
|
||||
pipelineStatus(pipeline) {
|
||||
// detailedStatus is graphQL, details.status is REST
|
||||
return pipeline?.detailedStatus || pipeline?.details?.status;
|
||||
},
|
||||
triggerButtonClass(pipeline) {
|
||||
const { group } = accessValue(pipeline, this.dataMethod, 'detailedStatus');
|
||||
|
||||
return `ci-status-icon-${group}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
v-if="linkedPipelines"
|
||||
:class="{
|
||||
'is-upstream': isUpstream,
|
||||
'is-downstream': isDownstream,
|
||||
}"
|
||||
class="linked-pipeline-mini-list gl-display-inline gl-vertical-align-middle"
|
||||
>
|
||||
<a
|
||||
v-for="pipeline in linkedPipelinesTrimmed"
|
||||
:key="pipeline.id"
|
||||
v-gl-tooltip="{ title: pipelineTooltipText(pipeline) }"
|
||||
:href="pipeline.path"
|
||||
:class="triggerButtonClass(pipeline)"
|
||||
class="linked-pipeline-mini-item gl-display-inline-block gl-h-6 gl-mr-2 gl-my-2 gl-rounded-full gl-vertical-align-middle"
|
||||
data-testid="linked-pipeline-mini-item"
|
||||
>
|
||||
<ci-icon
|
||||
is-borderless
|
||||
is-interactive
|
||||
css-classes="gl-rounded-full"
|
||||
:size="24"
|
||||
:status="pipelineStatus(pipeline)"
|
||||
class="gl-align-items-center gl-border gl-display-inline-flex"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-if="shouldRenderCounter"
|
||||
v-gl-tooltip="{ title: counterTooltipText }"
|
||||
:title="counterTooltipText"
|
||||
:href="pipelinePath"
|
||||
class="gl-align-items-center gl-bg-gray-50 gl-display-inline-flex gl-font-sm gl-h-6 gl-justify-content-center gl-rounded-pill gl-text-decoration-none gl-text-gray-500 gl-w-7 linked-pipelines-counter linked-pipeline-mini-item"
|
||||
data-testid="linked-pipeline-counter"
|
||||
>
|
||||
{{ counterLabel }}
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
|
@ -0,0 +1,102 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import PipelineStages from './pipeline_stages.vue';
|
||||
import LinkedPipelinesMiniList from './linked_pipelines_mini_list.vue';
|
||||
/**
|
||||
* Renders the pipeline mini graph.
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
LinkedPipelinesMiniList,
|
||||
PipelineStages,
|
||||
},
|
||||
arrowStyles: [
|
||||
'arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 gl-vertical-align-middle!',
|
||||
],
|
||||
props: {
|
||||
downstreamPipelines: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
isMergeTrain: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
pipelinePath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
stages: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
stagesClass: {
|
||||
type: [Array, Object, String],
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
updateDropdown: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
upstreamPipeline: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasDownstreamPipelines() {
|
||||
return Boolean(this.downstreamPipelines.length);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onPipelineActionRequestComplete() {
|
||||
this.$emit('pipelineActionRequestComplete');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="stage-cell" data-testid="pipeline-mini-graph">
|
||||
<linked-pipelines-mini-list
|
||||
v-if="upstreamPipeline"
|
||||
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
upstreamPipeline,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
data-testid="pipeline-mini-graph-upstream"
|
||||
/>
|
||||
<gl-icon
|
||||
v-if="upstreamPipeline"
|
||||
:class="$options.arrowStyles"
|
||||
name="long-arrow"
|
||||
data-testid="upstream-arrow-icon"
|
||||
/>
|
||||
<pipeline-stages
|
||||
:is-merge-train="isMergeTrain"
|
||||
:stages="stages"
|
||||
:update-dropdown="updateDropdown"
|
||||
:stages-class="stagesClass"
|
||||
data-testid="pipeline-stages"
|
||||
@pipelineActionRequestComplete="onPipelineActionRequestComplete"
|
||||
/>
|
||||
<gl-icon
|
||||
v-if="hasDownstreamPipelines"
|
||||
:class="$options.arrowStyles"
|
||||
name="long-arrow"
|
||||
data-testid="downstream-arrow-icon"
|
||||
/>
|
||||
<linked-pipelines-mini-list
|
||||
v-if="hasDownstreamPipelines"
|
||||
:triggered="downstreamPipelines"
|
||||
:pipeline-path="pipelinePath"
|
||||
data-testid="pipeline-mini-graph-downstream"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
|
||||
import PipelineStage from './pipeline_stage.vue';
|
||||
/**
|
||||
* Renders the pipeline mini graph.
|
||||
* Renders the pipeline stages portion of the pipeline mini graph.
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
|
@ -36,7 +36,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle">
|
||||
<div data-testid="pipeline-stages" class="gl-display-inline gl-vertical-align-middle">
|
||||
<div
|
||||
v-for="stage in stages"
|
||||
:key="stage.name"
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { GlTableLite, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__, __ } from '~/locale';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import eventHub from '../../event_hub';
|
||||
import PipelineMiniGraph from './pipeline_mini_graph.vue';
|
||||
import PipelineOperations from './pipeline_operations.vue';
|
||||
import PipelineStopModal from './pipeline_stop_modal.vue';
|
||||
import PipelineTriggerer from './pipeline_triggerer.vue';
|
||||
|
@ -17,8 +17,6 @@ const DEFAULT_TH_CLASSES =
|
|||
export default {
|
||||
components: {
|
||||
GlTableLite,
|
||||
LinkedPipelinesMiniList: () =>
|
||||
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
|
||||
PipelineMiniGraph,
|
||||
PipelineOperations,
|
||||
PipelinesStatusBadge,
|
||||
|
@ -169,29 +167,14 @@ export default {
|
|||
</template>
|
||||
|
||||
<template #cell(stages)="{ item }">
|
||||
<div class="stage-cell">
|
||||
<!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 -->
|
||||
<div></div>
|
||||
<linked-pipelines-mini-list
|
||||
v-if="item.triggered_by"
|
||||
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
item.triggered_by,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
data-testid="mini-graph-upstream"
|
||||
/>
|
||||
<pipeline-mini-graph
|
||||
v-if="item.details && item.details.stages && item.details.stages.length > 0"
|
||||
:stages="item.details.stages"
|
||||
:update-dropdown="updateGraphDropdown"
|
||||
@pipelineActionRequestComplete="onPipelineActionRequestComplete"
|
||||
/>
|
||||
<linked-pipelines-mini-list
|
||||
v-if="item.triggered.length"
|
||||
:triggered="item.triggered"
|
||||
:pipeline-path="item.path"
|
||||
data-testid="mini-graph-downstream"
|
||||
/>
|
||||
</div>
|
||||
<pipeline-mini-graph
|
||||
:downstream-pipelines="item.triggered"
|
||||
:pipeline-path="item.path"
|
||||
:stages="item.details.stages"
|
||||
:update-dropdown="updateGraphDropdown"
|
||||
:upstream-pipeline="item.triggered_by"
|
||||
@pipelineActionRequestComplete="onPipelineActionRequestComplete"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell(actions)="{ item }">
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import {
|
||||
getQueryHeaders,
|
||||
toggleQueryPollingByVisibility,
|
||||
} from '~/pipelines/components/graph/utils';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import { formatStages } from '../utils';
|
||||
import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql';
|
||||
|
@ -21,8 +21,6 @@ export default {
|
|||
components: {
|
||||
GlLoadingIcon,
|
||||
PipelineMiniGraph,
|
||||
LinkedPipelinesMiniList: () =>
|
||||
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
|
||||
},
|
||||
inject: {
|
||||
fullPath: {
|
||||
|
@ -92,12 +90,12 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
hasDownstream() {
|
||||
return this.pipeline?.downstream?.nodes.length > 0;
|
||||
},
|
||||
downstreamPipelines() {
|
||||
return this.pipeline?.downstream?.nodes;
|
||||
},
|
||||
pipelinePath() {
|
||||
return this.pipeline?.path ?? '';
|
||||
},
|
||||
upstreamPipeline() {
|
||||
return this.pipeline?.upstream;
|
||||
},
|
||||
|
@ -128,23 +126,13 @@ export default {
|
|||
<template>
|
||||
<div class="gl-pt-2">
|
||||
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
|
||||
<div v-else class="gl-align-items-center gl-display-flex">
|
||||
<linked-pipelines-mini-list
|
||||
v-if="upstreamPipeline"
|
||||
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
upstreamPipeline,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
data-testid="commit-box-mini-graph-upstream"
|
||||
/>
|
||||
|
||||
<pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" />
|
||||
|
||||
<linked-pipelines-mini-list
|
||||
v-if="hasDownstream"
|
||||
:triggered="downstreamPipelines"
|
||||
:pipeline-path="pipeline.path"
|
||||
data-testid="commit-box-mini-graph-downstream"
|
||||
/>
|
||||
</div>
|
||||
<pipeline-mini-graph
|
||||
v-else
|
||||
data-testid="commit-box-pipeline-mini-graph"
|
||||
:downstream-pipelines="downstreamPipelines"
|
||||
:pipeline-path="pipelinePath"
|
||||
:stages="formattedStages"
|
||||
:upstream-pipeline="upstreamPipeline"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -9,11 +9,11 @@ import {
|
|||
GlTooltipDirective,
|
||||
GlSafeHtmlDirective,
|
||||
} from '@gitlab/ui';
|
||||
import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
|
||||
import mrWidgetPipelineMixin from '~/vue_merge_request_widget/mixins/mr_widget_pipeline';
|
||||
import { s__, n__ } from '~/locale';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||
import { MT_MERGE_STRATEGY } from '../constants';
|
||||
|
@ -31,8 +31,6 @@ export default {
|
|||
PipelineMiniGraph,
|
||||
TimeAgoTooltip,
|
||||
TooltipOnTruncate,
|
||||
LinkedPipelinesMiniList: () =>
|
||||
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
@ -276,17 +274,15 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph">
|
||||
<span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
|
||||
<linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
|
||||
<pipeline-mini-graph
|
||||
v-if="hasStages"
|
||||
stages-class="mr-widget-pipeline-stages"
|
||||
:stages="pipeline.details.stages"
|
||||
:is-merge-train="isMergeTrain"
|
||||
/>
|
||||
</span>
|
||||
<linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" />
|
||||
<span class="gl-align-items-center gl-display-inline-flex">
|
||||
<pipeline-mini-graph
|
||||
v-if="pipeline.details.stages"
|
||||
:downstream-pipelines="triggered"
|
||||
:is-merge-train="isMergeTrain"
|
||||
:stages="pipeline.details.stages"
|
||||
:upstream-pipeline="triggeredBy[0]"
|
||||
stages-class="mr-widget-pipeline-stages"
|
||||
/>
|
||||
<pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { normalizeHeaders } from '~/lib/utils/common_utils';
|
|||
import { sprintf, __ } from '~/locale';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import StatusIcon from '../extensions/status_icon.vue';
|
||||
import { EXTENSION_ICON_NAMES } from '../../constants';
|
||||
import { EXTENSION_ICONS } from '../../constants';
|
||||
|
||||
const FETCH_TYPE_COLLAPSED = 'collapsed';
|
||||
|
||||
|
@ -66,7 +66,7 @@ export default {
|
|||
type: String,
|
||||
default: 'neutral',
|
||||
required: false,
|
||||
validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1,
|
||||
validator: (value) => Object.keys(EXTENSION_ICONS).indexOf(value) > -1,
|
||||
},
|
||||
isCollapsible: {
|
||||
type: Boolean,
|
||||
|
@ -84,6 +84,11 @@ export default {
|
|||
error: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
statusIcon() {
|
||||
return this.error ? EXTENSION_ICONS.failed : this.statusIconName;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isLoading(newValue) {
|
||||
this.$emit('is-loading', newValue);
|
||||
|
@ -147,18 +152,14 @@ export default {
|
|||
<template>
|
||||
<section class="media-section" data-testid="widget-extension">
|
||||
<div class="media gl-p-5">
|
||||
<status-icon
|
||||
:level="1"
|
||||
:name="widgetName"
|
||||
:is-loading="isLoading"
|
||||
:icon-name="statusIconName"
|
||||
/>
|
||||
<status-icon :level="1" :name="widgetName" :is-loading="isLoading" :icon-name="statusIcon" />
|
||||
<div
|
||||
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
|
||||
data-testid="widget-extension-top-level"
|
||||
>
|
||||
<div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
|
||||
<slot name="summary">{{ isLoading ? loadingText : summary }}</slot>
|
||||
<slot v-if="!error" name="summary">{{ isLoading ? loadingText : summary }}</slot>
|
||||
<span v-else>{{ error }}</span>
|
||||
</div>
|
||||
<!-- actions will go here -->
|
||||
<div
|
||||
|
|
|
@ -427,10 +427,10 @@
|
|||
padding-inline-start: 28px;
|
||||
margin-inline-start: 0 !important;
|
||||
|
||||
> input.task-list-item-checkbox {
|
||||
input.task-list-item-checkbox {
|
||||
position: absolute;
|
||||
inset-inline-start: 8px;
|
||||
top: 5px;
|
||||
inset-inline-start: $gl-padding-8;
|
||||
inset-block-start: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +1,24 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.description {
|
||||
ul,
|
||||
ol {
|
||||
/* We're changing list-style-position to inside because the default of
|
||||
* outside doesn't move negative margin to the left of the bullet. */
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
/* In the browser, the li element comes after (to the right of) the bullet point, so hovering
|
||||
* over the left of the bullet point doesn't trigger a row hover. To trigger hovering on the
|
||||
* left, we're applying negative margin here to shift the li element left. */
|
||||
margin-inline-start: -1rem;
|
||||
padding-inline-start: 2.5rem;
|
||||
margin-inline-start: 2.25rem;
|
||||
|
||||
.drag-icon {
|
||||
position: absolute;
|
||||
inset-block-start: 0.3rem;
|
||||
inset-inline-start: 1rem;
|
||||
}
|
||||
|
||||
/* The inside bullet aligns itself to the bottom, which we see when text to the right of
|
||||
* a multi-line list item wraps. We fix this by aligning it to the top, and excluding
|
||||
* other elements. Targeting ::marker doesn't seem to work, instead we exclude custom elements
|
||||
* or anything with a class */
|
||||
> *:not(gl-emoji, code, [class]) {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* The inside bullet is treated like an element inside the li element, so when we have a
|
||||
* multi-paragraph list item, the text doesn't start on the right of the bullet because
|
||||
* it is a block level p element. We make it inline to fix this. */
|
||||
> p:first-of-type {
|
||||
display: inline-block;
|
||||
max-width: calc(100% - 1.5rem);
|
||||
}
|
||||
|
||||
/* We fix the other paragraphs not indenting to the
|
||||
* right of the bullet due to the inside bullet. */
|
||||
p ~ a,
|
||||
p ~ blockquote,
|
||||
p ~ code,
|
||||
p ~ details,
|
||||
p ~ dl,
|
||||
p ~ h1,
|
||||
p ~ h2,
|
||||
p ~ h3,
|
||||
p ~ h4,
|
||||
p ~ h5,
|
||||
p ~ h6,
|
||||
p ~ hr,
|
||||
p ~ ol,
|
||||
p ~ p,
|
||||
p ~ table:not(.code), /* We need :not(.code) to override typography.scss */
|
||||
p ~ ul,
|
||||
p ~ .markdown-code-block {
|
||||
margin-inline-start: 1rem;
|
||||
inset-inline-start: -2.3rem;
|
||||
padding-inline-end: 1rem;
|
||||
width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul.task-list {
|
||||
> li.task-list-item {
|
||||
/* We're using !important to override the same selector in typography.scss */
|
||||
margin-inline-start: -1rem !important;
|
||||
padding-inline-start: 2.5rem;
|
||||
ul.task-list > li.task-list-item {
|
||||
margin-inline-start: 0.5rem !important; /* Override typography.scss */
|
||||
|
||||
> input.task-list-item-checkbox {
|
||||
position: static;
|
||||
vertical-align: middle;
|
||||
margin-block-start: -2px;
|
||||
}
|
||||
> .drag-icon {
|
||||
inset-inline-start: -0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -398,12 +398,6 @@ $tabs-holder-z-index: 250;
|
|||
display: block;
|
||||
}
|
||||
|
||||
.mr-widget-pipeline-graph {
|
||||
.dropdown-menu {
|
||||
z-index: $zindex-dropdown-menu;
|
||||
}
|
||||
}
|
||||
|
||||
.normal {
|
||||
flex: 1;
|
||||
flex-basis: auto;
|
||||
|
|
|
@ -33,12 +33,6 @@
|
|||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-pipeline-graph {
|
||||
.dropdown-menu {
|
||||
margin-top: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branch-info .commit-icon {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::HookLogsController < Admin::ApplicationController
|
||||
include ::Integrations::HooksExecution
|
||||
include ::WebHooks::HookExecutionNotice
|
||||
|
||||
before_action :hook, only: [:show, :retry]
|
||||
before_action :hook_log, only: [:show, :retry]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::HooksController < Admin::ApplicationController
|
||||
include ::Integrations::HooksExecution
|
||||
include ::WebHooks::HookActions
|
||||
|
||||
before_action :hook_logs, only: :edit
|
||||
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Integrations::HooksExecution
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
attr_writer :hooks, :hook
|
||||
end
|
||||
|
||||
def index
|
||||
self.hooks = relation.select(&:persisted?)
|
||||
self.hook = relation.new
|
||||
end
|
||||
|
||||
def create
|
||||
self.hook = relation.new(hook_params)
|
||||
hook.save
|
||||
|
||||
unless hook.valid?
|
||||
self.hooks = relation.select(&:persisted?)
|
||||
flash[:alert] = hook.errors.full_messages.join.html_safe
|
||||
end
|
||||
|
||||
redirect_to action: :index
|
||||
end
|
||||
|
||||
def update
|
||||
if hook.update(hook_params)
|
||||
flash[:notice] = _('Hook was successfully updated.')
|
||||
redirect_to action: :index
|
||||
else
|
||||
render 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_hook(hook)
|
||||
|
||||
redirect_to action: :index, status: :found
|
||||
end
|
||||
|
||||
def edit
|
||||
redirect_to(action: :index) unless hook
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hook_params
|
||||
permitted = hook_param_names + trigger_values
|
||||
permitted << { url_variables: [:key, :value] }
|
||||
|
||||
ps = params.require(:hook).permit(*permitted).to_h
|
||||
|
||||
ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables)
|
||||
|
||||
if action_name == 'update' && ps.key?(:url_variables)
|
||||
supplied = ps[:url_variables]
|
||||
ps[:url_variables] = hook.url_variables.merge(supplied).compact
|
||||
end
|
||||
|
||||
ps
|
||||
end
|
||||
|
||||
def hook_param_names
|
||||
%i[enable_ssl_verification token url push_events_branch_filter]
|
||||
end
|
||||
|
||||
def destroy_hook(hook)
|
||||
result = WebHooks::DestroyService.new(current_user).execute(hook)
|
||||
|
||||
if result[:status] == :success
|
||||
flash[:notice] =
|
||||
if result[:async]
|
||||
_("%{hook_type} was scheduled for deletion") % { hook_type: hook.model_name.human }
|
||||
else
|
||||
_("%{hook_type} was deleted") % { hook_type: hook.model_name.human }
|
||||
end
|
||||
else
|
||||
flash[:alert] = result[:message]
|
||||
end
|
||||
end
|
||||
|
||||
def set_hook_execution_notice(result)
|
||||
http_status = result[:http_status]
|
||||
message = result[:message]
|
||||
|
||||
if http_status && http_status >= 200 && http_status < 400
|
||||
flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
|
||||
elsif http_status
|
||||
flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
|
||||
else
|
||||
flash[:alert] = "Hook execution failed: #{message}"
|
||||
end
|
||||
end
|
||||
end
|
85
app/controllers/concerns/web_hooks/hook_actions.rb
Normal file
85
app/controllers/concerns/web_hooks/hook_actions.rb
Normal file
|
@ -0,0 +1,85 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WebHooks
|
||||
module HookActions
|
||||
extend ActiveSupport::Concern
|
||||
include HookExecutionNotice
|
||||
|
||||
included do
|
||||
attr_writer :hooks, :hook
|
||||
end
|
||||
|
||||
def index
|
||||
self.hooks = relation.select(&:persisted?)
|
||||
self.hook = relation.new
|
||||
end
|
||||
|
||||
def create
|
||||
self.hook = relation.new(hook_params)
|
||||
hook.save
|
||||
|
||||
unless hook.valid?
|
||||
self.hooks = relation.select(&:persisted?)
|
||||
flash[:alert] = hook.errors.full_messages.join.html_safe
|
||||
end
|
||||
|
||||
redirect_to action: :index
|
||||
end
|
||||
|
||||
def update
|
||||
if hook.update(hook_params)
|
||||
flash[:notice] = _('Hook was successfully updated.')
|
||||
redirect_to action: :index
|
||||
else
|
||||
render 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_hook(hook)
|
||||
|
||||
redirect_to action: :index, status: :found
|
||||
end
|
||||
|
||||
def edit
|
||||
redirect_to(action: :index) unless hook
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hook_params
|
||||
permitted = hook_param_names + trigger_values
|
||||
permitted << { url_variables: [:key, :value] }
|
||||
|
||||
ps = params.require(:hook).permit(*permitted).to_h
|
||||
|
||||
ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables)
|
||||
|
||||
if action_name == 'update' && ps.key?(:url_variables)
|
||||
supplied = ps[:url_variables]
|
||||
ps[:url_variables] = hook.url_variables.merge(supplied).compact
|
||||
end
|
||||
|
||||
ps
|
||||
end
|
||||
|
||||
def hook_param_names
|
||||
%i[enable_ssl_verification token url push_events_branch_filter]
|
||||
end
|
||||
|
||||
def destroy_hook(hook)
|
||||
result = WebHooks::DestroyService.new(current_user).execute(hook)
|
||||
|
||||
if result[:status] == :success
|
||||
flash[:notice] =
|
||||
if result[:async]
|
||||
format(_("%{hook_type} was scheduled for deletion"), hook_type: hook.model_name.human)
|
||||
else
|
||||
format(_("%{hook_type} was deleted"), hook_type: hook.model_name.human)
|
||||
end
|
||||
else
|
||||
flash[:alert] = result[:message]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
20
app/controllers/concerns/web_hooks/hook_execution_notice.rb
Normal file
20
app/controllers/concerns/web_hooks/hook_execution_notice.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WebHooks
|
||||
module HookExecutionNotice
|
||||
private
|
||||
|
||||
def set_hook_execution_notice(result)
|
||||
http_status = result[:http_status]
|
||||
message = result[:message]
|
||||
|
||||
if http_status && http_status >= 200 && http_status < 400
|
||||
flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
|
||||
elsif http_status
|
||||
flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
|
||||
else
|
||||
flash[:alert] = "Hook execution failed: #{message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::HookLogsController < Projects::ApplicationController
|
||||
include ::Integrations::HooksExecution
|
||||
include ::WebHooks::HookExecutionNotice
|
||||
|
||||
before_action :authorize_admin_project!
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::HooksController < Projects::ApplicationController
|
||||
include ::Integrations::HooksExecution
|
||||
include ::WebHooks::HookActions
|
||||
|
||||
# Authorize
|
||||
before_action :authorize_admin_project!
|
||||
|
|
|
@ -197,6 +197,9 @@ module UsersHelper
|
|||
banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' }
|
||||
return banned_badge if user.banned?
|
||||
|
||||
ldap_blocked_badge = { text: s_('AdminUsers|LDAP Blocked'), variant: 'danger' }
|
||||
return ldap_blocked_badge if user.ldap_blocked?
|
||||
|
||||
{ text: s_('AdminUsers|Blocked'), variant: 'danger' }
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
.gl-flex-grow-1
|
||||
%h3= s_('BackgroundMigrations|Background Migrations')
|
||||
%p.light.gl-mb-0
|
||||
- learnmore_link = help_page_path('development/database/batched_background_migrations')
|
||||
- learnmore_link = help_page_path('user/admin_area/monitoring/background_migrations')
|
||||
- learnmore_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: learnmore_link }
|
||||
= html_escape(s_('BackgroundMigrations|Background migrations are used to perform data migrations whenever a migration exceeds the time limits in our guidelines. %{linkStart}Learn more%{linkEnd}')) % { linkStart: learnmore_link_start, linkEnd: '</a>'.html_safe }
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
- page_title s_("UsageQuota|Usage")
|
||||
|
||||
= render_if_exists 'namespaces/free_user_cap/projects/usage_quota_limitations_banner'
|
||||
|
||||
= render Pajamas::AlertComponent.new(title: _('Repository usage recalculation started'),
|
||||
variant: :info,
|
||||
alert_options: { class: 'js-recalculation-started-alert gl-mt-4 gl-mb-5 gl-display-none' }) do |c|
|
||||
|
|
|
@ -120,7 +120,7 @@ module Gitlab
|
|||
stage: stage_value,
|
||||
extends: extends,
|
||||
rules: rules_value,
|
||||
job_variables: variables_entry.value_with_data,
|
||||
job_variables: variables_value.to_h,
|
||||
root_variables_inheritance: root_variables_inheritance,
|
||||
only: only_value,
|
||||
except: except_value,
|
||||
|
|
|
@ -18,9 +18,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def value
|
||||
@config.to_h do |key, data|
|
||||
[key.to_s, expand_data(data)[:value]]
|
||||
end
|
||||
@config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
|
||||
end
|
||||
|
||||
def self.default(**)
|
||||
|
@ -28,9 +26,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def value_with_data
|
||||
@config.to_h do |key, data|
|
||||
[key.to_s, expand_data(data)]
|
||||
end
|
||||
@config.to_h { |key, value| [key.to_s, expand_value(value)] }
|
||||
end
|
||||
|
||||
def use_value_data?
|
||||
|
@ -39,11 +35,11 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def expand_data(data)
|
||||
if data.is_a?(Hash)
|
||||
{ value: data[:value].to_s, description: data[:description] }.compact
|
||||
def expand_value(value)
|
||||
if value.is_a?(Hash)
|
||||
{ value: value[:value].to_s, description: value[:description] }
|
||||
else
|
||||
{ value: data.to_s }
|
||||
{ value: value.to_s, description: nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,24 +6,26 @@ module Gitlab
|
|||
module Helpers
|
||||
class << self
|
||||
def merge_variables(current_vars, new_vars)
|
||||
return current_vars if new_vars.blank?
|
||||
current_vars = transform_from_yaml_variables(current_vars)
|
||||
new_vars = transform_from_yaml_variables(new_vars)
|
||||
|
||||
current_vars = transform_to_array(current_vars) if current_vars.is_a?(Hash)
|
||||
new_vars = transform_to_array(new_vars) if new_vars.is_a?(Hash)
|
||||
|
||||
(new_vars + current_vars).uniq { |var| var[:key] }
|
||||
transform_to_yaml_variables(
|
||||
current_vars.merge(new_vars)
|
||||
)
|
||||
end
|
||||
|
||||
def transform_to_array(vars)
|
||||
vars.to_h.map do |key, data|
|
||||
if data.is_a?(Hash)
|
||||
{ key: key.to_s, **data.except(:key) }
|
||||
else
|
||||
{ key: key.to_s, value: data }
|
||||
end
|
||||
def transform_to_yaml_variables(vars)
|
||||
vars.to_h.map do |key, value|
|
||||
{ key: key.to_s, value: value, public: true }
|
||||
end
|
||||
end
|
||||
|
||||
def transform_from_yaml_variables(vars)
|
||||
return vars.stringify_keys.transform_values(&:to_s) if vars.is_a?(Hash)
|
||||
|
||||
vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] }
|
||||
end
|
||||
|
||||
def inherit_yaml_variables(from:, to:, inheritance:)
|
||||
merge_variables(apply_inheritance(from, inheritance), to)
|
||||
end
|
||||
|
@ -33,7 +35,7 @@ module Gitlab
|
|||
def apply_inheritance(variables, inheritance)
|
||||
case inheritance
|
||||
when true then variables
|
||||
when false then []
|
||||
when false then {}
|
||||
when Array then variables.select { |var| inheritance.include?(var[:key]) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def root_variables
|
||||
@root_variables ||= transform_to_array(variables)
|
||||
@root_variables ||= transform_to_yaml_variables(variables)
|
||||
end
|
||||
|
||||
def jobs
|
||||
|
@ -70,7 +70,7 @@ module Gitlab
|
|||
environment: job[:environment_name],
|
||||
coverage_regex: job[:coverage],
|
||||
# yaml_variables is calculated with using job_variables in Seed::Build
|
||||
job_variables: transform_to_array(job[:job_variables]),
|
||||
job_variables: transform_to_yaml_variables(job[:job_variables]),
|
||||
root_variables_inheritance: job[:root_variables_inheritance],
|
||||
needs_attributes: job.dig(:needs, :job),
|
||||
interruptible: job[:interruptible],
|
||||
|
@ -114,7 +114,7 @@ module Gitlab
|
|||
|
||||
Gitlab::Ci::Variables::Helpers.inherit_yaml_variables(
|
||||
from: root_variables,
|
||||
to: job[:job_variables],
|
||||
to: transform_to_yaml_variables(job[:job_variables]),
|
||||
inheritance: job.fetch(:root_variables_inheritance, true)
|
||||
)
|
||||
end
|
||||
|
@ -137,8 +137,8 @@ module Gitlab
|
|||
job[:release]
|
||||
end
|
||||
|
||||
def transform_to_array(variables)
|
||||
::Gitlab::Ci::Variables::Helpers.transform_to_array(variables)
|
||||
def transform_to_yaml_variables(variables)
|
||||
::Gitlab::Ci::Variables::Helpers.transform_to_yaml_variables(variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1056,9 +1056,6 @@ msgstr[1] ""
|
|||
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strong_start}%{release_count}%{strong_end} Release"
|
||||
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
|
||||
msgstr[0] ""
|
||||
|
@ -3123,6 +3120,9 @@ msgstr ""
|
|||
msgid "AdminUsers|It's you!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|LDAP Blocked"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21615,9 +21615,6 @@ msgstr ""
|
|||
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
|
||||
msgstr ""
|
||||
|
||||
msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
|
||||
msgstr ""
|
||||
|
||||
msgid "InviteMembersModal|Username or email address"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21642,9 +21639,6 @@ msgstr ""
|
|||
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "InviteMembers|Invite a group"
|
||||
msgstr ""
|
||||
|
||||
|
@ -45506,9 +45500,6 @@ msgstr ""
|
|||
msgid "Your profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your project has limited quotas and features"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
|
|||
visit admin_background_migrations_path
|
||||
|
||||
within '#content-body' do
|
||||
expect(page).to have_link('Learn more', href: help_page_path('development/database/batched_background_migrations'))
|
||||
expect(page).to have_link('Learn more', href: help_page_path('user/admin_area/monitoring/background_migrations'))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
|
|||
end
|
||||
|
||||
it 'displays a mini pipeline graph' do
|
||||
expect(page).to have_selector('[data-testid="commit-box-mini-graph"]')
|
||||
expect(page).to have_selector('[data-testid="commit-box-pipeline-mini-graph"]')
|
||||
|
||||
first('[data-testid="mini-pipeline-graph-dropdown"]').click
|
||||
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import createFlash from '~/flash';
|
||||
import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import { COMMIT_BOX_POLL_INTERVAL } from '~/projects/commit_box/info/constants';
|
||||
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
|
||||
import { mockPipelineStagesQueryResponse, mockStages } from './mock_data';
|
||||
import * as graphQlUtils from '~/pipelines/components/graph/utils';
|
||||
import {
|
||||
mockDownstreamQueryResponse,
|
||||
mockPipelineStagesQueryResponse,
|
||||
mockStages,
|
||||
mockUpstreamDownstreamQueryResponse,
|
||||
mockUpstreamQueryResponse,
|
||||
} from './mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
@ -17,61 +27,219 @@ Vue.use(VueApollo);
|
|||
describe('Commit box pipeline mini graph', () => {
|
||||
let wrapper;
|
||||
|
||||
const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph');
|
||||
const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream');
|
||||
const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
|
||||
|
||||
const downstreamHandler = jest.fn().mockResolvedValue(mockDownstreamQueryResponse);
|
||||
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
|
||||
const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
|
||||
const upstreamDownstreamHandler = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockUpstreamDownstreamQueryResponse);
|
||||
const upstreamHandler = jest.fn().mockResolvedValue(mockUpstreamQueryResponse);
|
||||
const advanceToNextFetch = () => {
|
||||
jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL);
|
||||
};
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
const handlers = [
|
||||
[getLinkedPipelinesQuery, {}],
|
||||
const fullPath = 'gitlab-org/gitlab';
|
||||
const iid = '315';
|
||||
const createMockApolloProvider = (handler = downstreamHandler) => {
|
||||
const requestHandlers = [
|
||||
[getLinkedPipelinesQuery, handler],
|
||||
[getPipelineStagesQuery, stagesHandler],
|
||||
];
|
||||
|
||||
return createMockApollo(requestHandlers);
|
||||
};
|
||||
|
||||
const createComponent = (handler) => {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(CommitBoxPipelineMiniGraph, {
|
||||
propsData: {
|
||||
stages: mockStages,
|
||||
...props,
|
||||
},
|
||||
apolloProvider: createMockApollo(handlers),
|
||||
provide: {
|
||||
fullPath,
|
||||
iid,
|
||||
dataMethod: 'graphql',
|
||||
graphqlResourceEtag: '/api/graphql:pipelines/id/320',
|
||||
},
|
||||
apolloProvider: createMockApolloProvider(handler),
|
||||
}),
|
||||
);
|
||||
|
||||
return waitForPromises();
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('linked pipelines', () => {
|
||||
describe('loading state', () => {
|
||||
it('should display loading state when loading', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
expect(findPipelineMiniGraph().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loaded state', () => {
|
||||
beforeEach(async () => {
|
||||
await createComponent();
|
||||
});
|
||||
|
||||
it('should display the mini pipeine graph', () => {
|
||||
expect(findMiniGraph().exists()).toBe(true);
|
||||
it('should not display loading state after the query is resolved', async () => {
|
||||
expect(findLoadingIcon().exists()).toBe(false);
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not display linked pipelines', () => {
|
||||
expect(findUpstream().exists()).toBe(false);
|
||||
expect(findDownstream().exists()).toBe(false);
|
||||
it('should display the pipeline mini graph', () => {
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when data is mismatched', () => {
|
||||
beforeEach(async () => {
|
||||
await createComponent({ props: { stages: [] } });
|
||||
describe('load upstream/downstream', () => {
|
||||
const samplePipeline = {
|
||||
__typename: expect.any(String),
|
||||
id: expect.any(String),
|
||||
path: expect.any(String),
|
||||
project: expect.any(Object),
|
||||
detailedStatus: expect.any(Object),
|
||||
};
|
||||
|
||||
it('formatted stages should be passed to the pipeline mini graph', async () => {
|
||||
const stage = mockStages[0];
|
||||
const expectedStages = [
|
||||
{
|
||||
name: stage.name,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: stage.status.id,
|
||||
icon: stage.status.icon,
|
||||
group: stage.status.group,
|
||||
},
|
||||
dropdown_path: stage.dropdown_path,
|
||||
title: stage.title,
|
||||
},
|
||||
];
|
||||
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findPipelineMiniGraph().props('stages')).toEqual(expectedStages);
|
||||
});
|
||||
|
||||
it('calls create flash with expected arguments', () => {
|
||||
it('should render a downstream pipeline only', async () => {
|
||||
createComponent(downstreamHandler);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
|
||||
const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
|
||||
|
||||
expect(downstreamPipelines).toEqual(expect.any(Array));
|
||||
expect(upstreamPipeline).toEqual(null);
|
||||
});
|
||||
|
||||
it('should pass the pipeline path prop for the counter badge', async () => {
|
||||
createComponent(downstreamHandler);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
const expectedPath = mockDownstreamQueryResponse.data.project.pipeline.path;
|
||||
const pipelinePath = findPipelineMiniGraph().props('pipelinePath');
|
||||
|
||||
expect(pipelinePath).toBe(expectedPath);
|
||||
});
|
||||
|
||||
it('should render an upstream pipeline only', async () => {
|
||||
createComponent(upstreamHandler);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
|
||||
const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
|
||||
|
||||
expect(upstreamPipeline).toEqual(samplePipeline);
|
||||
expect(downstreamPipelines).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should render downstream and upstream pipelines', async () => {
|
||||
createComponent(upstreamDownstreamHandler);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
|
||||
const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
|
||||
|
||||
expect(upstreamPipeline).toEqual(samplePipeline);
|
||||
expect(downstreamPipelines).toEqual(expect.arrayContaining([samplePipeline]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('error state', () => {
|
||||
it('createFlash should show if there is an error fetching the data', async () => {
|
||||
createComponent({ handler: failedHandler });
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'There was a problem handling the pipeline data.',
|
||||
captureError: true,
|
||||
error: new Error('Rest stages and graphQl stages must be the same length'),
|
||||
message: 'There was a problem fetching linked pipelines.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('polling', () => {
|
||||
it('polling interval is set for linked pipelines', () => {
|
||||
createComponent();
|
||||
|
||||
const expectedInterval = wrapper.vm.$apollo.queries.pipeline.options.pollInterval;
|
||||
|
||||
expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL);
|
||||
});
|
||||
|
||||
it('polling interval is set for pipeline stages', () => {
|
||||
createComponent();
|
||||
|
||||
const expectedInterval = wrapper.vm.$apollo.queries.pipelineStages.options.pollInterval;
|
||||
|
||||
expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL);
|
||||
});
|
||||
|
||||
it('polls for stages and linked pipelines', async () => {
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(stagesHandler).toHaveBeenCalledTimes(1);
|
||||
expect(downstreamHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
advanceToNextFetch();
|
||||
await waitForPromises();
|
||||
|
||||
expect(stagesHandler).toHaveBeenCalledTimes(2);
|
||||
expect(downstreamHandler).toHaveBeenCalledTimes(2);
|
||||
|
||||
advanceToNextFetch();
|
||||
await waitForPromises();
|
||||
|
||||
expect(stagesHandler).toHaveBeenCalledTimes(3);
|
||||
expect(downstreamHandler).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('toggles query polling with visibility check', async () => {
|
||||
jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
|
||||
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
|
||||
wrapper.vm.$apollo.queries.pipelineStages,
|
||||
);
|
||||
expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
|
||||
wrapper.vm.$apollo.queries.pipeline,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,116 +3,21 @@ export const mockStages = [
|
|||
name: 'build',
|
||||
title: 'build: passed',
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: 'success-409-409',
|
||||
icon: 'status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
tooltip: 'passed',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#build',
|
||||
details_path: '/root/ci-project/-/pipelines/318#build',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#build',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=build',
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
title: 'test: passed',
|
||||
status: {
|
||||
icon: 'status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
tooltip: 'passed',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#test',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#test',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test',
|
||||
},
|
||||
{
|
||||
name: 'test_two',
|
||||
title: 'test_two: passed',
|
||||
status: {
|
||||
icon: 'status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
tooltip: 'passed',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#test_two',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#test_two',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test_two',
|
||||
},
|
||||
{
|
||||
name: 'manual',
|
||||
title: 'manual: skipped',
|
||||
status: {
|
||||
icon: 'status_skipped',
|
||||
text: 'skipped',
|
||||
label: 'skipped',
|
||||
group: 'skipped',
|
||||
tooltip: 'skipped',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#manual',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png',
|
||||
action: {
|
||||
icon: 'play',
|
||||
title: 'Play all manual',
|
||||
path: '/root/ci-project/-/pipelines/611/stages/manual/play_manual',
|
||||
method: 'post',
|
||||
button_title: 'Play all manual',
|
||||
},
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#manual',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=manual',
|
||||
},
|
||||
{
|
||||
name: 'deploy',
|
||||
title: 'deploy: passed',
|
||||
status: {
|
||||
icon: 'status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
tooltip: 'passed',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#deploy',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#deploy',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=deploy',
|
||||
},
|
||||
{
|
||||
name: 'qa',
|
||||
title: 'qa: passed',
|
||||
status: {
|
||||
icon: 'status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
tooltip: 'passed',
|
||||
has_details: true,
|
||||
details_path: '/root/ci-project/-/pipelines/611#qa',
|
||||
illustration: null,
|
||||
favicon:
|
||||
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
|
||||
},
|
||||
path: '/root/ci-project/-/pipelines/611#qa',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa',
|
||||
path: '/root/ci-project/-/pipelines/318#build',
|
||||
dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=build',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -161,3 +66,109 @@ export const mockPipelineStatusResponse = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockDownstreamQueryResponse = {
|
||||
data: {
|
||||
project: {
|
||||
id: '1',
|
||||
pipeline: {
|
||||
path: '/root/ci-project/-/pipelines/790',
|
||||
id: 'pipeline-1',
|
||||
downstream: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'gid://gitlab/Ci::Pipeline/612',
|
||||
path: '/root/job-log-sections/-/pipelines/612',
|
||||
project: { id: '1', name: 'job-log-sections', __typename: 'Project' },
|
||||
detailedStatus: {
|
||||
id: 'status-1',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
],
|
||||
__typename: 'PipelineConnection',
|
||||
},
|
||||
upstream: null,
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockUpstreamDownstreamQueryResponse = {
|
||||
data: {
|
||||
project: {
|
||||
id: '1',
|
||||
pipeline: {
|
||||
id: 'pipeline-1',
|
||||
path: '/root/ci-project/-/pipelines/790',
|
||||
downstream: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'gid://gitlab/Ci::Pipeline/612',
|
||||
path: '/root/job-log-sections/-/pipelines/612',
|
||||
project: { id: '1', name: 'job-log-sections', __typename: 'Project' },
|
||||
detailedStatus: {
|
||||
id: 'status-1',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
],
|
||||
__typename: 'PipelineConnection',
|
||||
},
|
||||
upstream: {
|
||||
id: 'gid://gitlab/Ci::Pipeline/610',
|
||||
path: '/root/trigger-downstream/-/pipelines/610',
|
||||
project: { id: '1', name: 'trigger-downstream', __typename: 'Project' },
|
||||
detailedStatus: {
|
||||
id: 'status-1',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockUpstreamQueryResponse = {
|
||||
data: {
|
||||
project: {
|
||||
id: '1',
|
||||
pipeline: {
|
||||
id: 'pipeline-1',
|
||||
path: '/root/ci-project/-/pipelines/790',
|
||||
downstream: {
|
||||
nodes: [],
|
||||
__typename: 'PipelineConnection',
|
||||
},
|
||||
upstream: {
|
||||
id: 'gid://gitlab/Ci::Pipeline/610',
|
||||
path: '/root/trigger-downstream/-/pipelines/610',
|
||||
project: { id: '1', name: 'trigger-downstream', __typename: 'Project' },
|
||||
detailedStatus: {
|
||||
id: 'status-1',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
label: 'passed',
|
||||
__typename: 'DetailedStatus',
|
||||
},
|
||||
__typename: 'Pipeline',
|
||||
},
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { GlAlert, GlSprintf } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import UserLimitNotification from '~/invite_members/components/user_limit_notification.vue';
|
||||
|
||||
import {
|
||||
REACHED_LIMIT_MESSAGE,
|
||||
REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
|
||||
} from '~/invite_members/constants';
|
||||
|
||||
import { REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE } from '~/invite_members/constants';
|
||||
import { freeUsersLimit, membersCount } from '../mock_data/member_modal';
|
||||
|
||||
const WARNING_ALERT_TITLE = 'You only have space for 2 more members in name';
|
||||
|
@ -52,22 +47,6 @@ describe('UserLimitNotification', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when close to limit within a personal namepace', () => {
|
||||
beforeEach(() => {
|
||||
createComponent(true, false, { membersCount: 3, userNamespace: true });
|
||||
});
|
||||
|
||||
it('renders the limit for a personal namespace', () => {
|
||||
const alert = findAlert();
|
||||
|
||||
expect(alert.attributes('title')).toEqual(WARNING_ALERT_TITLE);
|
||||
|
||||
expect(alert.text()).toEqual(
|
||||
'To make more space, you can remove members who no longer need access.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when close to limit within a group', () => {
|
||||
it("renders user's limit notification", () => {
|
||||
createComponent(true, false, { membersCount: 3 });
|
||||
|
@ -91,19 +70,5 @@ describe('UserLimitNotification', () => {
|
|||
expect(alert.attributes('title')).toEqual("You've reached your 5 members limit for name");
|
||||
expect(alert.text()).toEqual(REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE);
|
||||
});
|
||||
|
||||
describe('when free user namespace', () => {
|
||||
it("renders user's limit notification", () => {
|
||||
createComponent(true, true, { userNamespace: true });
|
||||
|
||||
const alert = findAlert();
|
||||
|
||||
expect(alert.attributes('title')).toEqual(
|
||||
"You've reached your 5 members limit for your personal projects",
|
||||
);
|
||||
|
||||
expect(alert.text()).toEqual(REACHED_LIMIT_MESSAGE);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import { PIPELINE_FAILURE } from '~/pipeline_editor/constants';
|
||||
import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
describe('Pipeline Status', () => {
|
||||
let wrapper;
|
||||
let mockApollo;
|
||||
let mockLinkedPipelinesQuery;
|
||||
|
||||
const createComponent = ({ hasStages = true, options } = {}) => {
|
||||
wrapper = shallowMount(PipelineEditorMiniGraph, {
|
||||
provide: {
|
||||
dataMethod: 'graphql',
|
||||
projectFullPath: mockProjectFullPath,
|
||||
},
|
||||
propsData: {
|
||||
pipeline: mockProjectPipeline({ hasStages }).pipeline,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
const createComponentWithApollo = (hasStages = true) => {
|
||||
const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]];
|
||||
mockApollo = createMockApollo(handlers);
|
||||
|
||||
createComponent({
|
||||
hasStages,
|
||||
options: {
|
||||
apolloProvider: mockApollo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
|
||||
|
||||
beforeEach(() => {
|
||||
mockLinkedPipelinesQuery = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockLinkedPipelinesQuery.mockReset();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('when there are stages', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders pipeline mini graph', () => {
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no stages', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ hasStages: false });
|
||||
});
|
||||
|
||||
it('does not render pipeline mini graph', () => {
|
||||
expect(findPipelineMiniGraph().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when querying upstream and downstream pipelines', () => {
|
||||
describe('when query succeeds', () => {
|
||||
beforeEach(() => {
|
||||
mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines());
|
||||
createComponentWithApollo();
|
||||
});
|
||||
|
||||
it('should call the query with the correct variables', () => {
|
||||
expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1);
|
||||
expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({
|
||||
fullPath: mockProjectFullPath,
|
||||
iid: mockProjectPipeline().pipeline.iid,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when query fails', () => {
|
||||
beforeEach(async () => {
|
||||
mockLinkedPipelinesQuery.mockRejectedValue(new Error());
|
||||
createComponentWithApollo();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('should emit an error event when query fails', async () => {
|
||||
expect(wrapper.emitted('showError')).toHaveLength(1);
|
||||
expect(wrapper.emitted('showError')[0]).toEqual([
|
||||
{
|
||||
type: PIPELINE_FAILURE,
|
||||
reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
|
|||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
|
||||
import { PIPELINE_FAILURE } from '~/pipeline_editor/constants';
|
||||
import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data';
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import LinkedPipelinesMiniList from '~/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue';
|
||||
import mockData from './linked_pipelines_mock_data';
|
||||
|
||||
describe('Linked pipeline mini list', () => {
|
||||
let wrapper;
|
||||
|
||||
const findCiIcon = () => wrapper.findComponent(CiIcon);
|
||||
const findCiIcons = () => wrapper.findAllComponents(CiIcon);
|
||||
const findLinkedPipelineCounter = () => wrapper.find('[data-testid="linked-pipeline-counter"]');
|
||||
const findLinkedPipelineMiniItem = () =>
|
||||
wrapper.find('[data-testid="linked-pipeline-mini-item"]');
|
||||
const findLinkedPipelineMiniItems = () =>
|
||||
wrapper.findAll('[data-testid="linked-pipeline-mini-item"]');
|
||||
const findLinkedPipelineMiniList = () => wrapper.findComponent(LinkedPipelinesMiniList);
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = mount(LinkedPipelinesMiniList, {
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
},
|
||||
propsData: {
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
describe('when passed an upstream pipeline as prop', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
triggeredBy: [mockData.triggered_by],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('should render one linked pipeline item', () => {
|
||||
expect(findLinkedPipelineMiniItem().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render a linked pipeline with the correct href', () => {
|
||||
expect(findLinkedPipelineMiniItem().exists()).toBe(true);
|
||||
|
||||
expect(findLinkedPipelineMiniItem().attributes('href')).toBe(
|
||||
'/gitlab-org/gitlab-foss/-/pipelines/129',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render one ci status icon', () => {
|
||||
expect(findCiIcon().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render a borderless ci-icon', () => {
|
||||
expect(findCiIcon().exists()).toBe(true);
|
||||
|
||||
expect(findCiIcon().props('isBorderless')).toBe(true);
|
||||
expect(findCiIcon().classes('borderless')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render a ci-icon with a custom border class', () => {
|
||||
expect(findCiIcon().exists()).toBe(true);
|
||||
|
||||
expect(findCiIcon().classes('gl-border')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render the correct ci status icon', () => {
|
||||
expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have an activated tooltip', () => {
|
||||
expect(findLinkedPipelineMiniItem().exists()).toBe(true);
|
||||
const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
|
||||
|
||||
expect(tooltip.value.title).toBe('GitLabCE - running');
|
||||
});
|
||||
|
||||
it('should correctly set is-upstream', () => {
|
||||
expect(findLinkedPipelineMiniList().exists()).toBe(true);
|
||||
|
||||
expect(findLinkedPipelineMiniList().classes('is-upstream')).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly compute shouldRenderCounter', () => {
|
||||
expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(false);
|
||||
});
|
||||
|
||||
it('should not render the pipeline counter', () => {
|
||||
expect(findLinkedPipelineCounter().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when passed downstream pipelines as props', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
triggered: mockData.triggered,
|
||||
pipelinePath: 'my/pipeline/path',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('should render three linked pipeline items', () => {
|
||||
expect(findLinkedPipelineMiniItems().exists()).toBe(true);
|
||||
expect(findLinkedPipelineMiniItems().length).toBe(3);
|
||||
});
|
||||
|
||||
it('should render three ci status icons', () => {
|
||||
expect(findCiIcons().exists()).toBe(true);
|
||||
expect(findCiIcons().length).toBe(3);
|
||||
});
|
||||
|
||||
it('should render the correct ci status icon', () => {
|
||||
expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have an activated tooltip', () => {
|
||||
expect(findLinkedPipelineMiniItem().exists()).toBe(true);
|
||||
const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
|
||||
|
||||
expect(tooltip.value.title).toBe('GitLabCE - running');
|
||||
});
|
||||
|
||||
it('should correctly set is-downstream', () => {
|
||||
expect(findLinkedPipelineMiniList().exists()).toBe(true);
|
||||
|
||||
expect(findLinkedPipelineMiniList().classes('is-downstream')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render a borderless ci-icon', () => {
|
||||
expect(findCiIcon().exists()).toBe(true);
|
||||
|
||||
expect(findCiIcon().props('isBorderless')).toBe(true);
|
||||
expect(findCiIcon().classes('borderless')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render a ci-icon with a custom border class', () => {
|
||||
expect(findCiIcon().exists()).toBe(true);
|
||||
|
||||
expect(findCiIcon().classes('gl-border')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render the pipeline counter', () => {
|
||||
expect(findLinkedPipelineCounter().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly compute shouldRenderCounter', () => {
|
||||
expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly trim linkedPipelines', () => {
|
||||
expect(findLinkedPipelineMiniList().props('triggered').length).toBe(6);
|
||||
expect(findLinkedPipelineMiniList().vm.linkedPipelinesTrimmed.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the correct pipeline path', () => {
|
||||
expect(findLinkedPipelineCounter().exists()).toBe(true);
|
||||
|
||||
expect(findLinkedPipelineCounter().attributes('href')).toBe('my/pipeline/path');
|
||||
});
|
||||
|
||||
it('should render the correct counterTooltipText', () => {
|
||||
expect(findLinkedPipelineCounter().exists()).toBe(true);
|
||||
const tooltip = getBinding(findLinkedPipelineCounter().element, 'gl-tooltip');
|
||||
|
||||
expect(tooltip.value.title).toBe(findLinkedPipelineMiniList().vm.counterTooltipText);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,407 @@
|
|||
export default {
|
||||
triggered_by: {
|
||||
id: 129,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/129',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/129',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: '7-5-stable',
|
||||
path: '/gitlab-org/gitlab-foss/commits/7-5-stable',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: '23433d4d8b20d7e45c103d0b6048faad38a130ab',
|
||||
short_id: '23433d4d',
|
||||
title: 'Version 7.5.0.rc1',
|
||||
created_at: '2014-11-17T15:44:14.000+01:00',
|
||||
parent_ids: ['30ac909f30f58d319b42ed1537664483894b18cd'],
|
||||
message: 'Version 7.5.0.rc1\n',
|
||||
author_name: 'Jacob Vosmaer',
|
||||
author_email: 'contact@jacobvosmaer.nl',
|
||||
authored_date: '2014-11-17T15:44:14.000+01:00',
|
||||
committer_name: 'Jacob Vosmaer',
|
||||
committer_email: 'contact@jacobvosmaer.nl',
|
||||
committed_date: '2014-11-17T15:44:14.000+01:00',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/e66d11c0eedf8c07b3b18fca46599807?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/129/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/129/cancel',
|
||||
created_at: '2017-05-24T14:46:20.090Z',
|
||||
updated_at: '2017-05-24T14:46:29.906Z',
|
||||
},
|
||||
triggered: [
|
||||
{
|
||||
id: 132,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/132',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
short_id: 'b9d58c4c',
|
||||
title: 'getting user keys publically through http without any authentication, the github…',
|
||||
created_at: '2013-10-03T12:50:33.000+05:30',
|
||||
parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
|
||||
message:
|
||||
'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
|
||||
author_name: 'devaroop',
|
||||
author_email: 'devaroop123@yahoo.co.in',
|
||||
authored_date: '2013-10-02T20:39:29.000+05:30',
|
||||
committer_name: 'devaroop',
|
||||
committer_email: 'devaroop123@yahoo.co.in',
|
||||
committed_date: '2013-10-03T12:50:33.000+05:30',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
|
||||
created_at: '2017-05-24T14:46:24.644Z',
|
||||
updated_at: '2017-05-24T14:48:55.226Z',
|
||||
},
|
||||
{
|
||||
id: 133,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/133',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
short_id: 'b6bd4856',
|
||||
title: 'getting user keys publically through http without any authentication, the github…',
|
||||
created_at: '2013-10-02T20:39:29.000+05:30',
|
||||
parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
|
||||
message:
|
||||
'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
|
||||
author_name: 'devaroop',
|
||||
author_email: 'devaroop123@yahoo.co.in',
|
||||
authored_date: '2013-10-02T20:39:29.000+05:30',
|
||||
committer_name: 'devaroop',
|
||||
committer_email: 'devaroop123@yahoo.co.in',
|
||||
committed_date: '2013-10-02T20:39:29.000+05:30',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
|
||||
created_at: '2017-05-24T14:46:24.648Z',
|
||||
updated_at: '2017-05-24T14:48:59.673Z',
|
||||
},
|
||||
{
|
||||
id: 130,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/130',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
short_id: '6d7ced4a',
|
||||
title: 'Whitespace fixes to patch',
|
||||
created_at: '2013-10-08T13:53:22.000-05:00',
|
||||
parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
|
||||
message: 'Whitespace fixes to patch\n',
|
||||
author_name: 'Dale Hamel',
|
||||
author_email: 'dale.hamel@srvthe.net',
|
||||
authored_date: '2013-10-08T13:53:22.000-05:00',
|
||||
committer_name: 'Dale Hamel',
|
||||
committer_email: 'dale.hamel@invenia.ca',
|
||||
committed_date: '2013-10-08T13:53:22.000-05:00',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
|
||||
created_at: '2017-05-24T14:46:24.630Z',
|
||||
updated_at: '2017-05-24T14:49:45.091Z',
|
||||
},
|
||||
{
|
||||
id: 131,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/132',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
short_id: 'b9d58c4c',
|
||||
title: 'getting user keys publically through http without any authentication, the github…',
|
||||
created_at: '2013-10-03T12:50:33.000+05:30',
|
||||
parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
|
||||
message:
|
||||
'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
|
||||
author_name: 'devaroop',
|
||||
author_email: 'devaroop123@yahoo.co.in',
|
||||
authored_date: '2013-10-02T20:39:29.000+05:30',
|
||||
committer_name: 'devaroop',
|
||||
committer_email: 'devaroop123@yahoo.co.in',
|
||||
committed_date: '2013-10-03T12:50:33.000+05:30',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
|
||||
created_at: '2017-05-24T14:46:24.644Z',
|
||||
updated_at: '2017-05-24T14:48:55.226Z',
|
||||
},
|
||||
{
|
||||
id: 134,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/133',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
short_id: 'b6bd4856',
|
||||
title: 'getting user keys publically through http without any authentication, the github…',
|
||||
created_at: '2013-10-02T20:39:29.000+05:30',
|
||||
parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
|
||||
message:
|
||||
'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
|
||||
author_name: 'devaroop',
|
||||
author_email: 'devaroop123@yahoo.co.in',
|
||||
authored_date: '2013-10-02T20:39:29.000+05:30',
|
||||
committer_name: 'devaroop',
|
||||
committer_email: 'devaroop123@yahoo.co.in',
|
||||
committed_date: '2013-10-02T20:39:29.000+05:30',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
|
||||
created_at: '2017-05-24T14:46:24.648Z',
|
||||
updated_at: '2017-05-24T14:48:59.673Z',
|
||||
},
|
||||
{
|
||||
id: 135,
|
||||
active: true,
|
||||
path: '/gitlab-org/gitlab-foss/-/pipelines/130',
|
||||
project: {
|
||||
name: 'GitLabCE',
|
||||
},
|
||||
details: {
|
||||
status: {
|
||||
icon: 'status_running',
|
||||
text: 'running',
|
||||
label: 'running',
|
||||
group: 'running',
|
||||
has_details: true,
|
||||
details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
|
||||
favicon:
|
||||
'/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
latest: false,
|
||||
triggered: false,
|
||||
stuck: false,
|
||||
yaml_errors: false,
|
||||
retryable: true,
|
||||
cancelable: true,
|
||||
},
|
||||
ref: {
|
||||
name: 'crowd',
|
||||
path: '/gitlab-org/gitlab-foss/commits/crowd',
|
||||
tag: false,
|
||||
branch: true,
|
||||
},
|
||||
commit: {
|
||||
id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
short_id: '6d7ced4a',
|
||||
title: 'Whitespace fixes to patch',
|
||||
created_at: '2013-10-08T13:53:22.000-05:00',
|
||||
parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
|
||||
message: 'Whitespace fixes to patch\n',
|
||||
author_name: 'Dale Hamel',
|
||||
author_email: 'dale.hamel@srvthe.net',
|
||||
authored_date: '2013-10-08T13:53:22.000-05:00',
|
||||
committer_name: 'Dale Hamel',
|
||||
committer_email: 'dale.hamel@invenia.ca',
|
||||
committed_date: '2013-10-08T13:53:22.000-05:00',
|
||||
author_gravatar_url:
|
||||
'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
|
||||
commit_url:
|
||||
'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
|
||||
},
|
||||
retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
|
||||
cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
|
||||
created_at: '2017-05-24T14:46:24.630Z',
|
||||
updated_at: '2017-05-24T14:49:45.091Z',
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,149 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
|
||||
import mockLinkedPipelines from './linked_pipelines_mock_data';
|
||||
|
||||
const mockStages = pipelines[0].details.stages;
|
||||
|
||||
describe('Pipeline Mini Graph', () => {
|
||||
let wrapper;
|
||||
|
||||
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
|
||||
const findPipelineStages = () => wrapper.findComponent(PipelineStages);
|
||||
|
||||
const findLinkedPipelineUpstream = () =>
|
||||
wrapper.findComponent('[data-testid="pipeline-mini-graph-upstream"]');
|
||||
const findLinkedPipelineDownstream = () =>
|
||||
wrapper.findComponent('[data-testid="pipeline-mini-graph-downstream"]');
|
||||
const findDownstreamArrowIcon = () => wrapper.find('[data-testid="downstream-arrow-icon"]');
|
||||
const findUpstreamArrowIcon = () => wrapper.find('[data-testid="upstream-arrow-icon"]');
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = mount(PipelineMiniGraph, {
|
||||
propsData: {
|
||||
stages: mockStages,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
describe('rendered state without upstream or downstream pipelines', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('should render the pipeline stages', () => {
|
||||
expect(findPipelineStages().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the correct props', () => {
|
||||
expect(findPipelineMiniGraph().props()).toMatchObject({
|
||||
downstreamPipelines: [],
|
||||
isMergeTrain: false,
|
||||
pipelinePath: '',
|
||||
stages: expect.any(Array),
|
||||
stagesClass: '',
|
||||
updateDropdown: false,
|
||||
upstreamPipeline: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no linked pipelines', () => {
|
||||
expect(findLinkedPipelineDownstream().exists()).toBe(false);
|
||||
expect(findLinkedPipelineUpstream().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not render arrow icons', () => {
|
||||
expect(findUpstreamArrowIcon().exists()).toBe(false);
|
||||
expect(findDownstreamArrowIcon().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('triggers events in "action request complete"', () => {
|
||||
createComponent();
|
||||
|
||||
findPipelineMiniGraph(0).vm.$emit('pipelineActionRequestComplete');
|
||||
findPipelineMiniGraph(1).vm.$emit('pipelineActionRequestComplete');
|
||||
|
||||
expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendered state with upstream pipeline', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
upstreamPipeline: mockLinkedPipelines.triggered_by,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('should have the correct props', () => {
|
||||
expect(findPipelineMiniGraph().props()).toMatchObject({
|
||||
downstreamPipelines: [],
|
||||
isMergeTrain: false,
|
||||
pipelinePath: '',
|
||||
stages: expect.any(Array),
|
||||
stagesClass: '',
|
||||
updateDropdown: false,
|
||||
upstreamPipeline: expect.any(Object),
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the upstream linked pipelines mini list only', () => {
|
||||
expect(findLinkedPipelineUpstream().exists()).toBe(true);
|
||||
expect(findLinkedPipelineDownstream().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should render an upstream arrow icon only', () => {
|
||||
expect(findDownstreamArrowIcon().exists()).toBe(false);
|
||||
expect(findUpstreamArrowIcon().exists()).toBe(true);
|
||||
expect(findUpstreamArrowIcon().props('name')).toBe('long-arrow');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendered state with downstream pipelines', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
downstreamPipelines: mockLinkedPipelines.triggered,
|
||||
pipelinePath: 'my/pipeline/path',
|
||||
});
|
||||
});
|
||||
|
||||
it('should have the correct props', () => {
|
||||
expect(findPipelineMiniGraph().props()).toMatchObject({
|
||||
downstreamPipelines: expect.any(Array),
|
||||
isMergeTrain: false,
|
||||
pipelinePath: 'my/pipeline/path',
|
||||
stages: expect.any(Array),
|
||||
stagesClass: '',
|
||||
updateDropdown: false,
|
||||
upstreamPipeline: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it('should render the downstream linked pipelines mini list only', () => {
|
||||
expect(findLinkedPipelineDownstream().exists()).toBe(true);
|
||||
expect(findLinkedPipelineUpstream().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should render a downstream arrow icon only', () => {
|
||||
expect(findUpstreamArrowIcon().exists()).toBe(false);
|
||||
expect(findDownstreamArrowIcon().exists()).toBe(true);
|
||||
expect(findDownstreamArrowIcon().props('name')).toBe('long-arrow');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
|
||||
import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue';
|
||||
import eventHub from '~/pipelines/event_hub';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { stageReply } from '../../mock_data';
|
|
@ -1,18 +1,18 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
|
||||
import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue';
|
||||
import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
|
||||
|
||||
const mockStages = pipelines[0].details.stages;
|
||||
|
||||
describe('Pipeline Mini Graph', () => {
|
||||
describe('Pipeline Stages', () => {
|
||||
let wrapper;
|
||||
|
||||
const findPipelineStages = () => wrapper.findAll(PipelineStage);
|
||||
const findPipelineStagesAt = (i) => findPipelineStages().at(i);
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(PipelineMiniGraph, {
|
||||
wrapper = shallowMount(PipelineStages, {
|
||||
propsData: {
|
||||
stages: mockStages,
|
||||
...props,
|
|
@ -3,7 +3,7 @@ import { GlTableLite } from '@gitlab/ui';
|
|||
import { mount } from '@vue/test-utils';
|
||||
import fixture from 'test_fixtures/pipelines/pipelines.json';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import PipelineOperations from '~/pipelines/components/pipelines_list/pipeline_operations.vue';
|
||||
import PipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_triggerer.vue';
|
||||
import PipelineUrl from '~/pipelines/components/pipelines_list/pipeline_url.vue';
|
||||
|
@ -113,40 +113,28 @@ describe('Pipelines Table', () => {
|
|||
});
|
||||
|
||||
describe('stages cell', () => {
|
||||
it('should render a pipeline mini graph', () => {
|
||||
it('should render pipeline mini graph', () => {
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should render the right number of stages', () => {
|
||||
const stagesLength = pipeline.details.stages.length;
|
||||
expect(
|
||||
findPipelineMiniGraph().findAll('[data-testid="mini-pipeline-graph-dropdown"]'),
|
||||
).toHaveLength(stagesLength);
|
||||
expect(findPipelineMiniGraph().props('stages').length).toBe(stagesLength);
|
||||
});
|
||||
|
||||
describe('when pipeline does not have stages', () => {
|
||||
beforeEach(() => {
|
||||
pipeline = createMockPipeline();
|
||||
pipeline.details.stages = null;
|
||||
pipeline.details.stages = [];
|
||||
|
||||
createComponent({ pipelines: [pipeline] });
|
||||
});
|
||||
|
||||
it('stages are not rendered', () => {
|
||||
expect(findPipelineMiniGraph().exists()).toBe(false);
|
||||
expect(findPipelineMiniGraph().props('stages')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update dropdown', () => {
|
||||
expect(findPipelineMiniGraph().props('updateDropdown')).toBe(false);
|
||||
});
|
||||
|
||||
it('when update graph dropdown is set, should update graph dropdown', () => {
|
||||
createComponent({ pipelines: [pipeline], updateGraphDropdown: true });
|
||||
|
||||
expect(findPipelineMiniGraph().props('updateDropdown')).toBe(true);
|
||||
});
|
||||
|
||||
it('when action request is complete, should refresh table', () => {
|
||||
findPipelineMiniGraph().vm.$emit('pipelineActionRequestComplete');
|
||||
|
||||
|
|
|
@ -4,9 +4,8 @@ import axios from 'axios';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { trimText } from 'helpers/text_helper';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
|
||||
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
|
||||
import PipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
|
||||
import MRWidgetPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
|
||||
import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
|
||||
import { SUCCESS } from '~/vue_merge_request_widget/constants';
|
||||
import mockData from '../mock_data';
|
||||
|
||||
|
@ -30,14 +29,13 @@ describe('MRWidgetPipeline', () => {
|
|||
const findPipelineInfoContainer = () => wrapper.findByTestId('pipeline-info-container');
|
||||
const findCommitLink = () => wrapper.findByTestId('commit-link');
|
||||
const findPipelineFinishedAt = () => wrapper.findByTestId('finished-at');
|
||||
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
|
||||
const findAllPipelineStages = () => wrapper.findAllComponents(PipelineStage);
|
||||
const findPipelineCoverage = () => wrapper.findByTestId('pipeline-coverage');
|
||||
const findPipelineCoverageDelta = () => wrapper.findByTestId('pipeline-coverage-delta');
|
||||
const findPipelineCoverageTooltipText = () =>
|
||||
wrapper.findByTestId('pipeline-coverage-tooltip').text();
|
||||
const findPipelineCoverageDeltaTooltipText = () =>
|
||||
wrapper.findByTestId('pipeline-coverage-delta-tooltip').text();
|
||||
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
|
||||
const findMonitoringPipelineMessage = () => wrapper.findByTestId('monitoring-pipeline-message');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
|
||||
|
@ -45,7 +43,7 @@ describe('MRWidgetPipeline', () => {
|
|||
|
||||
const createWrapper = (props = {}, mountFn = shallowMount) => {
|
||||
wrapper = extendedWrapper(
|
||||
mountFn(PipelineComponent, {
|
||||
mountFn(MRWidgetPipelineComponent, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
|
@ -106,8 +104,10 @@ describe('MRWidgetPipeline', () => {
|
|||
});
|
||||
|
||||
it('should render pipeline graph', () => {
|
||||
const stagesCount = mockData.pipeline.details.stages.length;
|
||||
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
expect(findAllPipelineStages()).toHaveLength(mockData.pipeline.details.stages.length);
|
||||
expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount);
|
||||
});
|
||||
|
||||
describe('should render pipeline coverage information', () => {
|
||||
|
@ -176,15 +176,11 @@ describe('MRWidgetPipeline', () => {
|
|||
expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label);
|
||||
});
|
||||
|
||||
it('should render pipeline graph with correct styles', () => {
|
||||
it('should render pipeline graph', () => {
|
||||
const stagesCount = mockData.pipeline.details.stages.length;
|
||||
|
||||
expect(findPipelineMiniGraph().exists()).toBe(true);
|
||||
expect(findPipelineMiniGraph().findAll('.mr-widget-pipeline-stages')).toHaveLength(
|
||||
stagesCount,
|
||||
);
|
||||
|
||||
expect(findAllPipelineStages()).toHaveLength(stagesCount);
|
||||
expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount);
|
||||
});
|
||||
|
||||
it('should render coverage information', () => {
|
||||
|
|
|
@ -48,7 +48,8 @@ describe('MR Widget', () => {
|
|||
const fetchCollapsedData = jest.fn().mockReturnValue(() => Promise.reject());
|
||||
createComponent({ propsData: { fetchCollapsedData } });
|
||||
await waitForPromises();
|
||||
expect(wrapper.vm.error).toBe('Failed to load');
|
||||
expect(wrapper.findByText('Failed to load').exists()).toBe(true);
|
||||
expect(findStatusIcon().props()).toMatchObject({ iconName: 'failed', isLoading: false });
|
||||
});
|
||||
|
||||
it('displays loading icon until request is made and then displays status icon when the request is complete', async () => {
|
||||
|
|
|
@ -155,7 +155,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
|
|||
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
|
||||
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
|
||||
only: { refs: %w(branches tags) },
|
||||
job_variables: { 'VAR' => { value: 'job' } },
|
||||
job_variables: { 'VAR' => 'job' },
|
||||
root_variables_inheritance: true,
|
||||
after_script: [],
|
||||
ignore: false,
|
||||
|
@ -215,7 +215,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
|
|||
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
|
||||
stage: 'test',
|
||||
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
|
||||
job_variables: { 'VAR' => { value: 'job' } },
|
||||
job_variables: { 'VAR' => 'job' },
|
||||
root_variables_inheritance: true,
|
||||
ignore: false,
|
||||
after_script: ['make clean'],
|
||||
|
|
|
@ -97,15 +97,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
let(:attributes) do
|
||||
{ name: 'rspec',
|
||||
ref: 'master',
|
||||
job_variables: [{ key: 'VAR1', value: 'var 1' },
|
||||
{ key: 'VAR2', value: 'var 2' }],
|
||||
job_variables: [{ key: 'VAR1', value: 'var 1', public: true },
|
||||
{ key: 'VAR2', value: 'var 2', public: true }],
|
||||
rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
|
||||
end
|
||||
|
||||
it do
|
||||
is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
|
||||
{ key: 'VAR3', value: 'var 3' },
|
||||
{ key: 'VAR2', value: 'var 2' }])
|
||||
is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true },
|
||||
{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -114,13 +114,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
{
|
||||
name: 'rspec',
|
||||
ref: 'master',
|
||||
job_variables: [{ key: 'VARIABLE', value: 'value' }],
|
||||
job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
|
||||
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
|
||||
it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
|
||||
it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
|
||||
end
|
||||
|
||||
context 'with cache:key' do
|
||||
|
@ -257,19 +257,19 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
let(:attributes) do
|
||||
{ name: 'rspec',
|
||||
ref: 'master',
|
||||
yaml_variables: [{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' }],
|
||||
job_variables: [{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' }],
|
||||
yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }],
|
||||
job_variables: [{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }],
|
||||
root_variables_inheritance: root_variables_inheritance }
|
||||
end
|
||||
|
||||
context 'when the pipeline has variables' do
|
||||
let(:root_variables) do
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1' },
|
||||
{ key: 'VAR2', value: 'var pipeline 2' },
|
||||
{ key: 'VAR3', value: 'var pipeline 3' },
|
||||
{ key: 'VAR4', value: 'new var pipeline 4' }]
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
|
||||
{ key: 'VAR2', value: 'var pipeline 2', public: true },
|
||||
{ key: 'VAR3', value: 'var pipeline 3', public: true },
|
||||
{ key: 'VAR4', value: 'new var pipeline 4', public: true }]
|
||||
end
|
||||
|
||||
context 'when root_variables_inheritance is true' do
|
||||
|
@ -277,10 +277,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
|
||||
it 'returns calculated yaml variables' do
|
||||
expect(subject[:yaml_variables]).to match_array(
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1' },
|
||||
{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' },
|
||||
{ key: 'VAR4', value: 'new var pipeline 4' }]
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
|
||||
{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true },
|
||||
{ key: 'VAR4', value: 'new var pipeline 4', public: true }]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -290,8 +290,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
|
||||
it 'returns job variables' do
|
||||
expect(subject[:yaml_variables]).to match_array(
|
||||
[{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' }]
|
||||
[{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -301,9 +301,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
|
||||
it 'returns calculated yaml variables' do
|
||||
expect(subject[:yaml_variables]).to match_array(
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1' },
|
||||
{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' }]
|
||||
[{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
|
||||
{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -314,8 +314,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
|
||||
it 'returns seed yaml variables' do
|
||||
expect(subject[:yaml_variables]).to match_array(
|
||||
[{ key: 'VAR2', value: 'var 2' },
|
||||
{ key: 'VAR3', value: 'var 3' }])
|
||||
[{ key: 'VAR2', value: 'var 2', public: true },
|
||||
{ key: 'VAR3', value: 'var 3', public: true }])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -324,8 +324,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
let(:attributes) do
|
||||
{ name: 'rspec',
|
||||
ref: 'master',
|
||||
yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
|
||||
job_variables: [{ key: 'VAR1', value: 'var 1' }],
|
||||
yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
|
||||
job_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
|
||||
root_variables_inheritance: root_variables_inheritance,
|
||||
rules: rules }
|
||||
end
|
||||
|
@ -338,14 +338,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
end
|
||||
|
||||
it 'recalculates the variables' do
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
|
||||
{ key: 'VAR2', value: 'new var 2' })
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
|
||||
{ key: 'VAR2', value: 'new var 2', public: true })
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the rules use root variables' do
|
||||
let(:root_variables) do
|
||||
[{ key: 'VAR2', value: 'var pipeline 2' }]
|
||||
[{ key: 'VAR2', value: 'var pipeline 2', public: true }]
|
||||
end
|
||||
|
||||
let(:rules) do
|
||||
|
@ -353,15 +353,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
end
|
||||
|
||||
it 'recalculates the variables' do
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
|
||||
{ key: 'VAR2', value: 'overridden var 2' })
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
|
||||
{ key: 'VAR2', value: 'overridden var 2', public: true })
|
||||
end
|
||||
|
||||
context 'when the root_variables_inheritance is false' do
|
||||
let(:root_variables_inheritance) { false }
|
||||
|
||||
it 'does not recalculate the variables' do
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
|
||||
expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,27 +15,21 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
|
|||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
[{ key: 'key1', value: 'value1', public: true },
|
||||
{ key: 'key2', value: 'value22', public: true },
|
||||
{ key: 'key3', value: 'value3', public: true }]
|
||||
end
|
||||
|
||||
subject { described_class.merge_variables(current_variables, new_variables) }
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
|
||||
context 'when new variables is a hash' do
|
||||
let(:new_variables) do
|
||||
{ 'key2' => 'value22', 'key3' => 'value3' }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
|
||||
context 'when new variables is a hash with symbol keys' do
|
||||
|
@ -43,72 +37,79 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
|
|||
{ key2: 'value22', key3: 'value3' }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
|
||||
context 'when new variables is nil' do
|
||||
let(:new_variables) {}
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value2' }]
|
||||
[{ key: 'key1', value: 'value1', public: true },
|
||||
{ key: 'key2', value: 'value2', public: true }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.transform_to_array' do
|
||||
subject { described_class.transform_to_array(variables) }
|
||||
|
||||
context 'when values are strings' do
|
||||
let(:variables) do
|
||||
{ 'key1' => 'value1', 'key2' => 'value2' }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value2' }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
describe '.transform_to_yaml_variables' do
|
||||
let(:variables) do
|
||||
{ 'key1' => 'value1', 'key2' => 'value2' }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1', public: true },
|
||||
{ key: 'key2', value: 'value2', public: true }]
|
||||
end
|
||||
|
||||
subject { described_class.transform_to_yaml_variables(variables) }
|
||||
|
||||
it { is_expected.to eq(result) }
|
||||
|
||||
context 'when variables is nil' do
|
||||
let(:variables) {}
|
||||
|
||||
it { is_expected.to match_array([]) }
|
||||
it { is_expected.to eq([]) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.transform_from_yaml_variables' do
|
||||
let(:variables) do
|
||||
[{ key: 'key1', value: 'value1', public: true },
|
||||
{ key: 'key2', value: 'value2', public: true }]
|
||||
end
|
||||
|
||||
context 'when values are hashes' do
|
||||
let(:result) do
|
||||
{ 'key1' => 'value1', 'key2' => 'value2' }
|
||||
end
|
||||
|
||||
subject { described_class.transform_from_yaml_variables(variables) }
|
||||
|
||||
it { is_expected.to eq(result) }
|
||||
|
||||
context 'when variables is nil' do
|
||||
let(:variables) {}
|
||||
|
||||
it { is_expected.to eq({}) }
|
||||
end
|
||||
|
||||
context 'when variables is a hash' do
|
||||
let(:variables) do
|
||||
{ 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } }
|
||||
{ key1: 'value1', 'key2' => 'value2' }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1', description: 'var 1' },
|
||||
{ key: 'key2', value: 'value2' }]
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
|
||||
context 'when variables contain integers and symbols' do
|
||||
let(:variables) do
|
||||
{ key1: 1, key2: :value2 }
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
|
||||
context 'when a value data has `key` as a key' do
|
||||
let(:variables) do
|
||||
{ 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
|
||||
end
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value2' }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
let(:result1) do
|
||||
{ 'key1' => '1', 'key2' => 'value2' }
|
||||
end
|
||||
|
||||
it { is_expected.to eq(result1) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -126,35 +127,35 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
|
|||
let(:inheritance) { true }
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key1', value: 'value1' },
|
||||
{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
[{ key: 'key1', value: 'value1', public: true },
|
||||
{ key: 'key2', value: 'value22', public: true },
|
||||
{ key: 'key3', value: 'value3', public: true }]
|
||||
end
|
||||
|
||||
subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) }
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
|
||||
context 'when inheritance is false' do
|
||||
let(:inheritance) { false }
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
[{ key: 'key2', value: 'value22', public: true },
|
||||
{ key: 'key3', value: 'value3', public: true }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
|
||||
context 'when inheritance is array' do
|
||||
let(:inheritance) { ['key2'] }
|
||||
|
||||
let(:result) do
|
||||
[{ key: 'key2', value: 'value22' },
|
||||
{ key: 'key3', value: 'value3' }]
|
||||
[{ key: 'key2', value: 'value22', public: true },
|
||||
{ key: 'key3', value: 'value3', public: true }]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(result) }
|
||||
it { is_expected.to eq(result) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,8 +72,8 @@ module Gitlab
|
|||
|
||||
it 'returns calculated variables with root and job variables' do
|
||||
is_expected.to match_array([
|
||||
{ key: 'VAR1', value: 'value 11' },
|
||||
{ key: 'VAR2', value: 'value 2' }
|
||||
{ key: 'VAR1', value: 'value 11', public: true },
|
||||
{ key: 'VAR2', value: 'value 2', public: true }
|
||||
])
|
||||
end
|
||||
|
||||
|
|
|
@ -448,7 +448,7 @@ module Gitlab
|
|||
|
||||
it 'parses the root:variables as #root_variables' do
|
||||
expect(subject.root_variables)
|
||||
.to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
|
||||
.to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -490,7 +490,7 @@ module Gitlab
|
|||
|
||||
it 'parses the root:variables as #root_variables' do
|
||||
expect(subject.root_variables)
|
||||
.to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
|
||||
.to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1098,8 +1098,8 @@ module Gitlab
|
|||
|
||||
it 'returns job variables' do
|
||||
expect(job_variables).to contain_exactly(
|
||||
{ key: 'VAR1', value: 'value1' },
|
||||
{ key: 'VAR2', value: 'value2' }
|
||||
{ key: 'VAR1', value: 'value1', public: true },
|
||||
{ key: 'VAR2', value: 'value2', public: true }
|
||||
)
|
||||
expect(root_variables_inheritance).to eq(true)
|
||||
end
|
||||
|
@ -1203,21 +1203,21 @@ module Gitlab
|
|||
expect(config_processor.builds[0]).to include(
|
||||
name: 'test1',
|
||||
options: { script: ['test'] },
|
||||
job_variables: [{ key: 'VAR1', value: 'test1 var 1' },
|
||||
{ key: 'VAR2', value: 'test2 var 2' }]
|
||||
job_variables: [{ key: 'VAR1', value: 'test1 var 1', public: true },
|
||||
{ key: 'VAR2', value: 'test2 var 2', public: true }]
|
||||
)
|
||||
|
||||
expect(config_processor.builds[1]).to include(
|
||||
name: 'test2',
|
||||
options: { script: ['test'] },
|
||||
job_variables: [{ key: 'VAR1', value: 'base var 1' },
|
||||
{ key: 'VAR2', value: 'test2 var 2' }]
|
||||
job_variables: [{ key: 'VAR1', value: 'base var 1', public: true },
|
||||
{ key: 'VAR2', value: 'test2 var 2', public: true }]
|
||||
)
|
||||
|
||||
expect(config_processor.builds[2]).to include(
|
||||
name: 'test3',
|
||||
options: { script: ['test'] },
|
||||
job_variables: [{ key: 'VAR1', value: 'base var 1' }]
|
||||
job_variables: [{ key: 'VAR1', value: 'base var 1', public: true }]
|
||||
)
|
||||
|
||||
expect(config_processor.builds[3]).to include(
|
||||
|
|
|
@ -36,7 +36,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
|
|||
expect(pipeline.statuses).to match_array [test, bridge]
|
||||
expect(bridge.options).to eq(expected_bridge_options)
|
||||
expect(bridge.yaml_variables)
|
||||
.to include(key: 'CROSS', value: 'downstream')
|
||||
.to include(key: 'CROSS', value: 'downstream', public: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -40,8 +40,8 @@ RSpec.describe Ci::ListConfigVariablesService, :use_clean_rails_memory_store_cac
|
|||
it 'returns variable list' do
|
||||
expect(subject['KEY1']).to eq({ value: 'val 1', description: 'description 1' })
|
||||
expect(subject['KEY2']).to eq({ value: 'val 2', description: '' })
|
||||
expect(subject['KEY3']).to eq({ value: 'val 3' })
|
||||
expect(subject['KEY4']).to eq({ value: 'val 4' })
|
||||
expect(subject['KEY3']).to eq({ value: 'val 3', description: nil })
|
||||
expect(subject['KEY4']).to eq({ value: 'val 4', description: nil })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue