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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-21 15:08:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-21 15:08:33 +0300
commit6c44b676312eb6cdffadef45f9ca3e29a8cc92ab (patch)
tree06666cd369ac9ad0533cec689f2c2b4fb826f797 /spec
parentc1cea595b6a9b4d85424e9afd2cb765101ee04bf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/projects/ci/editor_spec.rb68
-rw-r--r--spec/frontend/diffs/utils/tree_worker_utils_spec.js2
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js30
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js48
-rw-r--r--spec/lib/gitlab/ci/config/yaml/interpolator_spec.rb239
-rw-r--r--spec/lib/gitlab/ci/interpolation/inputs/base_input_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/interpolation/inputs_spec.rb97
7 files changed, 335 insertions, 176 deletions
diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb
index 43da57c16d1..b09aa91f4ab 100644
--- a/spec/features/projects/ci/editor_spec.rb
+++ b/spec/features/projects/ci/editor_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
let(:default_branch) { 'main' }
let(:other_branch) { 'test' }
let(:branch_with_invalid_ci) { 'despair' }
+ let(:branch_without_ci) { 'empty' }
let(:default_content) { 'Default' }
@@ -45,6 +46,7 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
project.repository.create_file(user, project.ci_config_path_or_default, default_content, message: 'Create CI file for main', branch_name: default_branch)
project.repository.create_file(user, project.ci_config_path_or_default, valid_content, message: 'Create CI file for test', branch_name: other_branch)
project.repository.create_file(user, project.ci_config_path_or_default, invalid_content, message: 'Create CI file for test', branch_name: branch_with_invalid_ci)
+ project.repository.create_file(user, 'index.js', "file", message: 'New js file', branch_name: branch_without_ci)
visit project_ci_pipeline_editor_path(project)
wait_for_requests
@@ -62,6 +64,31 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
end
end
+ describe 'when there are no CI config file' do
+ before do
+ visit project_ci_pipeline_editor_path(project, branch_name: branch_without_ci)
+ end
+
+ it 'renders the empty page', :aggregate_failures do
+ expect(page).to have_content 'Optimize your workflow with CI/CD Pipelines'
+ expect(page).to have_selector '[data-testid="create_new_ci_button"]'
+ end
+
+ context 'when clicking on the create new CI button' do
+ before do
+ click_button 'Configure pipeline'
+ end
+
+ it 'renders the source editor with default content', :aggregate_failures do
+ expect(page).to have_selector('#source-editor-')
+
+ page.within('#source-editor-') do
+ expect(page).to have_content('This file is a template, and might need editing before it works on your project.')
+ end
+ end
+ end
+ end
+
describe 'When CI yml has valid syntax' do
before do
visit project_ci_pipeline_editor_path(project, branch_name: other_branch)
@@ -149,15 +176,6 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
end
shared_examples 'default branch switcher behavior' do
- def switch_to_branch(branch)
- find('[data-testid="branch-selector"]').click
-
- page.within '[data-testid="branch-selector"]' do
- click_button branch
- wait_for_requests
- end
- end
-
it 'displays current branch' do
page.within('[data-testid="branch-selector"]') do
expect(page).to have_content(default_branch)
@@ -195,12 +213,20 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
end
describe 'Branch Switcher' do
+ def switch_to_branch(branch)
+ # close button for the popover
+ find('[data-testid="close-button"]').click
+ find('[data-testid="branch-selector"]').click
+
+ page.within '[data-testid="branch-selector"]' do
+ click_button branch
+ wait_for_requests
+ end
+ end
+
before do
visit project_ci_pipeline_editor_path(project)
wait_for_requests
-
- # close button for the popover
- find('[data-testid="close-button"]').click
end
it_behaves_like 'default branch switcher behavior'
@@ -262,6 +288,24 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d
end
describe 'Commit Form' do
+ context 'when targetting the main branch' do
+ it 'does not show the option to create a Merge request', :aggregate_failures do
+ expect(page).not_to have_selector('[data-testid="new-mr-checkbox"]')
+ expect(page).not_to have_content('Start a new merge request with these changes')
+ end
+ end
+
+ context 'when targetting any non-main branch' do
+ before do
+ find('#source-branch-field').set('new_branch', clear: :backspace)
+ end
+
+ it 'shows the option to create a Merge request', :aggregate_failures do
+ expect(page).to have_selector('[data-testid="new-mr-checkbox"]')
+ expect(page).to have_content('Start a new merge request with these changes')
+ end
+ end
+
it 'is preserved when changing tabs' do
find('#commit-message').set('message', clear: :backspace)
find('#source-branch-field').set('new_branch', clear: :backspace)
diff --git a/spec/frontend/diffs/utils/tree_worker_utils_spec.js b/spec/frontend/diffs/utils/tree_worker_utils_spec.js
index 5fba00f9258..b29275f45a6 100644
--- a/spec/frontend/diffs/utils/tree_worker_utils_spec.js
+++ b/spec/frontend/diffs/utils/tree_worker_utils_spec.js
@@ -382,7 +382,7 @@ describe('~/diffs/utils/tree_worker_utils', () => {
},
{
type: 'tree',
- name: 'ee/lib/…/…/…/longtreenametomakepath',
+ name: 'ee/lib/ee/gitlab/checks/longtreenametomakepath',
tree: [
{
name: 'diff_check.rb',
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 8f1f6899935..b7d6bbd3991 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -238,36 +238,6 @@ describe('text_utility', () => {
});
});
- describe('truncatePathMiddleToLength', () => {
- it('does not truncate text', () => {
- expect(textUtils.truncatePathMiddleToLength('app/test', 50)).toEqual('app/test');
- });
-
- it('truncates middle of the path', () => {
- expect(textUtils.truncatePathMiddleToLength('app/test/diff', 13)).toEqual('app/…/diff');
- });
-
- it('truncates multiple times in the middle of the path', () => {
- expect(textUtils.truncatePathMiddleToLength('app/test/merge_request/diff', 13)).toEqual(
- 'app/…/…/diff',
- );
- });
-
- describe('given a path too long for the maxWidth', () => {
- it.each`
- path | maxWidth | result
- ${'aa/bb/cc'} | ${1} | ${'…'}
- ${'aa/bb/cc'} | ${2} | ${'…'}
- ${'aa/bb/cc'} | ${3} | ${'…/…'}
- ${'aa/bb/cc'} | ${4} | ${'…/…'}
- ${'aa/bb/cc'} | ${5} | ${'…/…/…'}
- `('truncates ($path, $maxWidth) to $result', ({ path, maxWidth, result }) => {
- expect(result.length).toBeLessThanOrEqual(maxWidth);
- expect(textUtils.truncatePathMiddleToLength(path, maxWidth)).toEqual(result);
- });
- });
- });
-
describe('slugifyWithUnderscore', () => {
it('should replaces whitespaces with underscore and convert to lower case', () => {
expect(textUtils.slugifyWithUnderscore('My Input String')).toEqual('my_input_string');
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
index 9343a3a5e90..18fdba32f52 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
@@ -121,14 +121,15 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
});
describe('fetch', () => {
- it('sets the data.collapsed property after a successfull call - multiPolling: false', async () => {
+ it('calls fetchCollapsedData properly when multiPolling is false', async () => {
const mockData = { headers: {}, status: HTTP_STATUS_OK, data: { vulnerabilities: [] } };
- createComponent({ propsData: { fetchCollapsedData: () => Promise.resolve(mockData) } });
+ const fetchCollapsedData = jest.fn().mockResolvedValue(mockData);
+ createComponent({ propsData: { fetchCollapsedData } });
await waitForPromises();
- expect(wrapper.emitted('input')[0][0]).toEqual({ collapsed: mockData.data, expanded: null });
+ expect(fetchCollapsedData).toHaveBeenCalledTimes(1);
});
- it('sets the data.collapsed property after a successfull call - multiPolling: true', async () => {
+ it('calls fetchCollapsedData properly when multiPolling is true', async () => {
const mockData1 = {
headers: {},
status: HTTP_STATUS_OK,
@@ -140,22 +141,22 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
data: { vulnerabilities: [{ vuln: 2 }] },
};
+ const fetchCollapsedData = [
+ jest.fn().mockResolvedValue(mockData1),
+ jest.fn().mockResolvedValue(mockData2),
+ ];
+
createComponent({
propsData: {
multiPolling: true,
- fetchCollapsedData: () => [
- () => Promise.resolve(mockData1),
- () => Promise.resolve(mockData2),
- ],
+ fetchCollapsedData: () => fetchCollapsedData,
},
});
await waitForPromises();
- expect(wrapper.emitted('input')[0][0]).toEqual({
- collapsed: [mockData1.data, mockData2.data],
- expanded: null,
- });
+ expect(fetchCollapsedData[0]).toHaveBeenCalledTimes(1);
+ expect(fetchCollapsedData[1]).toHaveBeenCalledTimes(1);
});
it('throws an error when the handler does not include headers or status objects', async () => {
@@ -328,11 +329,12 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
};
const fetchExpandedData = jest.fn().mockResolvedValue(mockDataExpanded);
+ const fetchCollapsedData = jest.fn().mockResolvedValue(mockDataCollapsed);
await createComponent({
propsData: {
isCollapsible: true,
- fetchCollapsedData: () => Promise.resolve(mockDataCollapsed),
+ fetchCollapsedData,
fetchExpandedData,
},
});
@@ -340,17 +342,8 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
findToggleButton().vm.$emit('click');
await waitForPromises();
- // First fetches the collapsed data
- expect(wrapper.emitted('input')[0][0]).toEqual({
- collapsed: mockDataCollapsed.data,
- expanded: null,
- });
-
- // Then fetches the expanded data
- expect(wrapper.emitted('input')[1][0]).toEqual({
- collapsed: null,
- expanded: mockDataExpanded.data,
- });
+ expect(fetchCollapsedData).toHaveBeenCalledTimes(1);
+ expect(fetchExpandedData).toHaveBeenCalledTimes(1);
// Triggering a click does not call the expanded data again
findToggleButton().vm.$emit('click');
@@ -371,14 +364,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
findToggleButton().vm.$emit('click');
await waitForPromises();
- // First fetches the collapsed data
- expect(wrapper.emitted('input')[0][0]).toEqual({
- collapsed: undefined,
- expanded: null,
- });
-
expect(fetchExpandedData).toHaveBeenCalledTimes(1);
- expect(wrapper.emitted('input')).toHaveLength(1); // Should not an emit an input call because request failed
findToggleButton().vm.$emit('click');
await waitForPromises();
diff --git a/spec/lib/gitlab/ci/config/yaml/interpolator_spec.rb b/spec/lib/gitlab/ci/config/yaml/interpolator_spec.rb
index b744adc570f..1621ff6655d 100644
--- a/spec/lib/gitlab/ci/config/yaml/interpolator_spec.rb
+++ b/spec/lib/gitlab/ci/config/yaml/interpolator_spec.rb
@@ -9,151 +9,185 @@ RSpec.describe Gitlab::Ci::Config::Yaml::Interpolator, feature_category: :pipeli
subject { described_class.new(result, arguments) }
- context 'when input data is valid' do
- let(:header) do
- { spec: { inputs: { website: nil } } }
- end
+ # Remove shared examples when ci_interpolation_inputs_refactor is removed.
+ shared_examples 'interpolator' do
+ context 'when input data is valid' do
+ let(:header) do
+ { spec: { inputs: { website: nil } } }
+ end
- let(:content) do
- { test: 'deploy $[[ inputs.website ]]' }
- end
+ let(:content) do
+ { test: 'deploy $[[ inputs.website ]]' }
+ end
- let(:arguments) do
- { website: 'gitlab.com' }
- end
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
- it 'correctly interpolates the config' do
- subject.interpolate!
+ it 'correctly interpolates the config' do
+ subject.interpolate!
- expect(subject).to be_valid
- expect(subject.to_hash).to eq({ test: 'deploy gitlab.com' })
+ expect(subject).to be_interpolated
+ expect(subject).to be_valid
+ expect(subject.to_hash).to eq({ test: 'deploy gitlab.com' })
+ end
end
- end
- context 'when config has a syntax error' do
- let(:result) { ::Gitlab::Ci::Config::Yaml::Result.new(error: 'Invalid configuration format') }
+ context 'when config has a syntax error' do
+ let(:result) { ::Gitlab::Ci::Config::Yaml::Result.new(error: 'Invalid configuration format') }
- let(:arguments) do
- { website: 'gitlab.com' }
- end
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
- it 'surfaces an error about invalid config' do
- subject.interpolate!
+ it 'surfaces an error about invalid config' do
+ subject.interpolate!
- expect(subject).not_to be_valid
- expect(subject.error_message).to eq subject.errors.first
- expect(subject.errors).to include 'Invalid configuration format'
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to eq subject.errors.first
+ expect(subject.errors).to include 'Invalid configuration format'
+ end
end
- end
- context 'when spec header is invalid' do
- let(:header) do
- { spec: { arguments: { website: nil } } }
- end
+ context 'when spec header is invalid' do
+ let(:header) do
+ { spec: { arguments: { website: nil } } }
+ end
- let(:content) do
- { test: 'deploy $[[ inputs.website ]]' }
- end
+ let(:content) do
+ { test: 'deploy $[[ inputs.website ]]' }
+ end
- let(:arguments) do
- { website: 'gitlab.com' }
- end
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
- it 'surfaces an error about invalid header' do
- subject.interpolate!
+ it 'surfaces an error about invalid header' do
+ subject.interpolate!
- expect(subject).not_to be_valid
- expect(subject.error_message).to eq subject.errors.first
- expect(subject.errors).to include('header:spec config contains unknown keys: arguments')
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to eq subject.errors.first
+ expect(subject.errors).to include('header:spec config contains unknown keys: arguments')
+ end
end
- end
- context 'when interpolation block is invalid' do
- let(:header) do
- { spec: { inputs: { website: nil } } }
- end
+ context 'when interpolation block is invalid' do
+ let(:header) do
+ { spec: { inputs: { website: nil } } }
+ end
- let(:content) do
- { test: 'deploy $[[ inputs.abc ]]' }
- end
+ let(:content) do
+ { test: 'deploy $[[ inputs.abc ]]' }
+ end
- let(:arguments) do
- { website: 'gitlab.com' }
- end
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
- it 'correctly interpolates the config' do
- subject.interpolate!
+ it 'correctly interpolates the config' do
+ subject.interpolate!
- expect(subject).not_to be_valid
- expect(subject.errors).to include 'unknown interpolation key: `abc`'
- expect(subject.error_message).to eq 'interpolation interrupted by errors, unknown interpolation key: `abc`'
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include 'unknown interpolation key: `abc`'
+ expect(subject.error_message).to eq 'interpolation interrupted by errors, unknown interpolation key: `abc`'
+ end
end
- end
- context 'when provided interpolation argument is invalid' do
- let(:header) do
- { spec: { inputs: { website: nil } } }
- end
+ context 'when multiple interpolation blocks are invalid' do
+ let(:header) do
+ { spec: { inputs: { website: nil } } }
+ end
- let(:content) do
- { test: 'deploy $[[ inputs.website ]]' }
- end
+ let(:content) do
+ { test: 'deploy $[[ inputs.something.abc ]] $[[ inputs.cde ]] $[[ efg ]]' }
+ end
- let(:arguments) do
- { website: ['gitlab.com'] }
- end
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
- it 'correctly interpolates the config' do
- subject.interpolate!
+ it 'correctly interpolates the config' do
+ subject.interpolate!
- expect(subject).not_to be_valid
- expect(subject.error_message).to eq subject.errors.first
- expect(subject.errors).to include 'unsupported value in input argument `website`'
+ expect(subject).not_to be_valid
+ expect(subject.error_message)
+ .to eq 'interpolation interrupted by errors, unknown interpolation key: `something`'
+ end
end
- end
- context 'when multiple interpolation blocks are invalid' do
- let(:header) do
- { spec: { inputs: { website: nil } } }
- end
+ describe '#to_hash' do
+ context 'when interpolation is not used' do
+ let(:result) do
+ ::Gitlab::Ci::Config::Yaml::Result.new(config: content)
+ end
- let(:content) do
- { test: 'deploy $[[ inputs.something.abc ]] $[[ inputs.cde ]] $[[ efg ]]' }
- end
+ let(:content) do
+ { test: 'deploy production' }
+ end
- let(:arguments) do
- { website: 'gitlab.com' }
- end
+ let(:arguments) { nil }
+
+ it 'returns original content' do
+ subject.interpolate!
+
+ expect(subject.to_hash).to eq(content)
+ end
+ end
- it 'correctly interpolates the config' do
- subject.interpolate!
+ context 'when interpolation is available' do
+ let(:header) do
+ { spec: { inputs: { website: nil } } }
+ end
- expect(subject).not_to be_valid
- expect(subject.error_message).to eq 'interpolation interrupted by errors, unknown interpolation key: `something`'
+ let(:content) do
+ { test: 'deploy $[[ inputs.website ]]' }
+ end
+
+ let(:arguments) do
+ { website: 'gitlab.com' }
+ end
+
+ it 'correctly interpolates content' do
+ subject.interpolate!
+
+ expect(subject.to_hash).to eq({ test: 'deploy gitlab.com' })
+ end
+ end
end
end
- describe '#to_hash' do
- context 'when interpolation is not used' do
- let(:result) do
- ::Gitlab::Ci::Config::Yaml::Result.new(config: content)
+ it_behaves_like 'interpolator' do
+ context 'when provided interpolation argument is invalid' do
+ let(:header) do
+ { spec: { inputs: { website: nil } } }
end
let(:content) do
- { test: 'deploy production' }
+ { test: 'deploy $[[ inputs.website ]]' }
end
- let(:arguments) { nil }
+ let(:arguments) do
+ { website: ['gitlab.com'] }
+ end
- it 'returns original content' do
+ it 'returns an error' do
subject.interpolate!
- expect(subject).not_to be_interpolated
- expect(subject.to_hash).to eq(content)
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to eq subject.errors.first
+ expect(subject.errors).to include '`website` input: provided value is not a string'
end
end
+ end
- context 'when interpolation is available' do
+ context 'when feature flag ci_interpolation_inputs_refactor is disabled' do
+ before do
+ stub_feature_flags(ci_interpolation_inputs_refactor: false)
+ end
+
+ it_behaves_like 'interpolator'
+
+ context 'when provided interpolation argument is invalid' do
let(:header) do
{ spec: { inputs: { website: nil } } }
end
@@ -163,14 +197,15 @@ RSpec.describe Gitlab::Ci::Config::Yaml::Interpolator, feature_category: :pipeli
end
let(:arguments) do
- { website: 'gitlab.com' }
+ { website: ['gitlab.com'] }
end
- it 'correctly interpolates content' do
+ it 'returns an error' do
subject.interpolate!
- expect(subject).to be_interpolated
- expect(subject.to_hash).to eq({ test: 'deploy gitlab.com' })
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to eq subject.errors.first
+ expect(subject.errors).to include 'unsupported value in input argument `website`'
end
end
end
diff --git a/spec/lib/gitlab/ci/interpolation/inputs/base_input_spec.rb b/spec/lib/gitlab/ci/interpolation/inputs/base_input_spec.rb
new file mode 100644
index 00000000000..30dbf1ffe51
--- /dev/null
+++ b/spec/lib/gitlab/ci/interpolation/inputs/base_input_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Interpolation::Inputs::BaseInput, feature_category: :pipeline_composition do
+ describe '.matches?' do
+ it 'is not implemented' do
+ expect { described_class.matches?(double) }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '.type_name' do
+ it 'is not implemented' do
+ expect { described_class.type_name }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#valid_value?' do
+ it 'is not implemented' do
+ expect do
+ described_class.new(
+ name: 'website', spec: { website: nil }, value: { website: 'example.com' }
+ ).valid_value?('test')
+ end.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/interpolation/inputs_spec.rb b/spec/lib/gitlab/ci/interpolation/inputs_spec.rb
new file mode 100644
index 00000000000..d7d7c85d04f
--- /dev/null
+++ b/spec/lib/gitlab/ci/interpolation/inputs_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Interpolation::Inputs, feature_category: :pipeline_composition do
+ let(:inputs) { described_class.new(specs, args) }
+ let(:specs) { { foo: { default: 'bar' } } }
+ let(:args) { {} }
+
+ context 'when inputs are valid' do
+ where(:specs, :args, :merged) do
+ [
+ [
+ { foo: { default: 'bar' } }, {},
+ { foo: 'bar' }
+ ],
+ [
+ { foo: { default: 'bar' } }, { foo: 'test' },
+ { foo: 'test' }
+ ],
+ [
+ { foo: nil }, { foo: 'bar' },
+ { foo: 'bar' }
+ ],
+ [
+ { foo: { type: 'string' } }, { foo: 'bar' },
+ { foo: 'bar' }
+ ],
+ [
+ { foo: { type: 'string', default: 'bar' } }, { foo: 'test' },
+ { foo: 'test' }
+ ],
+ [
+ { foo: { type: 'string', default: 'bar' } }, {},
+ { foo: 'bar' }
+ ],
+ [
+ { foo: { default: 'bar' }, baz: nil }, { baz: 'test' },
+ { foo: 'bar', baz: 'test' }
+ ]
+ ]
+ end
+
+ with_them do
+ it 'contains the merged inputs' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(merged)
+ end
+ end
+ end
+
+ context 'when inputs are invalid' do
+ where(:specs, :args, :errors) do
+ [
+ [
+ { foo: nil }, { foo: 'bar', test: 'bar' },
+ ['unknown input arguments: test']
+ ],
+ [
+ { foo: nil }, { test: 'bar', gitlab: '1' },
+ ['unknown input arguments: test, gitlab', '`foo` input: required value has not been provided']
+ ],
+ [
+ { foo: 123 }, {},
+ ['unknown input specification for `foo` (valid types: string)']
+ ],
+ [
+ { a: nil, foo: 123 }, { a: '123' },
+ ['unknown input specification for `foo` (valid types: string)']
+ ],
+ [
+ { foo: nil }, {},
+ ['`foo` input: required value has not been provided']
+ ],
+ [
+ { foo: { default: 123 } }, { foo: 'test' },
+ ['`foo` input: default value is not a string']
+ ],
+ [
+ { foo: { default: 'test' } }, { foo: 123 },
+ ['`foo` input: provided value is not a string']
+ ],
+ [
+ { foo: nil }, { foo: 123 },
+ ['`foo` input: provided value is not a string']
+ ]
+ ]
+ end
+
+ with_them do
+ it 'contains the merged inputs', :aggregate_failures do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(*errors)
+ end
+ end
+ end
+end