Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a821bd6ad1
commit
e3e300557f
|
@ -483,6 +483,16 @@ export const historyPushState = newUrl => {
|
|||
window.history.pushState({}, document.title, newUrl);
|
||||
};
|
||||
|
||||
/**
|
||||
* Based on the current location and the string parameters provided
|
||||
* overwrites the current entry in the history without reloading the page.
|
||||
*
|
||||
* @param {String} param
|
||||
*/
|
||||
export const historyReplaceState = newUrl => {
|
||||
window.history.replaceState({}, document.title, newUrl);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true for a String value of "true" and false otherwise.
|
||||
* This is the opposite of Boolean(...).toString().
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import Vue from 'vue';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import { doesHashExistInUrl } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
parseBoolean,
|
||||
historyReplaceState,
|
||||
buildUrlWithCurrentLocation,
|
||||
} from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
import PipelinesStore from '../../../../pipelines/stores/pipelines_store';
|
||||
import pipelinesComponent from '../../../../pipelines/components/pipelines.vue';
|
||||
import Translate from '../../../../vue_shared/translate';
|
||||
import { parseBoolean } from '../../../../lib/utils/common_utils';
|
||||
|
||||
Vue.use(Translate);
|
||||
Vue.use(GlToast);
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
|
@ -21,6 +29,11 @@ document.addEventListener(
|
|||
},
|
||||
created() {
|
||||
this.dataset = document.querySelector(this.$options.el).dataset;
|
||||
|
||||
if (doesHashExistInUrl('delete_success')) {
|
||||
this.$toast.show(__('The pipeline has been deleted'));
|
||||
historyReplaceState(buildUrlWithCurrentLocation());
|
||||
}
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('pipelines-component', {
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlModal } from '@gitlab/ui';
|
||||
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
|
||||
import eventHub from '../event_hub';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
const DELETE_MODAL_ID = 'pipeline-delete-modal';
|
||||
|
||||
export default {
|
||||
name: 'PipelineHeaderSection',
|
||||
components: {
|
||||
ciHeader,
|
||||
GlLoadingIcon,
|
||||
GlModal,
|
||||
},
|
||||
props: {
|
||||
pipeline: {
|
||||
|
@ -33,6 +36,11 @@ export default {
|
|||
shouldRenderContent() {
|
||||
return !this.isLoading && Object.keys(this.pipeline).length;
|
||||
},
|
||||
deleteModalConfirmationText() {
|
||||
return __(
|
||||
'Are you sure you want to delete this pipeline? Doing so will expire all pipeline caches and delete all related objects, such as builds, logs, artifacts, and triggers. This action cannot be undone.',
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -42,6 +50,13 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
onActionClicked(action) {
|
||||
if (action.modal) {
|
||||
this.$root.$emit('bv::show::modal', action.modal);
|
||||
} else {
|
||||
this.postAction(action);
|
||||
}
|
||||
},
|
||||
postAction(action) {
|
||||
const index = this.actions.indexOf(action);
|
||||
|
||||
|
@ -49,6 +64,13 @@ export default {
|
|||
|
||||
eventHub.$emit('headerPostAction', action);
|
||||
},
|
||||
deletePipeline() {
|
||||
const index = this.actions.findIndex(action => action.modal === DELETE_MODAL_ID);
|
||||
|
||||
this.$set(this.actions[index], 'isLoading', true);
|
||||
|
||||
eventHub.$emit('headerDeleteAction', this.actions[index]);
|
||||
},
|
||||
|
||||
getActions() {
|
||||
const actions = [];
|
||||
|
@ -58,7 +80,6 @@ export default {
|
|||
label: __('Retry'),
|
||||
path: this.pipeline.retry_path,
|
||||
cssClass: 'js-retry-button btn btn-inverted-secondary',
|
||||
type: 'button',
|
||||
isLoading: false,
|
||||
});
|
||||
}
|
||||
|
@ -68,7 +89,16 @@ export default {
|
|||
label: __('Cancel running'),
|
||||
path: this.pipeline.cancel_path,
|
||||
cssClass: 'js-btn-cancel-pipeline btn btn-danger',
|
||||
type: 'button',
|
||||
isLoading: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.pipeline.delete_path) {
|
||||
actions.push({
|
||||
label: __('Delete'),
|
||||
path: this.pipeline.delete_path,
|
||||
modal: DELETE_MODAL_ID,
|
||||
cssClass: 'js-btn-delete-pipeline btn btn-danger btn-inverted',
|
||||
isLoading: false,
|
||||
});
|
||||
}
|
||||
|
@ -76,6 +106,7 @@ export default {
|
|||
return actions;
|
||||
},
|
||||
},
|
||||
DELETE_MODAL_ID,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
@ -88,8 +119,21 @@ export default {
|
|||
:user="pipeline.user"
|
||||
:actions="actions"
|
||||
item-name="Pipeline"
|
||||
@actionClicked="postAction"
|
||||
@actionClicked="onActionClicked"
|
||||
/>
|
||||
|
||||
<gl-loading-icon v-if="isLoading" :size="2" class="prepend-top-default append-bottom-default" />
|
||||
|
||||
<gl-modal
|
||||
:modal-id="$options.DELETE_MODAL_ID"
|
||||
:title="__('Delete pipeline')"
|
||||
:ok-title="__('Delete pipeline')"
|
||||
ok-variant="danger"
|
||||
@ok="deletePipeline()"
|
||||
>
|
||||
<p>
|
||||
{{ deleteModalConfirmationText }}
|
||||
</p>
|
||||
</gl-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
|||
import Flash from '~/flash';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import { __ } from '~/locale';
|
||||
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
|
||||
import pipelineGraph from './components/graph/graph_component.vue';
|
||||
import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin';
|
||||
import PipelinesMediator from './pipeline_details_mediator';
|
||||
|
@ -62,9 +63,11 @@ export default () => {
|
|||
},
|
||||
created() {
|
||||
eventHub.$on('headerPostAction', this.postAction);
|
||||
eventHub.$on('headerDeleteAction', this.deleteAction);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('headerPostAction', this.postAction);
|
||||
eventHub.$off('headerDeleteAction', this.deleteAction);
|
||||
},
|
||||
methods: {
|
||||
postAction(action) {
|
||||
|
@ -73,6 +76,13 @@ export default () => {
|
|||
.then(() => this.mediator.refreshPipeline())
|
||||
.catch(() => Flash(__('An error occurred while making the request.')));
|
||||
},
|
||||
deleteAction(action) {
|
||||
this.mediator.stopPipelinePoll();
|
||||
this.mediator.service
|
||||
.deleteAction(action.path)
|
||||
.then(({ request }) => redirectTo(setUrlFragment(request.responseURL, 'delete_success')))
|
||||
.catch(() => Flash(__('An error occurred while deleting the pipeline.')));
|
||||
},
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('pipeline-header', {
|
||||
|
|
|
@ -35,7 +35,7 @@ export default class pipelinesMediator {
|
|||
if (!Visibility.hidden()) {
|
||||
this.poll.restart();
|
||||
} else {
|
||||
this.poll.stop();
|
||||
this.stopPipelinePoll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ export default class pipelinesMediator {
|
|||
}
|
||||
|
||||
refreshPipeline() {
|
||||
this.poll.stop();
|
||||
this.stopPipelinePoll();
|
||||
|
||||
return this.service
|
||||
.getPipeline()
|
||||
|
@ -64,6 +64,10 @@ export default class pipelinesMediator {
|
|||
);
|
||||
}
|
||||
|
||||
stopPipelinePoll() {
|
||||
this.poll.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend expects paramets in the following format: `expanded[]=id&expanded[]=id`
|
||||
*/
|
||||
|
|
|
@ -9,6 +9,11 @@ export default class PipelineService {
|
|||
return axios.get(this.pipeline, { params });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
deleteAction(endpoint) {
|
||||
return axios.delete(`${endpoint}.json`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
postAction(endpoint) {
|
||||
return axios.post(`${endpoint}.json`);
|
||||
|
|
|
@ -117,28 +117,7 @@ export default {
|
|||
|
||||
<section v-if="actions.length" class="header-action-buttons">
|
||||
<template v-for="(action, i) in actions">
|
||||
<gl-link
|
||||
v-if="action.type === 'link'"
|
||||
:key="i"
|
||||
:href="action.path"
|
||||
:class="action.cssClass"
|
||||
>
|
||||
{{ action.label }}
|
||||
</gl-link>
|
||||
|
||||
<gl-link
|
||||
v-else-if="action.type === 'ujs-link'"
|
||||
:key="i"
|
||||
:href="action.path"
|
||||
:class="action.cssClass"
|
||||
data-method="post"
|
||||
rel="nofollow"
|
||||
>
|
||||
{{ action.label }}
|
||||
</gl-link>
|
||||
|
||||
<loading-button
|
||||
v-else-if="action.type === 'button'"
|
||||
:key="i"
|
||||
:loading="action.isLoading"
|
||||
:disabled="action.isLoading"
|
||||
|
|
|
@ -80,6 +80,12 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
::Ci::DestroyPipelineService.new(project, current_user).execute(pipeline)
|
||||
|
||||
redirect_to project_pipelines_path(project), status: :see_other
|
||||
end
|
||||
|
||||
def builds
|
||||
render_show
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ class PipelinesFinder
|
|||
return Ci::Pipeline.none
|
||||
end
|
||||
|
||||
items = pipelines.no_child
|
||||
items = pipelines
|
||||
items = by_scope(items)
|
||||
items = by_status(items)
|
||||
items = by_ref(items)
|
||||
|
|
|
@ -54,10 +54,6 @@ module Ci
|
|||
def to_partial_path
|
||||
'projects/generic_commit_statuses/generic_commit_status'
|
||||
end
|
||||
|
||||
def yaml_for_downstream
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -61,9 +61,7 @@ module Ci
|
|||
has_one :chat_data, class_name: 'Ci::PipelineChatData'
|
||||
|
||||
has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline
|
||||
has_many :child_pipelines, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :sourced_pipelines, source: :pipeline
|
||||
has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline
|
||||
has_one :parent_pipeline, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :source_pipeline, source: :source_pipeline
|
||||
has_one :source_job, through: :source_pipeline, source: :source_job
|
||||
|
||||
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
|
||||
|
@ -215,7 +213,6 @@ module Ci
|
|||
end
|
||||
|
||||
scope :internal, -> { where(source: internal_sources) }
|
||||
scope :no_child, -> { where.not(source: :parent_pipeline) }
|
||||
scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) }
|
||||
scope :for_user, -> (user) { where(user: user) }
|
||||
scope :for_sha, -> (sha) { where(sha: sha) }
|
||||
|
@ -511,6 +508,10 @@ module Ci
|
|||
builds.skipped.after_stage(stage_idx).find_each(&:process)
|
||||
end
|
||||
|
||||
def child?
|
||||
false
|
||||
end
|
||||
|
||||
def latest?
|
||||
return false unless git_ref && commit.present?
|
||||
|
||||
|
@ -693,24 +694,6 @@ module Ci
|
|||
all_merge_requests.order(id: :desc)
|
||||
end
|
||||
|
||||
# If pipeline is a child of another pipeline, include the parent
|
||||
# and the siblings, otherwise return only itself.
|
||||
def same_family_pipeline_ids
|
||||
if (parent = parent_pipeline)
|
||||
[parent.id] + parent.child_pipelines.pluck(:id)
|
||||
else
|
||||
[self.id]
|
||||
end
|
||||
end
|
||||
|
||||
def child?
|
||||
parent_pipeline.present?
|
||||
end
|
||||
|
||||
def parent?
|
||||
child_pipelines.exists?
|
||||
end
|
||||
|
||||
def detailed_status(current_user)
|
||||
Gitlab::Ci::Status::Pipeline::Factory
|
||||
.new(self, current_user)
|
||||
|
|
|
@ -23,11 +23,10 @@ module Ci
|
|||
schedule: 4,
|
||||
api: 5,
|
||||
external: 6,
|
||||
cross_project_pipeline: 7,
|
||||
pipeline: 7,
|
||||
chat: 8,
|
||||
merge_request_event: 10,
|
||||
external_pull_request_event: 11,
|
||||
parent_pipeline: 12
|
||||
external_pull_request_event: 11
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -39,8 +38,7 @@ module Ci
|
|||
repository_source: 1,
|
||||
auto_devops_source: 2,
|
||||
remote_source: 4,
|
||||
external_project_source: 5,
|
||||
bridge_source: 6
|
||||
external_project_source: 5
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ module Ci
|
|||
validates :source_project, presence: true
|
||||
validates :source_job, presence: true
|
||||
validates :source_pipeline, presence: true
|
||||
|
||||
scope :same_project, -> { where(arel_table[:source_project_id].eq(arel_table[:project_id])) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PipelineDetailsEntity < PipelineEntity
|
||||
expose :project, using: ProjectEntity
|
||||
|
||||
expose :flags do
|
||||
expose :latest?, as: :latest
|
||||
end
|
||||
|
|
|
@ -77,6 +77,10 @@ class PipelineEntity < Grape::Entity
|
|||
cancel_project_pipeline_path(pipeline.project, pipeline)
|
||||
end
|
||||
|
||||
expose :delete_path, if: -> (*) { can_delete? } do |pipeline|
|
||||
project_pipeline_path(pipeline.project, pipeline)
|
||||
end
|
||||
|
||||
expose :failed_builds, if: -> (*) { can_retry? }, using: JobEntity do |pipeline|
|
||||
pipeline.failed_builds
|
||||
end
|
||||
|
@ -95,6 +99,10 @@ class PipelineEntity < Grape::Entity
|
|||
pipeline.cancelable?
|
||||
end
|
||||
|
||||
def can_delete?
|
||||
can?(request.current_user, :destroy_pipeline, pipeline)
|
||||
end
|
||||
|
||||
def has_presentable_merge_request?
|
||||
pipeline.triggered_by_merge_request? &&
|
||||
can?(request.current_user, :read_merge_request, pipeline.merge_request)
|
||||
|
|
|
@ -41,7 +41,6 @@ class PipelineSerializer < BaseSerializer
|
|||
def preloaded_relations
|
||||
[
|
||||
:latest_statuses_ordered_by_stage,
|
||||
:project,
|
||||
:stages,
|
||||
{
|
||||
failed_builds: %i(project metadata)
|
||||
|
|
|
@ -23,7 +23,7 @@ module Ci
|
|||
Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze
|
||||
|
||||
# rubocop: disable Metrics/ParameterLists
|
||||
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, bridge: nil, **options, &block)
|
||||
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, **options, &block)
|
||||
@pipeline = Ci::Pipeline.new
|
||||
|
||||
command = Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||
|
@ -46,7 +46,6 @@ module Ci
|
|||
current_user: current_user,
|
||||
push_options: params[:push_options] || {},
|
||||
chat_data: params[:chat_data],
|
||||
bridge: bridge,
|
||||
**extra_options(options))
|
||||
|
||||
sequence = Gitlab::Ci::Pipeline::Chain::Sequence
|
||||
|
@ -105,14 +104,14 @@ module Ci
|
|||
if Feature.enabled?(:ci_support_interruptible_pipelines, project, default_enabled: true)
|
||||
project.ci_pipelines
|
||||
.where(ref: pipeline.ref)
|
||||
.where.not(id: pipeline.same_family_pipeline_ids)
|
||||
.where.not(id: pipeline.id)
|
||||
.where.not(sha: project.commit(pipeline.ref).try(:id))
|
||||
.alive_or_scheduled
|
||||
.with_only_interruptible_builds
|
||||
else
|
||||
project.ci_pipelines
|
||||
.where(ref: pipeline.ref)
|
||||
.where.not(id: pipeline.same_family_pipeline_ids)
|
||||
.where.not(id: pipeline.id)
|
||||
.where.not(sha: project.commit(pipeline.ref).try(:id))
|
||||
.created_or_pending
|
||||
end
|
||||
|
|
|
@ -44,7 +44,7 @@ module Ci
|
|||
return error("400 Job has to be running", 400) unless job.running?
|
||||
|
||||
pipeline = Ci::CreatePipelineService.new(project, job.user, ref: params[:ref])
|
||||
.execute(:cross_project_pipeline, ignore_skip_ci: true) do |pipeline|
|
||||
.execute(:pipeline, ignore_skip_ci: true) do |pipeline|
|
||||
source = job.sourced_pipelines.build(
|
||||
source_pipeline: job.pipeline,
|
||||
source_project: job.project,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Delete Selected button being active after uploading designs after a deletion
|
||||
merge_request: 22516
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Allow an upstream pipeline to create a downstream pipeline in the same project
|
||||
merge_request: 20930
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add pipeline deletion button to pipeline details page
|
||||
merge_request: 21365
|
||||
author: Fabio Huser
|
||||
type: added
|
|
@ -343,7 +343,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
draw :merge_requests
|
||||
end
|
||||
|
||||
resources :pipelines, only: [:index, :new, :create, :show] do
|
||||
resources :pipelines, only: [:index, :new, :create, :show, :destroy] do
|
||||
collection do
|
||||
resource :pipelines_settings, path: 'settings', only: [:show, :update]
|
||||
get :charts
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
|
@ -305,12 +305,14 @@ For example, the query string
|
|||
### Accessing pipelines
|
||||
|
||||
You can find the current and historical pipeline runs under your project's
|
||||
**CI/CD > Pipelines** page. Clicking on a pipeline will show the jobs that were run for
|
||||
that pipeline.
|
||||
**CI/CD > Pipelines** page. You can also access pipelines for a merge request by navigating
|
||||
to its **Pipelines** tab.
|
||||
|
||||
![Pipelines index page](img/pipelines_index.png)
|
||||
|
||||
You can also access pipelines for a merge request by navigating to its **Pipelines** tab.
|
||||
Clicking on a pipeline will bring you to the **Pipeline Details** page and show
|
||||
the jobs that were run for that pipeline. From here you can cancel a running pipeline,
|
||||
retry jobs on a failed pipeline, or [delete a pipeline](#deleting-a-single-pipeline).
|
||||
|
||||
### Accessing individual jobs
|
||||
|
||||
|
@ -410,6 +412,20 @@ This functionality is only available:
|
|||
- For users with at least Developer access.
|
||||
- If the the stage contains [manual actions](#manual-actions-from-pipeline-graphs).
|
||||
|
||||
### Deleting a single pipeline
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/24851) in GitLab 12.7.
|
||||
|
||||
Users with [owner permissions](../user/permissions.md) in a project can delete a pipeline
|
||||
by clicking on the pipeline in the **CI/CD > Pipelines** to get to the **Pipeline Details**
|
||||
page, then using the **Delete** button.
|
||||
|
||||
![Pipeline Delete Button](img/pipeline-delete.png)
|
||||
|
||||
CAUTION: **Warning:**
|
||||
Deleting a pipeline will expire all pipeline caches, and delete all related objects,
|
||||
such as builds, logs, artifacts, and triggers. **This action cannot be undone.**
|
||||
|
||||
## Most Recent Pipeline
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/50499) in GitLab 12.3.
|
||||
|
|
|
@ -98,6 +98,10 @@ This was originally implemented in: <https://gitlab.com/gitlab-org/gitlab-foss/m
|
|||
|
||||
- Memory is through the roof! (TL;DR: Load images but block images requests!): <https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/12003>
|
||||
|
||||
#### Capybara expectation times out
|
||||
|
||||
- [Test imports a project (via Sidekiq) that is growing over time, leading to timeouts when the import takes longer than 60 seconds](https://gitlab.com/gitlab-org/gitlab/merge_requests/22599)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Flaky Tests: Are You Sure You Want to Rerun Them?](http://semaphoreci.com/blog/2017/04/20/flaky-tests.html)
|
||||
|
|
|
@ -10,7 +10,7 @@ module Gitlab
|
|||
:trigger_request, :schedule, :merge_request, :external_pull_request,
|
||||
:ignore_skip_ci, :save_incompleted,
|
||||
:seeds_block, :variables_attributes, :push_options,
|
||||
:chat_data, :allow_mirror_update, :bridge,
|
||||
:chat_data, :allow_mirror_update,
|
||||
# These attributes are set by Chains during processing:
|
||||
:config_content, :config_processor, :stage_seeds
|
||||
) do
|
||||
|
|
|
@ -9,7 +9,7 @@ module Gitlab
|
|||
include Chain::Helpers
|
||||
|
||||
SOURCES = [
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Bridge,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Runtime,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Repository,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::ExternalProject,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Remote,
|
||||
|
@ -17,7 +17,7 @@ module Gitlab
|
|||
].freeze
|
||||
|
||||
LEGACY_SOURCES = [
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Bridge,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::Runtime,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::LegacyRepository,
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content::LegacyAutoDevops
|
||||
].freeze
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Pipeline
|
||||
module Chain
|
||||
module Config
|
||||
class Content
|
||||
class Bridge < Source
|
||||
def content
|
||||
return unless @command.bridge
|
||||
|
||||
@command.bridge.yaml_for_downstream
|
||||
end
|
||||
|
||||
def source
|
||||
:bridge_source
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Pipeline
|
||||
module Chain
|
||||
module Config
|
||||
class Content
|
||||
class Runtime < Source
|
||||
def content
|
||||
@command.config_content
|
||||
end
|
||||
|
||||
def source
|
||||
# The only case when this source is used is when the config content
|
||||
# is passed in as parameter to Ci::CreatePipelineService.
|
||||
# This would only occur with parent/child pipelines which is being
|
||||
# implemented.
|
||||
# TODO: change source to return :runtime_source
|
||||
# https://gitlab.com/gitlab-org/gitlab/merge_requests/21041
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1631,6 +1631,9 @@ msgstr ""
|
|||
msgid "An error occurred while deleting the comment"
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while deleting the pipeline."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while detecting host keys"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2092,6 +2095,9 @@ msgstr ""
|
|||
msgid "Are you sure you want to delete this pipeline schedule?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to delete this pipeline? Doing so will expire all pipeline caches and delete all related objects, such as builds, logs, artifacts, and triggers. This action cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to erase this build?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3266,6 +3272,9 @@ msgstr ""
|
|||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|%{selectedPlanText} plan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|1. Your profile"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3278,9 +3287,36 @@ msgstr ""
|
|||
msgid "Checkout|Checkout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Continue to billing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|GitLab plan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Name of company or organization using GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Need more users? Purchase GitLab for your %{company}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Number of users"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Subscription details"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Users"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|company or team"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cherry-pick this commit"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5735,6 +5771,9 @@ msgstr ""
|
|||
msgid "Delete list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete pipeline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete snippet"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18098,6 +18137,9 @@ msgstr ""
|
|||
msgid "The phase of the development lifecycle."
|
||||
msgstr ""
|
||||
|
||||
msgid "The pipeline has been deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -740,4 +740,51 @@ describe Projects::PipelinesController do
|
|||
expect(response).to have_gitlab_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let!(:project) { create(:project, :private, :repository) }
|
||||
let!(:pipeline) { create(:ci_pipeline, :failed, project: project) }
|
||||
let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
|
||||
|
||||
context 'when user has ability to delete pipeline' do
|
||||
before do
|
||||
sign_in(project.owner)
|
||||
end
|
||||
|
||||
it 'deletes pipeline and redirects' do
|
||||
delete_pipeline
|
||||
|
||||
expect(response).to have_gitlab_http_status(303)
|
||||
|
||||
expect(Ci::Build.exists?(build.id)).to be_falsy
|
||||
expect(Ci::Pipeline.exists?(pipeline.id)).to be_falsy
|
||||
end
|
||||
|
||||
context 'and builds are disabled' do
|
||||
let(:feature) { ProjectFeature::DISABLED }
|
||||
|
||||
it 'fails to delete pipeline' do
|
||||
delete_pipeline
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has no privileges' do
|
||||
it 'fails to delete pipeline' do
|
||||
delete_pipeline
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_pipeline
|
||||
delete :destroy, params: {
|
||||
namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: pipeline.id
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,17 +24,17 @@ describe 'Import multiple repositories by uploading a manifest file', :js do
|
|||
expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
|
||||
end
|
||||
|
||||
it 'imports successfully imports a project', :sidekiq_might_not_need_inline do
|
||||
it 'imports successfully imports a project', :sidekiq_inline do
|
||||
visit new_import_manifest_path
|
||||
|
||||
attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
|
||||
click_on 'List available repositories'
|
||||
|
||||
page.within(first_row) do
|
||||
page.within(second_row) do
|
||||
click_on 'Import'
|
||||
|
||||
expect(page).to have_content 'Done'
|
||||
expect(page).to have_content("#{group.full_path}/build/make")
|
||||
expect(page).to have_content("#{group.full_path}/build/blueprint")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -47,7 +47,7 @@ describe 'Import multiple repositories by uploading a manifest file', :js do
|
|||
expect(page).to have_content 'The uploaded file is not a valid XML file.'
|
||||
end
|
||||
|
||||
def first_row
|
||||
page.all('table.import-jobs tbody tr')[0]
|
||||
def second_row
|
||||
page.all('table.import-jobs tbody tr')[1]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,7 +59,8 @@ describe 'Pipeline', :js do
|
|||
describe 'GET /:project/pipelines/:id' do
|
||||
include_context 'pipeline builds'
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, :repository, group: group) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
|
||||
|
||||
subject(:visit_pipeline) { visit project_pipeline_path(project, pipeline) }
|
||||
|
@ -329,6 +330,32 @@ describe 'Pipeline', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'deleting pipeline' do
|
||||
context 'when user can not delete' do
|
||||
before do
|
||||
visit_pipeline
|
||||
end
|
||||
|
||||
it { expect(page).not_to have_button('Delete') }
|
||||
end
|
||||
|
||||
context 'when deleting' do
|
||||
before do
|
||||
group.add_owner(user)
|
||||
|
||||
visit_pipeline
|
||||
|
||||
click_button 'Delete'
|
||||
click_button 'Delete pipeline'
|
||||
end
|
||||
|
||||
it 'redirects to pipeline overview page', :sidekiq_might_not_need_inline do
|
||||
expect(page).to have_content('The pipeline has been deleted')
|
||||
expect(current_path).to eq(project_pipelines_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline ref does not exist in repository anymore' do
|
||||
let(:pipeline) do
|
||||
create(:ci_empty_pipeline, project: project,
|
||||
|
|
|
@ -64,19 +64,6 @@ describe PipelinesFinder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when project has child pipelines' do
|
||||
let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
|
||||
let!(:child_pipeline) { create(:ci_pipeline, project: project, source: :parent_pipeline) }
|
||||
|
||||
let!(:pipeline_source) do
|
||||
create(:ci_sources_pipeline, pipeline: child_pipeline, source_pipeline: parent_pipeline)
|
||||
end
|
||||
|
||||
it 'filters out child pipelines and show only the parents' do
|
||||
is_expected.to eq([parent_pipeline])
|
||||
end
|
||||
end
|
||||
|
||||
HasStatus::AVAILABLE_STATUSES.each do |target|
|
||||
context "when status is #{target}" do
|
||||
let(:params) { { status: target } }
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import ProjectFormGroup from '~/confidential_merge_request/components/project_form_group.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
|
@ -30,7 +29,6 @@ function factory(projects = mockData) {
|
|||
mock.onGet(/api\/(.*)\/projects\/gitlab-org%2Fgitlab-ce\/forks/).reply(200, projects);
|
||||
|
||||
vm = shallowMount(ProjectFormGroup, {
|
||||
localVue,
|
||||
propsData: {
|
||||
namespacePath: 'gitlab-org',
|
||||
projectPath: 'gitlab-org/gitlab-ce',
|
||||
|
@ -49,7 +47,7 @@ describe('Confidential merge request project form group component', () => {
|
|||
it('renders fork dropdown', () => {
|
||||
factory();
|
||||
|
||||
return localVue.nextTick(() => {
|
||||
return vm.vm.$nextTick(() => {
|
||||
expect(vm.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -57,7 +55,7 @@ describe('Confidential merge request project form group component', () => {
|
|||
it('sets selected project as first fork', () => {
|
||||
factory();
|
||||
|
||||
return localVue.nextTick(() => {
|
||||
return vm.vm.$nextTick(() => {
|
||||
expect(vm.vm.selectedProject).toEqual({
|
||||
id: 1,
|
||||
name: 'root / gitlab-ce',
|
||||
|
@ -70,7 +68,7 @@ describe('Confidential merge request project form group component', () => {
|
|||
it('renders empty state when response is empty', () => {
|
||||
factory([]);
|
||||
|
||||
return localVue.nextTick(() => {
|
||||
return vm.vm.$nextTick(() => {
|
||||
expect(vm.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { createStore } from '~/contributors/stores';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import ContributorsCharts from '~/contributors/components/contributors.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
let wrapper;
|
||||
let mock;
|
||||
let store;
|
||||
|
@ -52,7 +51,7 @@ describe('Contributors charts', () => {
|
|||
|
||||
it('should display loader whiled loading data', () => {
|
||||
wrapper.vm.$store.state.loading = true;
|
||||
return localVue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.find('.contributors-loader').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -60,7 +59,7 @@ describe('Contributors charts', () => {
|
|||
it('should render charts when loading completed and there is chart data', () => {
|
||||
wrapper.vm.$store.state.loading = false;
|
||||
wrapper.vm.$store.state.chartData = chartData;
|
||||
return localVue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.find('.contributors-loader').exists()).toBe(false);
|
||||
expect(wrapper.find('.contributors-charts').exists()).toBe(true);
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
|
||||
import discussionsMockData from '../mock_data/diff_discussions';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
|
||||
|
||||
describe('DiffGutterAvatars', () => {
|
||||
|
@ -14,7 +13,6 @@ describe('DiffGutterAvatars', () => {
|
|||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(DiffGutterAvatars, {
|
||||
localVue,
|
||||
propsData: {
|
||||
...props,
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import EditButton from '~/diffs/components/edit_button.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
const editPath = 'test-path';
|
||||
|
||||
describe('EditButton', () => {
|
||||
|
@ -9,7 +8,6 @@ describe('EditButton', () => {
|
|||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(EditButton, {
|
||||
localVue,
|
||||
propsData: { ...props },
|
||||
sync: false,
|
||||
attachToDocument: true,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
const propsData = {
|
||||
total: '10',
|
||||
visible: 5,
|
||||
|
@ -14,7 +13,6 @@ describe('HiddenFilesWarning', () => {
|
|||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(HiddenFilesWarning, {
|
||||
localVue,
|
||||
sync: false,
|
||||
propsData,
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ describe('Diff no changes empty state', () => {
|
|||
const store = createStore();
|
||||
extendStore(store);
|
||||
|
||||
vm = shallowMount(localVue.extend(NoChanges), {
|
||||
vm = shallowMount(NoChanges, {
|
||||
localVue,
|
||||
store,
|
||||
propsData: {
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('ide/components/ide_status_list', () => {
|
|||
},
|
||||
});
|
||||
|
||||
wrapper = shallowMount(localVue.extend(IdeStatusList), {
|
||||
wrapper = shallowMount(IdeStatusList, {
|
||||
localVue,
|
||||
sync: false,
|
||||
store,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlEmptyState, GlPagination, GlSkeletonLoading } from '@gitlab/ui';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
|
@ -18,8 +18,6 @@ const TEST_ENDPOINT = '/issues';
|
|||
const TEST_CREATE_ISSUES_PATH = '/createIssue';
|
||||
const TEST_EMPTY_SVG_PATH = '/emptySvg';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
const MOCK_ISSUES = Array(PAGE_SIZE_MANUAL)
|
||||
.fill(0)
|
||||
.map((_, i) => ({
|
||||
|
@ -40,14 +38,13 @@ describe('Issuables list component', () => {
|
|||
};
|
||||
|
||||
const factory = (props = { sortKey: 'priority' }) => {
|
||||
wrapper = shallowMount(localVue.extend(IssuablesListApp), {
|
||||
wrapper = shallowMount(IssuablesListApp, {
|
||||
propsData: {
|
||||
endpoint: TEST_ENDPOINT,
|
||||
createIssuePath: TEST_CREATE_ISSUES_PATH,
|
||||
emptySvgPath: TEST_EMPTY_SVG_PATH,
|
||||
...props,
|
||||
},
|
||||
localVue,
|
||||
sync: false,
|
||||
attachToDocument: true,
|
||||
});
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import PinnedLinks from '~/issue_show/components/pinned_links.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
const plainZoomUrl = 'https://zoom.us/j/123456789';
|
||||
|
||||
describe('PinnedLinks', () => {
|
||||
|
@ -19,8 +17,7 @@ describe('PinnedLinks', () => {
|
|||
};
|
||||
|
||||
const createComponent = props => {
|
||||
wrapper = shallowMount(localVue.extend(PinnedLinks), {
|
||||
localVue,
|
||||
wrapper = shallowMount(PinnedLinks, {
|
||||
sync: false,
|
||||
propsData: {
|
||||
zoomMeetingUrl: null,
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlColumnChart } from '@gitlab/ui/dist/charts';
|
||||
import ColumnChart from '~/monitoring/components/charts/column.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
jest.mock('~/lib/utils/icon_utils', () => ({
|
||||
getSvgIconPathContent: jest.fn().mockResolvedValue('mockSvgPathContent'),
|
||||
}));
|
||||
|
@ -12,7 +10,7 @@ describe('Column component', () => {
|
|||
let columnChart;
|
||||
|
||||
beforeEach(() => {
|
||||
columnChart = shallowMount(localVue.extend(ColumnChart), {
|
||||
columnChart = shallowMount(ColumnChart, {
|
||||
propsData: {
|
||||
graphData: {
|
||||
metrics: [
|
||||
|
@ -35,7 +33,6 @@ describe('Column component', () => {
|
|||
containerWidth: 100,
|
||||
},
|
||||
sync: false,
|
||||
localVue,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import EmptyChart from '~/monitoring/components/charts/empty_chart.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
describe('Empty Chart component', () => {
|
||||
let emptyChart;
|
||||
const graphTitle = 'Memory Usage';
|
||||
|
||||
beforeEach(() => {
|
||||
emptyChart = shallowMount(localVue.extend(EmptyChart), {
|
||||
emptyChart = shallowMount(EmptyChart, {
|
||||
propsData: {
|
||||
graphTitle,
|
||||
},
|
||||
sync: false,
|
||||
localVue,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import SingleStatChart from '~/monitoring/components/charts/single_stat.vue';
|
||||
import { graphDataPrometheusQuery } from '../../mock_data';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
describe('Single Stat Chart component', () => {
|
||||
let singleStatChart;
|
||||
|
||||
beforeEach(() => {
|
||||
singleStatChart = shallowMount(localVue.extend(SingleStatChart), {
|
||||
singleStatChart = shallowMount(SingleStatChart, {
|
||||
propsData: {
|
||||
graphData: graphDataPrometheusQuery,
|
||||
},
|
||||
sync: false,
|
||||
localVue,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue';
|
||||
|
@ -15,12 +15,10 @@ describe('operation settings external dashboard component', () => {
|
|||
const operationsSettingsEndpoint = `${TEST_HOST}/mock/ops/settings/endpoint`;
|
||||
const externalDashboardUrl = `http://mock-external-domain.com/external/dashboard/url`;
|
||||
const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`;
|
||||
const localVue = createLocalVue();
|
||||
const mountComponent = (shallow = true) => {
|
||||
const config = [
|
||||
ExternalDashboard,
|
||||
{
|
||||
localVue,
|
||||
store: store({
|
||||
operationsSettingsEndpoint,
|
||||
externalDashboardUrl,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import registry from '~/registry/list/components/app.vue';
|
||||
|
@ -35,12 +34,8 @@ describe('Registry List', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// This is needed due to console.error called by vue to emit a warning that stop the tests.
|
||||
// See https://github.com/vuejs/vue-test-utils/issues/532.
|
||||
Vue.config.silent = true;
|
||||
wrapper = mount(registry, {
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
propsData,
|
||||
computed: {
|
||||
repos() {
|
||||
|
@ -52,7 +47,6 @@ describe('Registry List', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
Vue.config.silent = false;
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
|
@ -138,7 +132,7 @@ describe('Registry List', () => {
|
|||
wrapper = mount(registry, {
|
||||
propsData: {
|
||||
...propsData,
|
||||
endpoint: null,
|
||||
endpoint: '',
|
||||
isGroupPage,
|
||||
},
|
||||
methods,
|
||||
|
@ -146,7 +140,7 @@ describe('Registry List', () => {
|
|||
});
|
||||
|
||||
it('call the right vuex setters', () => {
|
||||
expect(methods.setMainEndpoint).toHaveBeenLastCalledWith(null);
|
||||
expect(methods.setMainEndpoint).toHaveBeenLastCalledWith('');
|
||||
expect(methods.setIsDeleteDisabled).toHaveBeenLastCalledWith(true);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import createFlash from '~/flash';
|
||||
|
@ -28,14 +27,10 @@ describe('collapsible registry container', () => {
|
|||
store,
|
||||
localVue,
|
||||
attachToDocument: true,
|
||||
sync: false,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createFlash.mockClear();
|
||||
// This is needed due to console.error called by vue to emit a warning that stop the tests
|
||||
// see https://github.com/vuejs/vue-test-utils/issues/532
|
||||
Vue.config.silent = true;
|
||||
store = new Vuex.Store({
|
||||
state: {
|
||||
isDeleteDisabled: false,
|
||||
|
@ -51,7 +46,6 @@ describe('collapsible registry container', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
Vue.config.silent = false;
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
|
@ -72,25 +66,23 @@ describe('collapsible registry container', () => {
|
|||
expectIsClosed();
|
||||
});
|
||||
|
||||
it('should be open when user clicks on closed repo', done => {
|
||||
it('should be open when user clicks on closed repo', () => {
|
||||
const toggleRepos = findToggleRepos();
|
||||
toggleRepos.at(0).trigger('click');
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
const container = findContainerImageTags();
|
||||
expect(container.exists()).toBe(true);
|
||||
expect(wrapper.vm.fetchList).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be closed when the user clicks on an opened repo', done => {
|
||||
it('should be closed when the user clicks on an opened repo', () => {
|
||||
const toggleRepos = findToggleRepos();
|
||||
toggleRepos.at(0).trigger('click');
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
toggleRepos.at(0).trigger('click');
|
||||
Vue.nextTick(() => {
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expectIsClosed();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import createFlash from '~/flash';
|
||||
|
@ -29,13 +28,9 @@ describe('table registry', () => {
|
|||
const bulkDeletePath = 'path';
|
||||
|
||||
const mountWithStore = config =>
|
||||
mount(tableRegistry, { ...config, store, localVue, attachToDocument: true, sync: false });
|
||||
mount(tableRegistry, { ...config, store, localVue, attachToDocument: true });
|
||||
|
||||
beforeEach(() => {
|
||||
// This is needed due to console.error called by vue to emit a warning that stop the tests
|
||||
// see https://github.com/vuejs/vue-test-utils/issues/532
|
||||
Vue.config.silent = true;
|
||||
|
||||
store = new Vuex.Store({
|
||||
state: {
|
||||
isDeleteDisabled: false,
|
||||
|
@ -52,7 +47,6 @@ describe('table registry', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
Vue.config.silent = false;
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
|
@ -82,53 +76,53 @@ describe('table registry', () => {
|
|||
});
|
||||
|
||||
describe('multi select', () => {
|
||||
it('selecting a row should enable delete button', done => {
|
||||
it('selecting a row should enable delete button', () => {
|
||||
const deleteBtn = findDeleteButton();
|
||||
const checkboxes = findSelectCheckboxes();
|
||||
|
||||
expect(deleteBtn.attributes('disabled')).toBe('disabled');
|
||||
|
||||
checkboxes.at(0).trigger('click');
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(deleteBtn.attributes('disabled')).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('selecting all checkbox should select all rows and enable delete button', done => {
|
||||
it('selecting all checkbox should select all rows and enable delete button', () => {
|
||||
const selectAll = findSelectAllCheckbox();
|
||||
selectAll.trigger('click');
|
||||
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
const checkboxes = findSelectCheckboxes();
|
||||
const checked = checkboxes.filter(w => w.element.checked);
|
||||
expect(checked.length).toBe(checkboxes.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('deselecting select all checkbox should deselect all rows and disable delete button', done => {
|
||||
it('deselecting select all checkbox should deselect all rows and disable delete button', () => {
|
||||
const checkboxes = findSelectCheckboxes();
|
||||
const selectAll = findSelectAllCheckbox();
|
||||
selectAll.trigger('click');
|
||||
selectAll.trigger('click');
|
||||
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
const checked = checkboxes.filter(w => !w.element.checked);
|
||||
expect(checked.length).toBe(checkboxes.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete multiple items when multiple items are selected', done => {
|
||||
it('should delete multiple items when multiple items are selected', () => {
|
||||
const multiDeleteItems = jest.fn().mockResolvedValue();
|
||||
wrapper.setMethods({ multiDeleteItems });
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const selectAll = findSelectAllCheckbox();
|
||||
selectAll.trigger('click');
|
||||
|
||||
Vue.nextTick(() => {
|
||||
return wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
const selectAll = findSelectAllCheckbox();
|
||||
selectAll.trigger('click');
|
||||
return wrapper.vm.$nextTick();
|
||||
})
|
||||
.then(() => {
|
||||
const deleteBtn = findDeleteButton();
|
||||
expect(wrapper.vm.selectedItems).toEqual([0, 1]);
|
||||
expect(deleteBtn.attributes('disabled')).toEqual(undefined);
|
||||
|
@ -140,9 +134,7 @@ describe('table registry', () => {
|
|||
path: bulkDeletePath,
|
||||
items: [firstImage.tag, secondImage.tag],
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show an error message if bulkDeletePath is not set', () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import { truncateSha } from '~/lib/utils/text_utility';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
@ -10,10 +10,7 @@ describe('Evidence Block', () => {
|
|||
let wrapper;
|
||||
|
||||
const factory = (options = {}) => {
|
||||
const localVue = createLocalVue();
|
||||
|
||||
wrapper = mount(localVue.extend(EvidenceBlock), {
|
||||
localVue,
|
||||
wrapper = mount(EvidenceBlock, {
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import environmentRowComponent from '~/serverless/components/environment_row.vue';
|
||||
|
||||
import { mockServerlessFunctions, mockServerlessFunctionsDiffEnv } from '../mock_data';
|
||||
import { translate } from '~/serverless/utils';
|
||||
|
||||
const createComponent = (localVue, env, envName) =>
|
||||
shallowMount(environmentRowComponent, { localVue, propsData: { env, envName }, sync: false }).vm;
|
||||
const createComponent = (env, envName) =>
|
||||
shallowMount(environmentRowComponent, { propsData: { env, envName }, sync: false }).vm;
|
||||
|
||||
describe('environment row component', () => {
|
||||
describe('default global cluster case', () => {
|
||||
let localVue;
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
localVue = createLocalVue();
|
||||
vm = createComponent(localVue, translate(mockServerlessFunctions.functions)['*'], '*');
|
||||
vm = createComponent(translate(mockServerlessFunctions.functions)['*'], '*');
|
||||
});
|
||||
|
||||
afterEach(() => vm.$destroy());
|
||||
|
@ -44,15 +42,9 @@ describe('environment row component', () => {
|
|||
|
||||
describe('default named cluster case', () => {
|
||||
let vm;
|
||||
let localVue;
|
||||
|
||||
beforeEach(() => {
|
||||
localVue = createLocalVue();
|
||||
vm = createComponent(
|
||||
localVue,
|
||||
translate(mockServerlessFunctionsDiffEnv.functions).test,
|
||||
'test',
|
||||
);
|
||||
vm = createComponent(translate(mockServerlessFunctionsDiffEnv.functions).test, 'test');
|
||||
});
|
||||
|
||||
afterEach(() => vm.$destroy());
|
||||
|
|
|
@ -34,6 +34,7 @@ describe('Pipeline details header', () => {
|
|||
avatar_url: 'link',
|
||||
},
|
||||
retry_path: 'path',
|
||||
delete_path: 'path',
|
||||
},
|
||||
isLoading: false,
|
||||
};
|
||||
|
@ -55,12 +56,22 @@ describe('Pipeline details header', () => {
|
|||
});
|
||||
|
||||
describe('action buttons', () => {
|
||||
it('should call postAction when button action is clicked', () => {
|
||||
it('should call postAction when retry button action is clicked', done => {
|
||||
eventHub.$on('headerPostAction', action => {
|
||||
expect(action.path).toEqual('path');
|
||||
done();
|
||||
});
|
||||
|
||||
vm.$el.querySelector('button').click();
|
||||
vm.$el.querySelector('.js-retry-button').click();
|
||||
});
|
||||
|
||||
it('should fire modal event when delete button action is clicked', done => {
|
||||
vm.$root.$on('bv::modal::show', action => {
|
||||
expect(action.componentId).toEqual('pipeline-delete-modal');
|
||||
done();
|
||||
});
|
||||
|
||||
vm.$el.querySelector('.js-btn-delete-pipeline').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,17 +31,9 @@ describe('Header CI Component', () => {
|
|||
{
|
||||
label: 'Retry',
|
||||
path: 'path',
|
||||
type: 'button',
|
||||
cssClass: 'btn',
|
||||
isLoading: false,
|
||||
},
|
||||
{
|
||||
label: 'Go',
|
||||
path: 'path',
|
||||
type: 'link',
|
||||
cssClass: 'link',
|
||||
isLoading: false,
|
||||
},
|
||||
],
|
||||
hasSidebarButton: true,
|
||||
};
|
||||
|
@ -77,11 +69,10 @@ describe('Header CI Component', () => {
|
|||
});
|
||||
|
||||
it('should render provided actions', () => {
|
||||
expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON');
|
||||
expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label);
|
||||
expect(vm.$el.querySelector('.link').tagName).toEqual('A');
|
||||
expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label);
|
||||
expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path);
|
||||
const btn = vm.$el.querySelector('.btn');
|
||||
|
||||
expect(btn.tagName).toEqual('BUTTON');
|
||||
expect(btn.textContent.trim()).toEqual(props.actions[0].label);
|
||||
});
|
||||
|
||||
it('should show loading icon', done => {
|
||||
|
|
|
@ -15,42 +15,6 @@ describe Gitlab::Ci::Pipeline::Chain::Config::Content do
|
|||
stub_feature_flags(ci_root_config_content: false)
|
||||
end
|
||||
|
||||
context 'when bridge job is passed in as parameter' do
|
||||
let(:ci_config_path) { nil }
|
||||
let(:bridge) { create(:ci_bridge) }
|
||||
|
||||
before do
|
||||
command.bridge = bridge
|
||||
end
|
||||
|
||||
context 'when bridge job has downstream yaml' do
|
||||
before do
|
||||
allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
|
||||
end
|
||||
|
||||
it 'returns the content already available in command' do
|
||||
subject.perform!
|
||||
|
||||
expect(pipeline.config_source).to eq 'bridge_source'
|
||||
expect(command.config_content).to eq 'the-yaml'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when bridge job does not have downstream yaml' do
|
||||
before do
|
||||
allow(bridge).to receive(:yaml_for_downstream).and_return(nil)
|
||||
end
|
||||
|
||||
it 'returns the next available source' do
|
||||
subject.perform!
|
||||
|
||||
expect(pipeline.config_source).to eq 'auto_devops_source'
|
||||
template = Gitlab::Template::GitlabCiYmlTemplate.find('Beta/Auto-DevOps')
|
||||
expect(command.config_content).to eq(template.content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config is defined in a custom path in the repository' do
|
||||
let(:ci_config_path) { 'path/to/config.yml' }
|
||||
|
||||
|
@ -171,23 +135,6 @@ describe Gitlab::Ci::Pipeline::Chain::Config::Content do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when bridge job is passed in as parameter' do
|
||||
let(:ci_config_path) { nil }
|
||||
let(:bridge) { create(:ci_bridge) }
|
||||
|
||||
before do
|
||||
command.bridge = bridge
|
||||
allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
|
||||
end
|
||||
|
||||
it 'returns the content already available in command' do
|
||||
subject.perform!
|
||||
|
||||
expect(pipeline.config_source).to eq 'bridge_source'
|
||||
expect(command.config_content).to eq 'the-yaml'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config is defined in a custom path in the repository' do
|
||||
let(:ci_config_path) { 'path/to/config.yml' }
|
||||
let(:config_content_result) do
|
||||
|
|
|
@ -201,8 +201,6 @@ ci_pipelines:
|
|||
- sourced_pipelines
|
||||
- triggered_by_pipeline
|
||||
- triggered_pipelines
|
||||
- child_pipelines
|
||||
- parent_pipeline
|
||||
- downstream_bridges
|
||||
- job_artifacts
|
||||
- vulnerabilities_occurrence_pipelines
|
||||
|
|
|
@ -2716,114 +2716,4 @@ describe Ci::Pipeline, :mailer do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#parent_pipeline' do
|
||||
let(:project) { create(:project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
context 'when pipeline is triggered by a pipeline from the same project' do
|
||||
let(:upstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
|
||||
|
||||
before do
|
||||
create(:ci_sources_pipeline,
|
||||
source_pipeline: upstream_pipeline,
|
||||
source_project: project,
|
||||
pipeline: pipeline,
|
||||
project: project)
|
||||
end
|
||||
|
||||
it 'returns the parent pipeline' do
|
||||
expect(pipeline.parent_pipeline).to eq(upstream_pipeline)
|
||||
end
|
||||
|
||||
it 'is child' do
|
||||
expect(pipeline).to be_child
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline is triggered by a pipeline from another project' do
|
||||
let(:upstream_pipeline) { create(:ci_pipeline) }
|
||||
|
||||
before do
|
||||
create(:ci_sources_pipeline,
|
||||
source_pipeline: upstream_pipeline,
|
||||
source_project: upstream_pipeline.project,
|
||||
pipeline: pipeline,
|
||||
project: project)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(pipeline.parent_pipeline).to be_nil
|
||||
end
|
||||
|
||||
it 'is not child' do
|
||||
expect(pipeline).not_to be_child
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline is not triggered by a pipeline' do
|
||||
it 'returns nil' do
|
||||
expect(pipeline.parent_pipeline).to be_nil
|
||||
end
|
||||
|
||||
it 'is not child' do
|
||||
expect(pipeline).not_to be_child
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#child_pipelines' do
|
||||
let(:project) { create(:project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
context 'when pipeline triggered other pipelines on same project' do
|
||||
let(:downstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
|
||||
|
||||
before do
|
||||
create(:ci_sources_pipeline,
|
||||
source_pipeline: pipeline,
|
||||
source_project: pipeline.project,
|
||||
pipeline: downstream_pipeline,
|
||||
project: pipeline.project)
|
||||
end
|
||||
|
||||
it 'returns the child pipelines' do
|
||||
expect(pipeline.child_pipelines).to eq [downstream_pipeline]
|
||||
end
|
||||
|
||||
it 'is parent' do
|
||||
expect(pipeline).to be_parent
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline triggered other pipelines on another project' do
|
||||
let(:downstream_pipeline) { create(:ci_pipeline) }
|
||||
|
||||
before do
|
||||
create(:ci_sources_pipeline,
|
||||
source_pipeline: pipeline,
|
||||
source_project: pipeline.project,
|
||||
pipeline: downstream_pipeline,
|
||||
project: downstream_pipeline.project)
|
||||
end
|
||||
|
||||
it 'returns empty array' do
|
||||
expect(pipeline.child_pipelines).to be_empty
|
||||
end
|
||||
|
||||
it 'is not parent' do
|
||||
expect(pipeline).not_to be_parent
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline did not trigger any pipelines' do
|
||||
it 'returns empty array' do
|
||||
expect(pipeline.child_pipelines).to be_empty
|
||||
end
|
||||
|
||||
it 'is not parent' do
|
||||
expect(pipeline).not_to be_parent
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -123,6 +123,26 @@ describe PipelineEntity do
|
|||
end
|
||||
end
|
||||
|
||||
context 'delete path' do
|
||||
context 'user has ability to delete pipeline' do
|
||||
let(:project) { create(:project, namespace: user.namespace) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
it 'contains delete path' do
|
||||
expect(subject[:delete_path]).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'user does not have ability to delete pipeline' do
|
||||
let(:project) { create(:project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
it 'does not contain delete path' do
|
||||
expect(subject).not_to have_key(:delete_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline ref is empty' do
|
||||
let(:pipeline) { create(:ci_empty_pipeline) }
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::CreatePipelineService do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
|
||||
context 'custom config content' do
|
||||
let(:bridge) do
|
||||
double(:bridge, yaml_for_downstream: <<~YML
|
||||
rspec:
|
||||
script: rspec
|
||||
custom:
|
||||
script: custom
|
||||
YML
|
||||
)
|
||||
end
|
||||
|
||||
subject { service.execute(:push, bridge: bridge) }
|
||||
|
||||
it 'creates a pipeline using the content passed in as param' do
|
||||
expect(subject).to be_persisted
|
||||
expect(subject.builds.map(&:name)).to eq %w[rspec custom]
|
||||
expect(subject.config_source).to eq 'bridge_source'
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue