diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-02 00:10:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-02 00:10:21 +0300 |
commit | 5b8f2c8a24237cc5b2e2ba365b79e6293b93e459 (patch) | |
tree | 6ce5ce8932acbe93832fc0476f323e95bbb7dd8f /spec | |
parent | 304e230182b74eb84833c60b50618a5f0d7e2f9a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/packages.rb | 6 | ||||
-rw-r--r-- | spec/frontend/profile/account/components/delete_account_modal_spec.js | 82 | ||||
-rw-r--r-- | spec/graphql/types/package_type_enum_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/reports/test_suite_spec.rb | 31 | ||||
-rw-r--r-- | spec/lib/gitlab/regex_spec.rb | 17 | ||||
-rw-r--r-- | spec/models/ci/pipeline_artifact_spec.rb | 10 | ||||
-rw-r--r-- | spec/models/packages/package_spec.rb | 29 | ||||
-rw-r--r-- | spec/presenters/ci/pipeline_artifacts/code_coverage_presenter_spec.rb (renamed from spec/lib/gitlab/ci/pipeline/artifact/code_coverage_spec.rb) | 7 | ||||
-rw-r--r-- | spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb | 134 | ||||
-rw-r--r-- | spec/services/ci/generate_coverage_reports_service_spec.rb | 3 | ||||
-rw-r--r-- | spec/support/shared_examples/services/packages_shared_examples.rb | 1 | ||||
-rw-r--r-- | spec/support/test_reports/test_reports_helper.rb | 4 |
12 files changed, 266 insertions, 60 deletions
diff --git a/spec/factories/packages.rb b/spec/factories/packages.rb index 0f9138ddefe..ee9079579e0 100644 --- a/spec/factories/packages.rb +++ b/spec/factories/packages.rb @@ -121,6 +121,12 @@ FactoryBot.define do conan_metadatum { build(:conan_metadatum, package: nil) } end end + + factory :generic_package do + sequence(:name) { |n| "generic-package-#{n}" } + version { '1.0.0' } + package_type { :generic } + end end factory :composer_metadatum, class: 'Packages::Composer::Metadatum' do diff --git a/spec/frontend/profile/account/components/delete_account_modal_spec.js b/spec/frontend/profile/account/components/delete_account_modal_spec.js index 4da82152818..7834456f7c4 100644 --- a/spec/frontend/profile/account/components/delete_account_modal_spec.js +++ b/spec/frontend/profile/account/components/delete_account_modal_spec.js @@ -1,21 +1,49 @@ import Vue from 'vue'; import { TEST_HOST } from 'helpers/test_constants'; -import mountComponent from 'helpers/vue_mount_component_helper'; +import { merge } from 'lodash'; +import { mount } from '@vue/test-utils'; import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue'; +const GlModalStub = { + name: 'gl-modal-stub', + template: ` + <div> + <slot></slot> + </div> + `, +}; + describe('DeleteAccountModal component', () => { const actionUrl = `${TEST_HOST}/delete/user`; const username = 'hasnoname'; - let Component; + let wrapper; let vm; - beforeEach(() => { - Component = Vue.extend(deleteAccountModal); - }); + const createWrapper = (options = {}) => { + wrapper = mount( + deleteAccountModal, + merge( + {}, + { + propsData: { + actionUrl, + username, + }, + stubs: { + GlModal: GlModalStub, + }, + }, + options, + ), + ); + vm = wrapper.vm; + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); + wrapper = null; + vm = null; }); const findElements = () => { @@ -23,16 +51,16 @@ describe('DeleteAccountModal component', () => { return { form: vm.$refs.form, input: vm.$el.querySelector(`[name="${confirmation}"]`), - submitButton: vm.$el.querySelector('.btn-danger'), }; }; + const findModal = () => wrapper.find(GlModalStub); describe('with password confirmation', () => { beforeEach(done => { - vm = mountComponent(Component, { - actionUrl, - confirmWithPassword: true, - username, + createWrapper({ + propsData: { + confirmWithPassword: true, + }, }); vm.isOpen = true; @@ -43,7 +71,7 @@ describe('DeleteAccountModal component', () => { }); it('does not accept empty password', done => { - const { form, input, submitButton } = findElements(); + const { form, input } = findElements(); jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = ''; input.dispatchEvent(new Event('input')); @@ -51,8 +79,8 @@ describe('DeleteAccountModal component', () => { Vue.nextTick() .then(() => { expect(vm.enteredPassword).toBe(input.value); - expect(submitButton).toHaveAttr('disabled', 'disabled'); - submitButton.click(); + expect(findModal().attributes('ok-disabled')).toBe('true'); + findModal().vm.$emit('primary'); expect(form.submit).not.toHaveBeenCalled(); }) @@ -61,7 +89,7 @@ describe('DeleteAccountModal component', () => { }); it('submits form with password', done => { - const { form, input, submitButton } = findElements(); + const { form, input } = findElements(); jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = 'anything'; input.dispatchEvent(new Event('input')); @@ -69,8 +97,8 @@ describe('DeleteAccountModal component', () => { Vue.nextTick() .then(() => { expect(vm.enteredPassword).toBe(input.value); - expect(submitButton).not.toHaveAttr('disabled', 'disabled'); - submitButton.click(); + expect(findModal().attributes('ok-disabled')).toBeUndefined(); + findModal().vm.$emit('primary'); expect(form.submit).toHaveBeenCalled(); }) @@ -81,10 +109,10 @@ describe('DeleteAccountModal component', () => { describe('with username confirmation', () => { beforeEach(done => { - vm = mountComponent(Component, { - actionUrl, - confirmWithPassword: false, - username, + createWrapper({ + propsData: { + confirmWithPassword: false, + }, }); vm.isOpen = true; @@ -95,7 +123,7 @@ describe('DeleteAccountModal component', () => { }); it('does not accept wrong username', done => { - const { form, input, submitButton } = findElements(); + const { form, input } = findElements(); jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = 'this is wrong'; input.dispatchEvent(new Event('input')); @@ -103,8 +131,8 @@ describe('DeleteAccountModal component', () => { Vue.nextTick() .then(() => { expect(vm.enteredUsername).toBe(input.value); - expect(submitButton).toHaveAttr('disabled', 'disabled'); - submitButton.click(); + expect(findModal().attributes('ok-disabled')).toBe('true'); + findModal().vm.$emit('primary'); expect(form.submit).not.toHaveBeenCalled(); }) @@ -113,7 +141,7 @@ describe('DeleteAccountModal component', () => { }); it('submits form with correct username', done => { - const { form, input, submitButton } = findElements(); + const { form, input } = findElements(); jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = username; input.dispatchEvent(new Event('input')); @@ -121,8 +149,8 @@ describe('DeleteAccountModal component', () => { Vue.nextTick() .then(() => { expect(vm.enteredUsername).toBe(input.value); - expect(submitButton).not.toHaveAttr('disabled', 'disabled'); - submitButton.click(); + expect(findModal().attributes('ok-disabled')).toBeUndefined(); + findModal().vm.$emit('primary'); expect(form.submit).toHaveBeenCalled(); }) diff --git a/spec/graphql/types/package_type_enum_spec.rb b/spec/graphql/types/package_type_enum_spec.rb index fadec9744ed..80a20a68bc2 100644 --- a/spec/graphql/types/package_type_enum_spec.rb +++ b/spec/graphql/types/package_type_enum_spec.rb @@ -4,6 +4,6 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['PackageTypeEnum'] do it 'exposes all package types' do - expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER]) + expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC]) end end diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb index fbe3473f6b0..15fa78444e5 100644 --- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb @@ -176,6 +176,37 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do end end + describe '#sorted' do + subject { test_suite.sorted } + + context 'when there are multiple failed test cases' do + before do + test_suite.add_test_case(create_test_case_rspec_failed('test_spec_1', 1.11)) + test_suite.add_test_case(create_test_case_rspec_failed('test_spec_2', 4.44)) + end + + it 'returns test cases sorted by execution time desc' do + expect(subject.test_cases['failed'].each_value.first.execution_time).to eq(4.44) + expect(subject.test_cases['failed'].values.second.execution_time).to eq(1.11) + end + end + + context 'when there are multiple test cases' do + let(:status_ordered) { %w(error failed success skipped) } + + before do + test_suite.add_test_case(test_case_success) + test_suite.add_test_case(test_case_failed) + test_suite.add_test_case(test_case_error) + test_suite.add_test_case(test_case_skipped) + end + + it 'returns test cases sorted by status' do + expect(subject.test_cases.keys).to eq(status_ordered) + end + end + end + Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type| describe "##{status_type}" do subject { test_suite.public_send("#{status_type}") } diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 838667675c7..f736f19ca9a 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -426,4 +426,21 @@ RSpec.describe Gitlab::Regex do it { is_expected.not_to match('1.2') } it { is_expected.not_to match('1./2.3') } end + + describe '.generic_package_version_regex' do + subject { described_class.generic_package_version_regex } + + it { is_expected.to match('1.2.3') } + it { is_expected.to match('1.3.350') } + it { is_expected.not_to match('1.3.350-20201230123456') } + it { is_expected.not_to match('..1.2.3') } + it { is_expected.not_to match(' 1.2.3') } + it { is_expected.not_to match("1.2.3 \r\t") } + it { is_expected.not_to match("\r\t 1.2.3") } + it { is_expected.not_to match('1.2.3-4/../../') } + it { is_expected.not_to match('1.2.3-4%2e%2e%') } + it { is_expected.not_to match('../../../../../1.2.3') } + it { is_expected.not_to match('%2e%2e%2f1.2.3') } + it { is_expected.not_to match('') } + end end diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb index 14f3bcc463e..716ab4d8522 100644 --- a/spec/models/ci/pipeline_artifact_spec.rb +++ b/spec/models/ci/pipeline_artifact_spec.rb @@ -109,4 +109,14 @@ RSpec.describe Ci::PipelineArtifact, type: :model do end end end + + describe '#present' do + subject { coverage_report.present } + + context 'when file_type is code_coverage' do + it 'uses code coverage presenter' do + expect(subject.present).to be_kind_of(Ci::PipelineArtifacts::CodeCoveragePresenter) + end + end + end end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index da15b0f0453..4867ac2a75b 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -236,6 +236,25 @@ RSpec.describe Packages::Package, type: :model do it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) } end + context 'generic package' do + subject { build_stubbed(:generic_package) } + + it { is_expected.to validate_presence_of(:version) } + it { is_expected.to allow_value('1.2.3').for(:version) } + it { is_expected.to allow_value('1.3.350').for(:version) } + it { is_expected.not_to allow_value('1.3.350-20201230123456').for(:version) } + it { is_expected.not_to allow_value('..1.2.3').for(:version) } + it { is_expected.not_to allow_value(' 1.2.3').for(:version) } + it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) } + it { is_expected.not_to allow_value("\r\t 1.2.3").for(:version) } + it { is_expected.not_to allow_value('1.2.3-4/../../').for(:version) } + it { is_expected.not_to allow_value('1.2.3-4%2e%2e%').for(:version) } + it { is_expected.not_to allow_value('../../../../../1.2.3').for(:version) } + it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) } + it { is_expected.not_to allow_value('').for(:version) } + it { is_expected.not_to allow_value(nil).for(:version) } + end + it_behaves_like 'validating version to be SemVer compliant for', :npm_package it_behaves_like 'validating version to be SemVer compliant for', :nuget_package end @@ -552,11 +571,17 @@ RSpec.describe Packages::Package, type: :model do describe 'plan_limits' do Packages::Package.package_types.keys.without('composer').each do |pt| + plan_limit_name = if pt == 'generic' + "#{pt}_packages_max_file_size" + else + "#{pt}_max_file_size" + end + context "File size limits for #{pt}" do let(:package) { create("#{pt}_package") } - it "plan_limits includes column #{pt}_max_file_size" do - expect { package.project.actual_limits.send("#{pt}_max_file_size") } + it "plan_limits includes column #{plan_limit_name}" do + expect { package.project.actual_limits.send(plan_limit_name) } .not_to raise_error(NoMethodError) end end diff --git a/spec/lib/gitlab/ci/pipeline/artifact/code_coverage_spec.rb b/spec/presenters/ci/pipeline_artifacts/code_coverage_presenter_spec.rb index f3bc9fd0ec2..e679f5fa144 100644 --- a/spec/lib/gitlab/ci/pipeline/artifact/code_coverage_spec.rb +++ b/spec/presenters/ci/pipeline_artifacts/code_coverage_presenter_spec.rb @@ -2,12 +2,13 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Pipeline::Artifact::CodeCoverage do +RSpec.describe Ci::PipelineArtifacts::CodeCoveragePresenter do let(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_code_coverage_with_multiple_files) } - let(:code_coverage) { described_class.new(pipeline_artifact) } + + subject(:presenter) { described_class.new(pipeline_artifact) } describe '#for_files' do - subject { code_coverage.for_files(filenames) } + subject { presenter.for_files(filenames) } context 'when code coverage has data' do context 'when filenames is empty' do diff --git a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb index 5804b4b00fc..b769109057e 100644 --- a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb +++ b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb @@ -14,40 +14,120 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco allow(cop).to receive(:in_migration?).and_return(true) end - context 'when indexes are configured with an options hash, but no name' do - it 'registers an offense' do - expect_offense(<<~RUBY) - class TestComplexIndexes < ActiveRecord::Migration[6.0] - DOWNTIME = false + context 'when creating complex indexes as part of create_table' do + context 'when indexes are configured with an options hash, but no name' do + it 'registers an offense' do + expect_offense(<<~RUBY) + class TestComplexIndexes < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def up + create_table :test_table do |t| + t.integer :column1, null: false + t.integer :column2, null: false + t.jsonb :column3 + + t.index :column1, unique: true + t.index :column2, where: 'column1 = 0' + ^^^^^ #{described_class::MSG} + t.index :column3, using: :gin + ^^^^^ #{described_class::MSG} + end + end + + def down + drop_table :test_table + end + end + RUBY + + expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}")) + end + end + + context 'when indexes are configured with an options hash and name' do + it 'registers no offense' do + expect_no_offenses(<<~RUBY) + class TestComplexIndexes < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def up + create_table :test_table do |t| + t.integer :column1, null: false + t.integer :column2, null: false + t.jsonb :column3 + + t.index :column1, unique: true + t.index :column2, where: 'column1 = 0', name: 'my_index_1' + t.index :column3, using: :gin, name: 'my_gin_index' + end + end + + def down + drop_table :test_table + end + end + RUBY + end + end + end - INDEX_NAME = 'my_test_name' + context 'when indexes are added to an existing table' do + context 'when indexes are configured with an options hash, but no name' do + it 'registers an offense' do + expect_offense(<<~RUBY) + class TestComplexIndexes < ActiveRecord::Migration[6.0] + DOWNTIME = false - disable_ddl_transaction! + disable_ddl_transaction! - def up - add_concurrent_index :test_indexes, :column1 + def up + add_index :test_indexes, :column1 - add_concurrent_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc } - ^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} + add_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc } + ^^^^^^^^^ #{described_class::MSG} + end - add_concurrent_index :test_indexes, :column3, where: 'column3 = 10', name: 'idx_equal_to_10' + def down + add_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL' + ^^^^^^^^^ #{described_class::MSG} + + add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops + ^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} + end end + RUBY - def down - add_concurrent_index :test_indexes, :column4, 'unique' => true + expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}")) + end + end - add_concurrent_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL' - ^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} + context 'when indexes are configured with an options hash and a name' do + it 'registers no offenses' do + expect_no_offenses(<<~RUBY) + class TestComplexIndexes < ActiveRecord::Migration[6.0] + DOWNTIME = false - add_concurrent_index :test_indexes, :column5, using: :gin, name: INDEX_NAME + INDEX_NAME = 'my_test_name' - add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops - ^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} - end - end - RUBY + disable_ddl_transaction! + + def up + add_index :test_indexes, :column1 + + add_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc }, name: 'my_index_1' + + add_concurrent_index :test_indexes, :column3, where: 'column3 = 10', name: 'idx_equal_to_10' + end + + def down + add_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL', name: 'my_index_2' - expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}")) + add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops, name: INDEX_NAME + end + end + RUBY + end end end end @@ -65,7 +145,13 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco disable_ddl_transaction! def up - add_concurrent_index :test_indexes, :column1, where: "some_column = 'value'" + create_table :test_table do |t| + t.integer :column1 + + t.index :column1, where: 'column2 IS NOT NULL' + end + + add_index :test_indexes, :column1, where: "some_column = 'value'" end def down diff --git a/spec/services/ci/generate_coverage_reports_service_spec.rb b/spec/services/ci/generate_coverage_reports_service_spec.rb index 722b92ea3b6..d39053adebc 100644 --- a/spec/services/ci/generate_coverage_reports_service_spec.rb +++ b/spec/services/ci/generate_coverage_reports_service_spec.rb @@ -16,7 +16,8 @@ RSpec.describe Ci::GenerateCoverageReportsService do let!(:base_pipeline) { nil } it 'returns status and data', :aggregate_failures do - expect_next_instance_of(Gitlab::Ci::Pipeline::Artifact::CodeCoverage) do |instance| + expect_any_instance_of(Ci::PipelineArtifact) do |instance| + expect(instance).to receive(:present) expect(instance).to receive(:for_files).with(merge_request.new_paths).and_call_original end diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb index 45a4c2bb151..836997e78f5 100644 --- a/spec/support/shared_examples/services/packages_shared_examples.rb +++ b/spec/support/shared_examples/services/packages_shared_examples.rb @@ -161,6 +161,7 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false| let_it_be(:package4) { create(:nuget_package, project: project) } let_it_be(:package5) { create(:pypi_package, project: project) } let_it_be(:package6) { create(:composer_package, project: project) } + let_it_be(:package7) { create(:generic_package, project: project) } Packages::Package.package_types.keys.each do |package_type| context "for package type #{package_type}" do diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb index 6ba50c83b25..ad9ecb6f460 100644 --- a/spec/support/test_reports/test_reports_helper.rb +++ b/spec/support/test_reports/test_reports_helper.rb @@ -10,12 +10,12 @@ module TestReportsHelper status: Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS) end - def create_test_case_rspec_failed(name = 'test_spec') + def create_test_case_rspec_failed(name = 'test_spec', execution_time = 2.22) Gitlab::Ci::Reports::TestCase.new( name: 'Test#sum when a is 1 and b is 3 returns summary', classname: "spec.#{name}", file: './spec/test_spec.rb', - execution_time: 2.22, + execution_time: execution_time, system_output: sample_rspec_failed_message, status: Gitlab::Ci::Reports::TestCase::STATUS_FAILED) end |