Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-10 09:07:16 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-10 09:07:16 +0300
commit32c68185530d784ce557342e69d96af2f43b0dcd (patch)
treee5372123e2d9bd24a9c8106f1961ff0b57ef834c
parent1e5e685f4fa6745023da8691edc272e39804a50a (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/editor/schema/ci.json14
-rw-r--r--app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue490
-rw-r--r--app/assets/javascripts/pipeline_new/index.js50
-rw-r--r--app/controllers/projects/pipelines_controller.rb1
-rw-r--r--config/feature_flags/development/run_pipeline_graphql.yml8
-rw-r--r--doc/ci/pipelines/index.md6
-rw-r--r--qa/qa/page/project/pipeline/new.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb90
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb111
-rw-r--r--spec/features/populate_new_pipeline_vars_with_params_spec.rb42
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb1
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb62
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml9
-rw-r--r--spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js456
16 files changed, 115 insertions, 1240 deletions
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 21c50acbf5d..f67519712cc 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -758,7 +758,17 @@
"type": "object",
"properties": {
"value": {
- "type": "string"
+ "type": "string",
+ "markdownDescription": "Default value of the variable. If used with `options`, `value` must be included in the array. [Learn More](https://docs.gitlab.com/ee/ci/pipelines/index.html#prefill-variables-in-manual-pipelines)"
+ },
+ "options": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "uniqueItems": true,
+ "markdownDescription": "A list of predefined values that users can select from in the **Run pipeline** page when running a pipeline manually. [Learn More](https://docs.gitlab.com/ee/ci/pipelines/index.html#configure-a-list-of-selectable-values-for-a-prefilled-variable)"
},
"description": {
"type": "string",
@@ -1922,4 +1932,4 @@
"additionalProperties": false
}
}
-}
+} \ No newline at end of file
diff --git a/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue b/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue
deleted file mode 100644
index c3d46d1954a..00000000000
--- a/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue
+++ /dev/null
@@ -1,490 +0,0 @@
-<script>
-import {
- GlAlert,
- GlIcon,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormTextarea,
- GlLink,
- GlSprintf,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import * as Sentry from '@sentry/browser';
-import { uniqueId } from 'lodash';
-import Vue from 'vue';
-import SafeHtml from '~/vue_shared/directives/safe_html';
-import axios from '~/lib/utils/axios_utils';
-import { backOff } from '~/lib/utils/common_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
-import { s__, __, n__ } from '~/locale';
-import {
- VARIABLE_TYPE,
- FILE_TYPE,
- CONFIG_VARIABLES_TIMEOUT,
- CC_VALIDATION_REQUIRED_ERROR,
-} from '../constants';
-import filterVariables from '../utils/filter_variables';
-import RefsDropdown from './refs_dropdown.vue';
-
-const i18n = {
- variablesDescription: s__(
- 'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.',
- ),
- defaultError: __('Something went wrong on our end. Please try again.'),
- refsLoadingErrorTitle: s__('Pipeline|Branches or tags could not be loaded.'),
- submitErrorTitle: s__('Pipeline|Pipeline cannot be run.'),
- warningTitle: __('The form contains the following warning:'),
- maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'),
- removeVariableLabel: s__('CiVariables|Remove variable'),
-};
-
-export default {
- typeOptions: {
- [VARIABLE_TYPE]: __('Variable'),
- [FILE_TYPE]: __('File'),
- },
- i18n,
- formElementClasses: 'gl-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0',
- // this height value is used inline on the textarea to match the input field height
- // it's used to prevent the overwrite if 'gl-h-7' or 'gl-h-7!' were used
- textAreaStyle: { height: '32px' },
- components: {
- GlAlert,
- GlIcon,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormTextarea,
- GlLink,
- GlSprintf,
- GlLoadingIcon,
- RefsDropdown,
- CcValidationRequiredAlert: () =>
- import('ee_component/billings/components/cc_validation_required_alert.vue'),
- },
- directives: { SafeHtml },
- props: {
- pipelinesPath: {
- type: String,
- required: true,
- },
- configVariablesPath: {
- type: String,
- required: true,
- },
- defaultBranch: {
- type: String,
- required: true,
- },
- projectId: {
- type: String,
- required: true,
- },
- settingsLink: {
- type: String,
- required: true,
- },
- fileParams: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- refParam: {
- type: String,
- required: false,
- default: '',
- },
- variableParams: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- maxWarnings: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- refValue: {
- shortName: this.refParam,
- },
- form: {},
- errorTitle: null,
- error: null,
- warnings: [],
- totalWarnings: 0,
- isWarningDismissed: false,
- isLoading: false,
- submitted: false,
- ccAlertDismissed: false,
- };
- },
- computed: {
- overMaxWarningsLimit() {
- return this.totalWarnings > this.maxWarnings;
- },
- warningsSummary() {
- return n__('%d warning found:', '%d warnings found:', this.warnings.length);
- },
- summaryMessage() {
- return this.overMaxWarningsLimit ? i18n.maxWarningsSummary : this.warningsSummary;
- },
- shouldShowWarning() {
- return this.warnings.length > 0 && !this.isWarningDismissed;
- },
- refShortName() {
- return this.refValue.shortName;
- },
- refFullName() {
- return this.refValue.fullName;
- },
- variables() {
- return this.form[this.refFullName]?.variables ?? [];
- },
- descriptions() {
- return this.form[this.refFullName]?.descriptions ?? {};
- },
- ccRequiredError() {
- return this.error === CC_VALIDATION_REQUIRED_ERROR && !this.ccAlertDismissed;
- },
- },
- watch: {
- refValue() {
- this.loadConfigVariablesForm();
- },
- },
- created() {
- // this is needed until we add support for ref type in url query strings
- // ensure default branch is called with full ref on load
- // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
- if (this.refValue.shortName === this.defaultBranch) {
- this.refValue.fullName = `refs/heads/${this.refValue.shortName}`;
- }
-
- this.loadConfigVariablesForm();
- },
- methods: {
- addEmptyVariable(refValue) {
- const { variables } = this.form[refValue];
-
- const lastVar = variables[variables.length - 1];
- if (lastVar?.key === '' && lastVar?.value === '') {
- return;
- }
-
- variables.push({
- uniqueId: uniqueId(`var-${refValue}`),
- variable_type: VARIABLE_TYPE,
- key: '',
- value: '',
- });
- },
- setVariable(refValue, type, key, value) {
- const { variables } = this.form[refValue];
-
- const variable = variables.find((v) => v.key === key);
- if (variable) {
- variable.type = type;
- variable.value = value;
- } else {
- variables.push({
- uniqueId: uniqueId(`var-${refValue}`),
- key,
- value,
- variable_type: type,
- });
- }
- },
- setVariableType(key, type) {
- const { variables } = this.form[this.refFullName];
- const variable = variables.find((v) => v.key === key);
- variable.variable_type = type;
- },
- setVariableParams(refValue, type, paramsObj) {
- Object.entries(paramsObj).forEach(([key, value]) => {
- this.setVariable(refValue, type, key, value);
- });
- },
- removeVariable(index) {
- this.variables.splice(index, 1);
- },
- canRemove(index) {
- return index < this.variables.length - 1;
- },
- loadConfigVariablesForm() {
- // Skip when variables already cached in `form`
- if (this.form[this.refFullName]) {
- return;
- }
-
- this.fetchConfigVariables(this.refFullName || this.refShortName)
- .then(({ descriptions, params }) => {
- Vue.set(this.form, this.refFullName, {
- variables: [],
- descriptions,
- });
-
- // Add default variables from yml
- this.setVariableParams(this.refFullName, VARIABLE_TYPE, params);
- })
- .catch(() => {
- Vue.set(this.form, this.refFullName, {
- variables: [],
- descriptions: {},
- });
- })
- .finally(() => {
- // Add/update variables, e.g. from query string
- if (this.variableParams) {
- this.setVariableParams(this.refFullName, VARIABLE_TYPE, this.variableParams);
- }
- if (this.fileParams) {
- this.setVariableParams(this.refFullName, FILE_TYPE, this.fileParams);
- }
-
- // Adds empty var at the end of the form
- this.addEmptyVariable(this.refFullName);
- });
- },
- fetchConfigVariables(refValue) {
- this.isLoading = true;
-
- return backOff((next, stop) => {
- axios
- .get(this.configVariablesPath, {
- params: {
- sha: refValue,
- },
- })
- .then(({ data, status }) => {
- if (status === httpStatusCodes.NO_CONTENT) {
- next();
- } else {
- this.isLoading = false;
- stop(data);
- }
- })
- .catch((error) => {
- stop(error);
- });
- }, CONFIG_VARIABLES_TIMEOUT)
- .then((data) => {
- const params = {};
- const descriptions = {};
-
- Object.entries(data).forEach(([key, { value, description }]) => {
- if (description) {
- params[key] = value;
- descriptions[key] = description;
- }
- });
-
- return { params, descriptions };
- })
- .catch((error) => {
- this.isLoading = false;
-
- Sentry.captureException(error);
-
- return { params: {}, descriptions: {} };
- });
- },
- createPipeline() {
- this.submitted = true;
- this.ccAlertDismissed = false;
-
- return axios
- .post(this.pipelinesPath, {
- // send shortName as fall back for query params
- // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
- ref: this.refValue.fullName || this.refShortName,
- variables_attributes: filterVariables(this.variables),
- })
- .then(({ data }) => {
- redirectTo(`${this.pipelinesPath}/${data.id}`);
- })
- .catch((err) => {
- // always re-enable submit button
- this.submitted = false;
-
- const {
- errors = [],
- warnings = [],
- total_warnings: totalWarnings = 0,
- } = err.response.data;
- const [error] = errors;
-
- this.reportError({
- title: i18n.submitErrorTitle,
- error,
- warnings,
- totalWarnings,
- });
- });
- },
- onRefsLoadingError(error) {
- this.reportError({ title: i18n.refsLoadingErrorTitle });
-
- Sentry.captureException(error);
- },
- reportError({ title = null, error = i18n.defaultError, warnings = [], totalWarnings = 0 }) {
- this.errorTitle = title;
- this.error = error;
- this.warnings = warnings;
- this.totalWarnings = totalWarnings;
- },
- dismissError() {
- this.ccAlertDismissed = true;
- this.error = null;
- },
- },
-};
-</script>
-
-<template>
- <gl-form @submit.prevent="createPipeline">
- <cc-validation-required-alert v-if="ccRequiredError" class="gl-pb-5" @dismiss="dismissError" />
- <gl-alert
- v-else-if="error"
- :title="errorTitle"
- :dismissible="false"
- variant="danger"
- class="gl-mb-4"
- data-testid="run-pipeline-error-alert"
- >
- <span v-safe-html="error"></span>
- </gl-alert>
- <gl-alert
- v-if="shouldShowWarning"
- :title="$options.i18n.warningTitle"
- variant="warning"
- class="gl-mb-4"
- data-testid="run-pipeline-warning-alert"
- @dismiss="isWarningDismissed = true"
- >
- <details>
- <summary>
- <gl-sprintf :message="summaryMessage">
- <template #total>
- {{ totalWarnings }}
- </template>
- <template #warningsDisplayed>
- {{ maxWarnings }}
- </template>
- </gl-sprintf>
- </summary>
- <p
- v-for="(warning, index) in warnings"
- :key="`warning-${index}`"
- data-testid="run-pipeline-warning"
- >
- {{ warning }}
- </p>
- </details>
- </gl-alert>
- <gl-form-group :label="s__('Pipeline|Run for branch name or tag')">
- <refs-dropdown v-model="refValue" @loadingError="onRefsLoadingError" />
- </gl-form-group>
-
- <gl-loading-icon v-if="isLoading" class="gl-mb-5" size="lg" />
-
- <gl-form-group v-else :label="s__('Pipeline|Variables')">
- <div
- v-for="(variable, index) in variables"
- :key="variable.uniqueId"
- class="gl-mb-3 gl-pb-2"
- data-testid="ci-variable-row"
- data-qa-selector="ci_variable_row_container"
- >
- <div
- class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row"
- >
- <gl-dropdown
- :text="$options.typeOptions[variable.variable_type]"
- :class="$options.formElementClasses"
- data-testid="pipeline-form-ci-variable-type"
- >
- <gl-dropdown-item
- v-for="type in Object.keys($options.typeOptions)"
- :key="type"
- @click="setVariableType(variable.key, type)"
- >
- {{ $options.typeOptions[type] }}
- </gl-dropdown-item>
- </gl-dropdown>
- <gl-form-input
- v-model="variable.key"
- :placeholder="s__('CiVariables|Input variable key')"
- :class="$options.formElementClasses"
- data-testid="pipeline-form-ci-variable-key"
- data-qa-selector="ci_variable_key_field"
- @change="addEmptyVariable(refFullName)"
- />
- <gl-form-textarea
- v-model="variable.value"
- :placeholder="s__('CiVariables|Input variable value')"
- class="gl-mb-3"
- :style="$options.textAreaStyle"
- :no-resize="false"
- data-testid="pipeline-form-ci-variable-value"
- data-qa-selector="ci_variable_value_field"
- />
-
- <template v-if="variables.length > 1">
- <gl-button
- v-if="canRemove(index)"
- class="gl-md-ml-3 gl-mb-3"
- data-testid="remove-ci-variable-row"
- variant="danger"
- category="secondary"
- :aria-label="$options.i18n.removeVariableLabel"
- @click="removeVariable(index)"
- >
- <gl-icon class="gl-mr-0! gl-display-none gl-md-display-block" name="clear" />
- <span class="gl-md-display-none">{{ $options.i18n.removeVariableLabel }}</span>
- </gl-button>
- <gl-button
- v-else
- class="gl-md-ml-3 gl-mb-3 gl-display-none gl-md-display-block gl-visibility-hidden"
- icon="clear"
- :aria-label="$options.i18n.removeVariableLabel"
- />
- </template>
- </div>
- <div v-if="descriptions[variable.key]" class="gl-text-gray-500 gl-mb-3">
- {{ descriptions[variable.key] }}
- </div>
- </div>
-
- <template #description
- ><gl-sprintf :message="$options.i18n.variablesDescription">
- <template #link="{ content }">
- <gl-link :href="settingsLink">{{ content }}</gl-link>
- </template>
- </gl-sprintf></template
- >
- </gl-form-group>
- <div class="gl-pt-5 gl-display-flex">
- <gl-button
- type="submit"
- category="primary"
- variant="confirm"
- class="js-no-auto-disable gl-mr-3"
- data-qa-selector="run_pipeline_button"
- data-testid="run_pipeline_button"
- :disabled="submitted"
- >{{ s__('Pipeline|Run pipeline') }}</gl-button
- >
- <gl-button :href="pipelinesPath">{{ __('Cancel') }}</gl-button>
- </div>
- </gl-form>
-</template>
diff --git a/app/assets/javascripts/pipeline_new/index.js b/app/assets/javascripts/pipeline_new/index.js
index 60b4c93d1d5..71c76aeab36 100644
--- a/app/assets/javascripts/pipeline_new/index.js
+++ b/app/assets/javascripts/pipeline_new/index.js
@@ -1,53 +1,9 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import LegacyPipelineNewForm from './components/legacy_pipeline_new_form.vue';
import PipelineNewForm from './components/pipeline_new_form.vue';
import { resolvers } from './graphql/resolvers';
-const mountLegacyPipelineNewForm = (el) => {
- const {
- // provide/inject
- projectRefsEndpoint,
-
- // props
- configVariablesPath,
- defaultBranch,
- fileParam,
- maxWarnings,
- pipelinesPath,
- projectId,
- refParam,
- settingsLink,
- varParam,
- } = el.dataset;
-
- const variableParams = JSON.parse(varParam);
- const fileParams = JSON.parse(fileParam);
-
- return new Vue({
- el,
- provide: {
- projectRefsEndpoint,
- },
- render(createElement) {
- return createElement(LegacyPipelineNewForm, {
- props: {
- configVariablesPath,
- defaultBranch,
- fileParams,
- maxWarnings: Number(maxWarnings),
- pipelinesPath,
- projectId,
- refParam,
- settingsLink,
- variableParams,
- },
- });
- },
- });
-};
-
const mountPipelineNewForm = (el) => {
const {
// provide/inject
@@ -101,9 +57,5 @@ const mountPipelineNewForm = (el) => {
export default () => {
const el = document.getElementById('js-new-pipeline');
- if (gon.features?.runPipelineGraphql) {
- mountPipelineNewForm(el);
- } else {
- mountLegacyPipelineNewForm(el);
- }
+ mountPipelineNewForm(el);
};
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 7d1a75ae449..ea8c556dc49 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -26,7 +26,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:pipeline_tabs_vue, @project)
- push_frontend_feature_flag(:run_pipeline_graphql, @project)
end
# Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
diff --git a/config/feature_flags/development/run_pipeline_graphql.yml b/config/feature_flags/development/run_pipeline_graphql.yml
deleted file mode 100644
index 78d8afbbee5..00000000000
--- a/config/feature_flags/development/run_pipeline_graphql.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: run_pipeline_graphql
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96633
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372310
-milestone: '15.4'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index 2126997e25f..e013f9475d8 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -192,11 +192,7 @@ In this example:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/363660) in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `run_pipeline_graphql`. Disabled by default.
> - The `options` keyword was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105502) in GitLab 15.7.
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `run_pipeline_graphql`.
-The feature is not ready for production use.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106038) in GitLab 15.7. Feature flag `run_pipeline_graphql` removed.
You can define an array of CI/CD variable values the user can select from when running a pipeline manually.
These values are in a dropdown list in the **Run pipeline** page. Add the list of
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index f56f69fc077..1d85d072e34 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -5,15 +5,6 @@ module QA
module Project
module Pipeline
class New < QA::Page::Base
- # TODO: Replace legacy form with the new form when run_pipeline_graphql is enabled by default
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- view 'app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue' do
- element :run_pipeline_button, required: true
- element :ci_variable_row_container
- element :ci_variable_key_field
- element :ci_variable_value_field
- end
-
view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
element :run_pipeline_button, required: true
element :ci_variable_row_container
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
index 4a0a8be3659..5dda8b04805 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
@@ -2,10 +2,7 @@
module QA
RSpec.describe 'Verify', :runner do
- describe 'Pipeline with customizable variable', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :project
- } do
+ describe 'Pipeline with customizable variable' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:pipeline_job_name) { 'customizable-variable' }
let(:variable_custom_value) { 'Custom Foo' }
@@ -48,74 +45,45 @@ module QA
end
end
- shared_examples 'pipeline with custom variable' do
- before do
- Flow::Login.sign_in
+ before do
+ Flow::Login.sign_in
- project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
-
- after do
- runner&.remove_via_api!
- end
-
- it 'manually creates a pipeline and uses the defined custom variable value' do
- Page::Project::Pipeline::New.perform do |new|
- new.configure_variable(value: variable_custom_value)
- new.click_run_pipeline_button
- end
-
- Page::Project::Pipeline::Show.perform do |show|
- Support::Waiter.wait_until { show.passed? }
- end
-
- job = Resource::Job.fabricate_via_api! do |job|
- job.id = project.job_by_name(pipeline_job_name)[:id]
- job.name = pipeline_job_name
- job.project = project
- end
-
- job.visit!
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
+ end
- Page::Project::Job::Show.perform do |show|
- expect(show.output).to have_content(variable_custom_value)
- end
- end
+ after do
+ runner&.remove_via_api!
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context(
- 'with feature flag disabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/361814'
- ) do
- before do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ it 'manually creates a pipeline and uses the defined custom variable value',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975' do
+ Page::Project::Pipeline::New.perform do |new|
+ new.configure_variable(value: variable_custom_value)
+ new.click_run_pipeline_button
end
- it_behaves_like 'pipeline with custom variable'
- end
-
- context(
- 'with feature flag enabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975'
- ) do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql, project: project)
+ Page::Project::Pipeline::Show.perform do |show|
+ Support::Waiter.wait_until { show.passed? }
end
- after do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.id = project.job_by_name(pipeline_job_name)[:id]
+ job.name = pipeline_job_name
+ job.project = project
end
- it_behaves_like 'pipeline with custom variable'
+ job.visit!
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content(variable_custom_value)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
index c5112ebedc6..1878292015e 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
@@ -2,10 +2,7 @@
module QA
RSpec.describe 'Verify' do
- describe 'Pipeline with prefill variables', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :global
- } do
+ describe 'Pipeline with prefill variables' do
let(:prefill_variable_description1) { Faker::Lorem.sentence }
let(:prefill_variable_value1) { Faker::Lorem.word }
let(:prefill_variable_value5) { Faker::Lorem.word }
@@ -50,93 +47,51 @@ module QA
end
end
- shared_examples 'pre-filled variables form' do |testcase|
- it 'shows only variables with description as prefill variables on the run pipeline page', testcase: testcase do
- Page::Project::Pipeline::New.perform do |new|
- aggregate_failures do
- expect(new).to have_field('Input variable key', with: 'TEST1')
- expect(new).to have_field('Input variable value', with: prefill_variable_value1)
- expect(new).to have_content(prefill_variable_description1)
+ before do
+ Flow::Login.sign_in
+ project.visit!
- expect(new).to have_field('Input variable key', with: 'TEST2')
- expect(new).to have_field('Input variable value', with: '')
- expect(new).to have_content(prefill_variable_description2)
+ # Navigate to Run Pipeline page
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- expect(new).not_to have_field('Input variable key', with: 'TEST3')
- expect(new).not_to have_field('Input variable key', with: 'TEST4')
-
- # Legacy and GQL app will render the variable field differently
- expect(new).to have_field('Input variable key', with: 'TEST5')
- expect(new).to have_content(prefill_variable_description5)
- end
- end
- end
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context 'with feature flag disabled' do
- before do
- Flow::Login.sign_in
- project.visit!
+ it 'shows only variables with description as prefill variables on the run pipeline page',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new).to have_field('Input variable key', with: 'TEST1')
+ expect(new).to have_field('Input variable value', with: prefill_variable_value1)
+ expect(new).to have_content(prefill_variable_description1)
- # Navigate to Run Pipeline page
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ expect(new).to have_field('Input variable key', with: 'TEST2')
+ expect(new).to have_field('Input variable value', with: '')
+ expect(new).to have_content(prefill_variable_description2)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
+ expect(new).not_to have_field('Input variable key', with: 'TEST3')
+ expect(new).not_to have_field('Input variable key', with: 'TEST4')
- it_behaves_like 'pre-filled variables form', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371204'
-
- it 'does not prefill dropdown variables but renders them as input fields',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383819' do
- Page::Project::Pipeline::New.perform do |new|
- expect(new.has_variable_dropdown?).to be(false)
- expect(new).to have_field('Input variable value', with: 'FOO')
+ expect(new).to have_field('Input variable key', with: 'TEST5')
+ expect(new).to have_content(prefill_variable_description5)
end
end
end
- context 'with feature flag enabled' do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql)
- sleep 60
-
- Flow::Login.sign_in
- project.visit!
-
- # Navigate to Run Pipeline page
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
-
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
-
- after do
- Runtime::Feature.disable(:run_pipeline_graphql)
- sleep 60
- end
-
- it_behaves_like 'pre-filled variables form', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977'
-
- it 'shows dropdown for variables with description, value, and options defined',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383820' do
- Page::Project::Pipeline::New.perform do |new|
- aggregate_failures do
- expect(new.variable_dropdown).to have_text('FOO')
+ it 'shows dropdown for variables with description, value, and options defined',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383820' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new.variable_dropdown).to have_text('FOO')
- new.click_variable_dropdown
+ new.click_variable_dropdown
- expect(new.variable_dropdown_item_with_index(0)).to have_text(prefill_variable_value5)
- expect(new.variable_dropdown_item_with_index(1)).to have_text('FOO')
- end
+ expect(new.variable_dropdown_item_with_index(0)).to have_text(prefill_variable_value5)
+ expect(new.variable_dropdown_item_with_index(1)).to have_text('FOO')
end
end
end
diff --git a/spec/features/populate_new_pipeline_vars_with_params_spec.rb b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
index 0943b018693..a83b5a81a41 100644
--- a/spec/features/populate_new_pipeline_vars_with_params_spec.rb
+++ b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
@@ -7,42 +7,24 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js, featur
let(:project) { create(:project) }
let(:page_path) { new_project_pipeline_path(project) }
- shared_examples 'form pre-filled with URL params' do
- before do
- sign_in(user)
- project.add_maintainer(user)
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
- visit "#{page_path}?var[key1]=value1&file_var[key2]=value2"
- end
-
- it "var[key1]=value1 populates env_var variable correctly" do
- page.within(all("[data-testid='ci-variable-row']")[0]) do
- expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1')
- expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1')
- end
- end
-
- it "file_var[key2]=value2 populates file variable correctly" do
- page.within(all("[data-testid='ci-variable-row']")[1]) do
- expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2')
- expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2')
- end
- end
+ visit "#{page_path}?var[key1]=value1&file_var[key2]=value2"
end
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: false)
+ it "var[key1]=value1 populates env_var variable correctly" do
+ page.within(all("[data-testid='ci-variable-row']")[0]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key1')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value1')
end
-
- it_behaves_like 'form pre-filled with URL params'
end
- context 'when feature flag is enabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: true)
+ it "file_var[key2]=value2 populates file variable correctly" do
+ page.within(all("[data-testid='ci-variable-row']")[1]) do
+ expect(find("[data-testid='pipeline-form-ci-variable-key']").value).to eq('key2')
+ expect(find("[data-testid='pipeline-form-ci-variable-value']").value).to eq('value2')
end
-
- it_behaves_like 'form pre-filled with URL params'
end
end
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
index 0da5fc81da0..b6ae4144709 100644
--- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb
+++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
@@ -673,7 +673,6 @@ RSpec.describe 'Pipelines', :js, feature_category: :projects do
let(:project) { create(:project, :repository) }
before do
- stub_feature_flags(run_pipeline_graphql: false)
visit new_project_pipeline_path(project)
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 14151ad0a6a..3bdabd672c7 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -663,7 +663,19 @@ RSpec.describe 'Pipelines', :js, feature_category: :projects do
describe 'POST /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
- shared_examples 'run pipeline form with gitlab-ci.yml' do
+ before do
+ visit new_project_pipeline_path(project)
+ end
+
+ context 'for valid commit', :js do
+ before do
+ click_button project.default_branch
+ wait_for_requests
+
+ find('p', text: 'master').click
+ wait_for_requests
+ end
+
context 'with gitlab-ci.yml', :js do
before do
stub_ci_pipeline_to_return_yaml_file
@@ -697,9 +709,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :projects do
end
end
end
- end
- shared_examples 'run pipeline form without gitlab-ci.yml' do
context 'without gitlab-ci.yml' do
before do
click_on 'Run pipeline'
@@ -719,52 +729,6 @@ RSpec.describe 'Pipelines', :js, feature_category: :projects do
end
end
end
-
- # Run Pipeline form with REST endpoints
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: false)
- visit new_project_pipeline_path(project)
- end
-
- context 'for valid commit', :js do
- before do
- click_button project.default_branch
- wait_for_requests
-
- find('p', text: 'master').click
- wait_for_requests
- end
-
- it_behaves_like 'run pipeline form with gitlab-ci.yml'
-
- it_behaves_like 'run pipeline form without gitlab-ci.yml'
- end
- end
-
- # Run Pipeline form with GraphQL
- context 'with feature flag enabled' do
- before do
- stub_feature_flags(run_pipeline_graphql: true)
- visit new_project_pipeline_path(project)
- end
-
- context 'for valid commit', :js do
- before do
- click_button project.default_branch
- wait_for_requests
-
- find('p', text: 'master').click
- wait_for_requests
- end
-
- it_behaves_like 'run pipeline form with gitlab-ci.yml'
-
- it_behaves_like 'run pipeline form without gitlab-ci.yml'
- end
- end
end
describe 'Reset runner caches' do
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js
index a06f81e4d1c..b8b91eb8167 100644
--- a/spec/frontend/editor/schema/ci/ci_schema_spec.js
+++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js
@@ -45,6 +45,7 @@ import ProjectPathIncludeNoSlashYaml from './yaml_tests/negative_tests/project_p
import ProjectPathIncludeTailSlashYaml from './yaml_tests/negative_tests/project_path/include/tailing_slash.yml';
import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
import TriggerNegative from './yaml_tests/negative_tests/trigger.yml';
+import VariablesInvalidOptionsYaml from './yaml_tests/negative_tests/variables/invalid_options.yml';
import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
@@ -113,6 +114,7 @@ describe('negative tests', () => {
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,
+ VariablesInvalidOptionsYaml,
VariablesInvalidSyntaxDescYaml,
VariablesWrongSyntaxUsageExpand,
ProjectPathIncludeEmptyYaml,
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml
new file mode 100644
index 00000000000..aac4c4e456d
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/variables/invalid_options.yml
@@ -0,0 +1,4 @@
+variables:
+ INVALID_OPTIONS:
+ value: "staging"
+ options: "staging" # must be an array
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
index 53d020c432f..5c91de9be70 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/variables.yml
@@ -4,11 +4,18 @@ variables:
FOO:
value: "BAR"
description: "A single value variable"
- DEPLOY_ENVIRONMENT:
+ VAR_WITH_DESCRIPTION:
description: "A multi-value variable"
RAW_VAR:
value: "Hello $FOO"
expand: false
+ VAR_WITH_OPTIONS:
+ value: "staging"
+ options:
+ - "production"
+ - "staging"
+ - "canary"
+ description: "The deployment target. Set to 'production' by default."
rspec:
script: rspec
diff --git a/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js b/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js
deleted file mode 100644
index 512b152f106..00000000000
--- a/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js
+++ /dev/null
@@ -1,456 +0,0 @@
-import { GlForm, GlSprintf, GlLoadingIcon } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import CreditCardValidationRequiredAlert from 'ee_component/billings/components/cc_validation_required_alert.vue';
-import { TEST_HOST } from 'helpers/test_constants';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
-import LegacyPipelineNewForm from '~/pipeline_new/components/legacy_pipeline_new_form.vue';
-import RefsDropdown from '~/pipeline_new/components/refs_dropdown.vue';
-import {
- mockQueryParams,
- mockPostParams,
- mockProjectId,
- mockError,
- mockRefs,
- mockCreditCardValidationRequiredError,
-} from '../mock_data';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- redirectTo: jest.fn(),
-}));
-
-const projectRefsEndpoint = '/root/project/refs';
-const pipelinesPath = '/root/project/-/pipelines';
-const configVariablesPath = '/root/project/-/pipelines/config_variables';
-const newPipelinePostResponse = { id: 1 };
-const defaultBranch = 'main';
-
-describe('Pipeline New Form', () => {
- let wrapper;
- let mock;
- let dummySubmitEvent;
-
- const findForm = () => wrapper.findComponent(GlForm);
- const findRefsDropdown = () => wrapper.findComponent(RefsDropdown);
- const findSubmitButton = () => wrapper.find('[data-testid="run_pipeline_button"]');
- const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
- const findRemoveIcons = () => wrapper.findAll('[data-testid="remove-ci-variable-row"]');
- const findDropdowns = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-type"]');
- const findKeyInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-key"]');
- const findValueInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-value"]');
- const findErrorAlert = () => wrapper.find('[data-testid="run-pipeline-error-alert"]');
- const findWarningAlert = () => wrapper.find('[data-testid="run-pipeline-warning-alert"]');
- const findWarningAlertSummary = () => findWarningAlert().findComponent(GlSprintf);
- const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findCCAlert = () => wrapper.findComponent(CreditCardValidationRequiredAlert);
- const getFormPostParams = () => JSON.parse(mock.history.post[0].data);
-
- const selectBranch = (branch) => {
- // Select a branch in the dropdown
- findRefsDropdown().vm.$emit('input', {
- shortName: branch,
- fullName: `refs/heads/${branch}`,
- });
- };
-
- const createComponent = (props = {}, method = shallowMount) => {
- wrapper = method(LegacyPipelineNewForm, {
- provide: {
- projectRefsEndpoint,
- },
- propsData: {
- projectId: mockProjectId,
- pipelinesPath,
- configVariablesPath,
- defaultBranch,
- refParam: defaultBranch,
- settingsLink: '',
- maxWarnings: 25,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {});
- mock.onGet(projectRefsEndpoint).reply(httpStatusCodes.OK, mockRefs);
-
- dummySubmitEvent = {
- preventDefault: jest.fn(),
- };
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
-
- mock.restore();
- });
-
- describe('Form', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
-
- await waitForPromises();
- });
-
- it('displays the correct values for the provided query params', async () => {
- expect(findDropdowns().at(0).props('text')).toBe('Variable');
- expect(findDropdowns().at(1).props('text')).toBe('File');
- expect(findRefsDropdown().props('value')).toEqual({ shortName: 'tag-1' });
- expect(findVariableRows()).toHaveLength(3);
- });
-
- it('displays a variable from provided query params', () => {
- expect(findKeyInputs().at(0).element.value).toBe('test_var');
- expect(findValueInputs().at(0).element.value).toBe('test_var_val');
- });
-
- it('displays an empty variable for the user to fill out', async () => {
- expect(findKeyInputs().at(2).element.value).toBe('');
- expect(findValueInputs().at(2).element.value).toBe('');
- expect(findDropdowns().at(2).props('text')).toBe('Variable');
- });
-
- it('does not display remove icon for last row', () => {
- expect(findRemoveIcons()).toHaveLength(2);
- });
-
- it('removes ci variable row on remove icon button click', async () => {
- findRemoveIcons().at(1).trigger('click');
-
- await nextTick();
-
- expect(findVariableRows()).toHaveLength(2);
- });
-
- it('creates blank variable on input change event', async () => {
- const input = findKeyInputs().at(2);
- input.element.value = 'test_var_2';
- input.trigger('change');
-
- await nextTick();
-
- expect(findVariableRows()).toHaveLength(4);
- expect(findKeyInputs().at(3).element.value).toBe('');
- expect(findValueInputs().at(3).element.value).toBe('');
- });
- });
-
- describe('Pipeline creation', () => {
- beforeEach(async () => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
-
- await waitForPromises();
- });
-
- it('does not submit the native HTML form', async () => {
- createComponent();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- expect(dummySubmitEvent.preventDefault).toHaveBeenCalled();
- });
-
- it('disables the submit button immediately after submitting', async () => {
- createComponent();
-
- expect(findSubmitButton().props('disabled')).toBe(false);
-
- findForm().vm.$emit('submit', dummySubmitEvent);
- await waitForPromises();
-
- expect(findSubmitButton().props('disabled')).toBe(true);
- });
-
- it('creates pipeline with full ref and variables', async () => {
- createComponent();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
- await waitForPromises();
-
- expect(getFormPostParams().ref).toEqual(`refs/heads/${defaultBranch}`);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
- });
-
- it('creates a pipeline with short ref and variables from the query params', async () => {
- createComponent(mockQueryParams);
-
- await waitForPromises();
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
-
- expect(getFormPostParams()).toEqual(mockPostParams);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
- });
- });
-
- describe('When the ref has been changed', () => {
- beforeEach(async () => {
- createComponent({}, mount);
-
- await waitForPromises();
- });
- it('variables persist between ref changes', async () => {
- selectBranch('main');
-
- await waitForPromises();
-
- const mainInput = findKeyInputs().at(0);
- mainInput.element.value = 'build_var';
- mainInput.trigger('change');
-
- await nextTick();
-
- selectBranch('branch-1');
-
- await waitForPromises();
-
- const branchOneInput = findKeyInputs().at(0);
- branchOneInput.element.value = 'deploy_var';
- branchOneInput.trigger('change');
-
- await nextTick();
-
- selectBranch('main');
-
- await waitForPromises();
-
- expect(findKeyInputs().at(0).element.value).toBe('build_var');
- expect(findVariableRows().length).toBe(2);
-
- selectBranch('branch-1');
-
- await waitForPromises();
-
- expect(findKeyInputs().at(0).element.value).toBe('deploy_var');
- expect(findVariableRows().length).toBe(2);
- });
- });
-
- describe('when yml defines a variable', () => {
- const mockYmlKey = 'yml_var';
- const mockYmlValue = 'yml_var_val';
- const mockYmlMultiLineValue = `A value
- with multiple
- lines`;
- const mockYmlDesc = 'A var from yml.';
-
- it('loading icon is shown when content is requested and hidden when received', async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: mockYmlDesc,
- },
- });
-
- expect(findLoadingIcon().exists()).toBe(true);
-
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('multi-line strings are added to the value field without removing line breaks', async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlMultiLineValue,
- description: mockYmlDesc,
- },
- });
-
- await waitForPromises();
-
- expect(findValueInputs().at(0).element.value).toBe(mockYmlMultiLineValue);
- });
-
- describe('with description', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: mockYmlDesc,
- },
- });
-
- await waitForPromises();
- });
-
- it('displays all the variables', async () => {
- expect(findVariableRows()).toHaveLength(4);
- });
-
- it('displays a variable from yml', () => {
- expect(findKeyInputs().at(0).element.value).toBe(mockYmlKey);
- expect(findValueInputs().at(0).element.value).toBe(mockYmlValue);
- });
-
- it('displays a variable from provided query params', () => {
- expect(findKeyInputs().at(1).element.value).toBe('test_var');
- expect(findValueInputs().at(1).element.value).toBe('test_var_val');
- });
-
- it('adds a description to the first variable from yml', () => {
- expect(findVariableRows().at(0).text()).toContain(mockYmlDesc);
- });
-
- it('removes the description when a variable key changes', async () => {
- findKeyInputs().at(0).element.value = 'yml_var_modified';
- findKeyInputs().at(0).trigger('change');
-
- await nextTick();
-
- expect(findVariableRows().at(0).text()).not.toContain(mockYmlDesc);
- });
- });
-
- describe('without description', () => {
- beforeEach(async () => {
- createComponent(mockQueryParams, mount);
-
- mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
- [mockYmlKey]: {
- value: mockYmlValue,
- description: null,
- },
- yml_var2: {
- value: 'yml_var2_val',
- },
- yml_var3: {
- description: '',
- },
- });
-
- await waitForPromises();
- });
-
- it('displays all the variables', async () => {
- expect(findVariableRows()).toHaveLength(3);
- });
- });
- });
-
- describe('Form errors and warnings', () => {
- beforeEach(() => {
- createComponent();
- });
-
- describe('when the refs cannot be loaded', () => {
- beforeEach(() => {
- mock
- .onGet(projectRefsEndpoint, { params: { search: '' } })
- .reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
-
- findRefsDropdown().vm.$emit('loadingError');
- });
-
- it('shows both an error alert', () => {
- expect(findErrorAlert().exists()).toBe(true);
- expect(findWarningAlert().exists()).toBe(false);
- });
- });
-
- describe('when the error response can be handled', () => {
- beforeEach(async () => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.BAD_REQUEST, mockError);
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('shows both error and warning', () => {
- expect(findErrorAlert().exists()).toBe(true);
- expect(findWarningAlert().exists()).toBe(true);
- });
-
- it('shows the correct error', () => {
- expect(findErrorAlert().text()).toBe(mockError.errors[0]);
- });
-
- it('shows the correct warning title', () => {
- const { length } = mockError.warnings;
-
- expect(findWarningAlertSummary().attributes('message')).toBe(`${length} warnings found:`);
- });
-
- it('shows the correct amount of warnings', () => {
- expect(findWarnings()).toHaveLength(mockError.warnings.length);
- });
-
- it('re-enables the submit button', () => {
- expect(findSubmitButton().props('disabled')).toBe(false);
- });
-
- it('does not show the credit card validation required alert', () => {
- expect(findCCAlert().exists()).toBe(false);
- });
-
- describe('when the error response is credit card validation required', () => {
- beforeEach(async () => {
- mock
- .onPost(pipelinesPath)
- .reply(httpStatusCodes.BAD_REQUEST, mockCreditCardValidationRequiredError);
-
- window.gon = {
- subscriptions_url: TEST_HOST,
- payment_form_url: TEST_HOST,
- };
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('shows credit card validation required alert', () => {
- expect(findErrorAlert().exists()).toBe(false);
- expect(findCCAlert().exists()).toBe(true);
- });
-
- it('clears error and hides the alert on dismiss', async () => {
- expect(findCCAlert().exists()).toBe(true);
- expect(wrapper.vm.$data.error).toBe(mockCreditCardValidationRequiredError.errors[0]);
-
- findCCAlert().vm.$emit('dismiss');
-
- await nextTick();
-
- expect(findCCAlert().exists()).toBe(false);
- expect(wrapper.vm.$data.error).toBe(null);
- });
- });
- });
-
- describe('when the error response cannot be handled', () => {
- beforeEach(async () => {
- mock
- .onPost(pipelinesPath)
- .reply(httpStatusCodes.INTERNAL_SERVER_ERROR, 'something went wrong');
-
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
- });
-
- it('re-enables the submit button', () => {
- expect(findSubmitButton().props('disabled')).toBe(false);
- });
- });
- });
-});