diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-10 06:07:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-10 06:07:16 +0000 |
commit | 32c68185530d784ce557342e69d96af2f43b0dcd (patch) | |
tree | e5372123e2d9bd24a9c8106f1961ff0b57ef834c /spec | |
parent | 1e5e685f4fa6745023da8691edc272e39804a50a (diff) | |
download | gitlab-ce-32c68185530d784ce557342e69d96af2f43b0dcd.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
7 files changed, 39 insertions, 537 deletions
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); - }); - }); - }); -}); |