diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-11 12:10:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-11 12:10:00 +0300 |
commit | 564919dfc6c6b352163d4c6dc01827a5f12ffc88 (patch) | |
tree | 9e91b8dde12a3054fffc513c6e0ab0400a039a88 /spec | |
parent | 2000704b7a6cc9eb37dd597ed03567265eda9308 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/deprecation_toolkit_env.rb | 8 | ||||
-rw-r--r-- | spec/features/projects/commit/mini_pipeline_graph_spec.rb | 3 | ||||
-rw-r--r-- | spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js | 50 | ||||
-rw-r--r-- | spec/frontend/commit/mock_data.js | 117 | ||||
-rw-r--r-- | spec/frontend/issues_list/components/issues_list_app_spec.js | 2 | ||||
-rw-r--r-- | spec/frontend/issues_list/mock_data.js | 6 | ||||
-rw-r--r-- | spec/frontend/repository/components/delete_blob_modal_spec.js | 45 | ||||
-rw-r--r-- | spec/graphql/gitlab_schema_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/fake_application_settings_spec.rb | 16 | ||||
-rw-r--r-- | spec/models/packages/package_spec.rb | 144 | ||||
-rw-r--r-- | spec/models/personal_access_token_spec.rb | 8 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 45 | ||||
-rw-r--r-- | spec/requests/api/npm_project_packages_spec.rb | 32 | ||||
-rw-r--r-- | spec/tooling/danger/product_intelligence_spec.rb | 22 | ||||
-rw-r--r-- | spec/views/shared/access_tokens/_table.html.haml_spec.rb | 166 |
15 files changed, 588 insertions, 78 deletions
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb index 7077118a5d5..b95a8c599bf 100644 --- a/spec/deprecation_toolkit_env.rb +++ b/spec/deprecation_toolkit_env.rb @@ -47,16 +47,18 @@ module DeprecationToolkitEnv end # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 + # Note: When a spec fails due to this warning, please update the spec to address the deprecation. def self.kwargs_warning %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} end - # Allow these Gem paths to trigger keyword warnings as we upgrade these gems - # one by one + # Note: No new exceptions should be added here, unless they are in external dependencies. + # In this case, we recommend to add a silence together with an issue to patch or update + # the dependency causing the problem. + # See https://gitlab.com/gitlab-org/gitlab/-/commit/aea37f506bbe036378998916d374966c031bf347#note_647515736 def self.allowed_kwarg_warning_paths %w[ actionpack-6.1.3.2/lib/action_dispatch/routing/route_set.rb - graphql-1.11.8/lib/graphql/schema.rb ] end diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 6de02556175..57b35d81bb8 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -19,6 +19,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do before do build.run visit project_commit_path(project, project.commit.id) + wait_for_requests end it 'display icon with status' do @@ -26,7 +27,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do end it 'displays a mini pipeline graph' do - expect(page).to have_selector('[data-testid="pipeline-mini-graph"]') + expect(page).to have_selector('[data-testid="commit-box-mini-graph"]') first('.mini-pipeline-graph-dropdown-toggle').click diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js new file mode 100644 index 00000000000..1a2e188e7ae --- /dev/null +++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js @@ -0,0 +1,50 @@ +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue'; +import { mockStages } from './mock_data'; + +describe('Commit box pipeline mini graph', () => { + let wrapper; + + const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph'); + const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); + const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); + + const createComponent = () => { + wrapper = extendedWrapper( + shallowMount(CommitBoxPipelineMiniGraph, { + propsData: { + stages: mockStages, + }, + mocks: { + $apollo: { + queries: { + pipeline: { + loading: false, + }, + }, + }, + }, + }), + ); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('linked pipelines', () => { + it('should display the mini pipeine graph', () => { + expect(findMiniGraph().exists()).toBe(true); + }); + + it('should not display linked pipelines', () => { + expect(findUpstream().exists()).toBe(false); + expect(findDownstream().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/commit/mock_data.js b/spec/frontend/commit/mock_data.js new file mode 100644 index 00000000000..ef018a4fbd7 --- /dev/null +++ b/spec/frontend/commit/mock_data.js @@ -0,0 +1,117 @@ +export const mockStages = [ + { + name: 'build', + title: 'build: passed', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#build', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/root/ci-project/-/pipelines/611#build', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=build', + }, + { + name: 'test', + title: 'test: passed', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#test', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/root/ci-project/-/pipelines/611#test', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test', + }, + { + name: 'test_two', + title: 'test_two: passed', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#test_two', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/root/ci-project/-/pipelines/611#test_two', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test_two', + }, + { + name: 'manual', + title: 'manual: skipped', + status: { + icon: 'status_skipped', + text: 'skipped', + label: 'skipped', + group: 'skipped', + tooltip: 'skipped', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#manual', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png', + action: { + icon: 'play', + title: 'Play all manual', + path: '/root/ci-project/-/pipelines/611/stages/manual/play_manual', + method: 'post', + button_title: 'Play all manual', + }, + }, + path: '/root/ci-project/-/pipelines/611#manual', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=manual', + }, + { + name: 'deploy', + title: 'deploy: passed', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#deploy', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/root/ci-project/-/pipelines/611#deploy', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=deploy', + }, + { + name: 'qa', + title: 'qa: passed', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/root/ci-project/-/pipelines/611#qa', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/root/ci-project/-/pipelines/611#qa', + dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa', + }, +]; diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index de065af8b2b..0cb1092135f 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -36,6 +36,7 @@ import { TOKEN_TYPE_LABEL, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_MY_REACTION, + TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, urlSortParams, } from '~/issues_list/constants'; @@ -557,6 +558,7 @@ describe('IssuesListApp component', () => { { type: TOKEN_TYPE_ASSIGNEE, preloadedAuthors }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_LABEL }, + { type: TOKEN_TYPE_TYPE }, { type: TOKEN_TYPE_MY_REACTION }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_ITERATION }, diff --git a/spec/frontend/issues_list/mock_data.js b/spec/frontend/issues_list/mock_data.js index 681ef5dfe95..d3f3f2f9f23 100644 --- a/spec/frontend/issues_list/mock_data.js +++ b/spec/frontend/issues_list/mock_data.js @@ -107,6 +107,8 @@ export const locationSearch = [ export const locationSearchWithSpecialValues = [ 'assignee_id=123', 'assignee_username=bart', + 'type[]=issue', + 'type[]=incident', 'my_reaction_emoji=None', 'iteration_id=Current', 'milestone_title=Upcoming', @@ -142,6 +144,8 @@ export const filteredTokens = [ export const filteredTokensWithSpecialValues = [ { type: 'assignee_username', value: { data: '123', operator: OPERATOR_IS } }, { type: 'assignee_username', value: { data: 'bart', operator: OPERATOR_IS } }, + { type: 'type', value: { data: 'issue', operator: OPERATOR_IS } }, + { type: 'type', value: { data: 'incident', operator: OPERATOR_IS } }, { type: 'my_reaction_emoji', value: { data: 'None', operator: OPERATOR_IS } }, { type: 'iteration', value: { data: 'Current', operator: OPERATOR_IS } }, { type: 'milestone', value: { data: 'Upcoming', operator: OPERATOR_IS } }, @@ -173,6 +177,7 @@ export const apiParams = { export const apiParamsWithSpecialValues = { assigneeId: '123', assigneeUsernames: 'bart', + types: ['ISSUE', 'INCIDENT'], myReactionEmoji: 'None', iterationWildcardId: 'CURRENT', milestoneWildcardId: 'UPCOMING', @@ -202,6 +207,7 @@ export const urlParams = { export const urlParamsWithSpecialValues = { assignee_id: '123', 'assignee_username[]': 'bart', + 'type[]': ['issue', 'incident'], my_reaction_emoji: 'None', iteration_id: 'Current', milestone_title: 'Upcoming', diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js index 8927eb8bd24..2c62868f391 100644 --- a/spec/frontend/repository/components/delete_blob_modal_spec.js +++ b/spec/frontend/repository/components/delete_blob_modal_spec.js @@ -39,6 +39,14 @@ describe('DeleteBlobModal', () => { const findForm = () => findModal().findComponent(GlForm); const findCommitTextarea = () => findForm().findComponent(GlFormTextarea); const findTargetInput = () => findForm().findComponent(GlFormInput); + const findCommitHint = () => wrapper.find('[data-testid="hint"]'); + + const fillForm = async (inputValue = {}) => { + const { targetText, commitText } = inputValue; + + await findTargetInput().vm.$emit('input', targetText); + await findCommitTextarea().vm.$emit('input', commitText); + }; afterEach(() => { wrapper.destroy(); @@ -126,6 +134,36 @@ describe('DeleteBlobModal', () => { ); }); + describe('hint', () => { + const targetText = 'some target branch'; + const hintText = 'Try to keep the first line under 52 characters and the others under 72.'; + const charsGenerator = (length) => 'lorem'.repeat(length); + + beforeEach(async () => { + createFullComponent(); + await nextTick(); + }); + + it.each` + commitText | exist | desc + ${charsGenerator(53)} | ${true} | ${'first line length > 52'} + ${`lorem\n${charsGenerator(73)}`} | ${true} | ${'other line length > 72'} + ${charsGenerator(52)} | ${true} | ${'other line length = 52'} + ${`lorem\n${charsGenerator(72)}`} | ${true} | ${'other line length = 72'} + ${`lorem`} | ${false} | ${'first line length < 53'} + ${`lorem\nlorem`} | ${false} | ${'other line length < 53'} + `('displays hint $exist for $desc', async ({ commitText, exist }) => { + await fillForm({ targetText, commitText }); + + if (!exist) { + expect(findCommitHint().exists()).toBe(false); + return; + } + + expect(findCommitHint().text()).toBe(hintText); + }); + }); + describe('form submission', () => { let submitSpy; @@ -139,13 +177,6 @@ describe('DeleteBlobModal', () => { submitSpy.mockRestore(); }); - const fillForm = async (inputValue = {}) => { - const { targetText, commitText } = inputValue; - - await findTargetInput().vm.$emit('input', targetText); - await findCommitTextarea().vm.$emit('input', commitText); - }; - describe('invalid form', () => { beforeEach(async () => { await fillForm({ targetText: '', commitText: '' }); diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 1b5ecc4fe1b..3fa0dc95126 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -37,7 +37,7 @@ RSpec.describe GitlabSchema do describe '.execute' do describe 'setting query `max_complexity` and `max_depth`' do - subject(:result) { described_class.execute('query', kwargs).query } + subject(:result) { described_class.execute('query', **kwargs).query } shared_examples 'sets default limits' do specify do diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb index ec32afcfb7b..b300498e898 100644 --- a/spec/lib/gitlab/fake_application_settings_spec.rb +++ b/spec/lib/gitlab/fake_application_settings_spec.rb @@ -6,27 +6,35 @@ RSpec.describe Gitlab::FakeApplicationSettings do let(:defaults) do described_class.defaults.merge( foobar: 'asdf', - 'test?' => 123 + 'test?'.to_sym => 123, + # these two settings have no default in ApplicationSettingImplementation, + # so we need to set one here + domain_denylist: [], + archive_builds_in_seconds: nil ) end let(:setting) { described_class.new(defaults) } - it 'wraps OpenStruct variables properly' do + it 'defines methods for default attributes' do expect(setting.password_authentication_enabled_for_web).to be_truthy expect(setting.signup_enabled).to be_truthy expect(setting.foobar).to eq('asdf') end - it 'defines predicate methods' do + it 'defines predicate methods for boolean properties' do expect(setting.password_authentication_enabled_for_web?).to be_truthy expect(setting.signup_enabled?).to be_truthy end - it 'does not define a predicate method' do + it 'does not define a predicate method for non-boolean properties' do expect(setting.foobar?).to be_nil end + it 'returns nil for undefined attributes' do + expect(setting.does_not_exist).to be_nil + end + it 'does not override an existing predicate method' do expect(setting.test?).to eq(123) end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 285c39e7aaf..4d4d4ad4fa9 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe Packages::Package, type: :model do include SortingHelper + using RSpec::Parameterized::TableSyntax it_behaves_like 'having unique enum values' @@ -435,33 +436,154 @@ RSpec.describe Packages::Package, type: :model do let_it_be(:second_project) { create(:project, namespace: group)} let(:package) { build(:npm_package, project: project, name: name) } - let(:second_package) { build(:npm_package, project: second_project, name: name, version: '5.0.0') } - context 'following the naming convention' do - let(:name) { "@#{group.path}/test" } - - it 'will allow the first package' do + shared_examples 'validating the first package' do + it 'validates the first package' do expect(package).to be_valid end + end - it 'will not allow npm package with duplicate name' do + shared_examples 'validating the second package' do + it 'validates the second package' do + package.save! + + expect(second_package).to be_valid + end + end + + shared_examples 'not validating the second package' do |field_with_error:| + it 'does not validate the second package' do package.save! expect(second_package).not_to be_valid + case field_with_error + when :base + expect(second_package.errors.messages[:base]).to eq ['Package already exists'] + when :name + expect(second_package.errors.messages[:name]).to eq ['has already been taken'] + else + raise ArgumentError, "field #{field_with_error} not expected" + end + end + end + + context 'following the naming convention' do + let(:name) { "@#{group.path}/test" } + + context 'with the second package in the project of the first package' do + let(:second_package) { build(:npm_package, project: project, name: second_package_name, version: second_package_version) } + + context 'with no duplicated name' do + let(:second_package_name) { "@#{group.path}/test2" } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicated name' do + let(:second_package_name) { package.name } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicate name and duplicated version' do + let(:second_package_name) { package.name } + let(:second_package_version) { package.version } + + it_behaves_like 'validating the first package' + it_behaves_like 'not validating the second package', field_with_error: :name + end + end + + context 'with the second package in a different project than the first package' do + let(:second_package) { build(:npm_package, project: second_project, name: second_package_name, version: second_package_version) } + + context 'with no duplicated name' do + let(:second_package_name) { "@#{group.path}/test2" } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicated name' do + let(:second_package_name) { package.name } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicate name and duplicated version' do + let(:second_package_name) { package.name } + let(:second_package_version) { package.version } + + it_behaves_like 'validating the first package' + it_behaves_like 'not validating the second package', field_with_error: :base + end end end context 'not following the naming convention' do let(:name) { '@foobar/test' } - it 'will allow the first package' do - expect(package).to be_valid + context 'with the second package in the project of the first package' do + let(:second_package) { build(:npm_package, project: project, name: second_package_name, version: second_package_version) } + + context 'with no duplicated name' do + let(:second_package_name) { "@foobar/test2" } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicated name' do + let(:second_package_name) { package.name } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicate name and duplicated version' do + let(:second_package_name) { package.name } + let(:second_package_version) { package.version } + + it_behaves_like 'validating the first package' + it_behaves_like 'not validating the second package', field_with_error: :name + end end - it 'will allow npm package with duplicate name' do - package.save! + context 'with the second package in a different project than the first package' do + let(:second_package) { build(:npm_package, project: second_project, name: second_package_name, version: second_package_version) } - expect(second_package).to be_valid + context 'with no duplicated name' do + let(:second_package_name) { "@foobar/test2" } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicated name' do + let(:second_package_name) { package.name } + let(:second_package_version) { '5.0.0' } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end + + context 'with duplicate name and duplicated version' do + let(:second_package_name) { package.name } + let(:second_package_version) { package.version } + + it_behaves_like 'validating the first package' + it_behaves_like 'validating the second package' + end end end end diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb index 67ecbe13c1a..8cd831d2f85 100644 --- a/spec/models/personal_access_token_spec.rb +++ b/spec/models/personal_access_token_spec.rb @@ -73,6 +73,14 @@ RSpec.describe PersonalAccessToken do end end + describe '#expired_but_not_enforced?' do + let(:token) { build(:personal_access_token) } + + it 'returns false', :aggregate_failures do + expect(token).not_to be_expired_but_not_enforced + end + end + describe 'Redis storage' do let(:user_id) { 123 } let(:token) { 'KS3wegQYXBLYhQsciwsj' } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 048ac7ab6d5..7e1673a5299 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -826,8 +826,6 @@ RSpec.describe Project, factory_default: :keep do end describe '#merge_method' do - using RSpec::Parameterized::TableSyntax - where(:ff, :rebase, :method) do true | true | :ff true | false | :ff @@ -1951,8 +1949,6 @@ RSpec.describe Project, factory_default: :keep do end context 'when set to INTERNAL in application settings' do - using RSpec::Parameterized::TableSyntax - before do stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) end @@ -2013,8 +2009,6 @@ RSpec.describe Project, factory_default: :keep do end describe '#default_branch_protected?' do - using RSpec::Parameterized::TableSyntax - let_it_be(:namespace) { create(:namespace) } let_it_be(:project) { create(:project, namespace: namespace) } @@ -6839,33 +6833,44 @@ RSpec.describe Project, factory_default: :keep do end describe '#package_already_taken?' do - let_it_be(:namespace) { create(:namespace) } + let_it_be(:namespace) { create(:namespace, path: 'test') } let_it_be(:project) { create(:project, :public, namespace: namespace) } - let_it_be(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") } + let_it_be(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo", version: '1.2.3') } - context 'no package exists with the same name' do - it 'returns false' do - result = project.package_already_taken?("@#{namespace.path}/bar", package_type: :npm) - expect(result).to be false + subject { project.package_already_taken?(package_name, package_version, package_type: :npm) } + + context 'within the package project' do + where(:package_name, :package_version, :expected_result) do + '@test/bar' | '1.2.3' | false + '@test/bar' | '5.5.5' | false + '@test/foo' | '1.2.3' | false + '@test/foo' | '5.5.5' | false end - it 'returns false if it is the project that the package belongs to' do - result = project.package_already_taken?("@#{namespace.path}/foo", package_type: :npm) - expect(result).to be false + with_them do + it { is_expected.to eq expected_result} end end - context 'a package already exists with the same name' do + context 'within a different project' do let_it_be(:alt_project) { create(:project, :public, namespace: namespace) } - it 'returns true' do - result = alt_project.package_already_taken?(package.name, package_type: :npm) - expect(result).to be true + subject { alt_project.package_already_taken?(package_name, package_version, package_type: :npm) } + + where(:package_name, :package_version, :expected_result) do + '@test/bar' | '1.2.3' | false + '@test/bar' | '5.5.5' | false + '@test/foo' | '1.2.3' | true + '@test/foo' | '5.5.5' | false + end + + with_them do + it { is_expected.to eq expected_result} end context 'for a different package type' do it 'returns false' do - result = alt_project.package_already_taken?(package.name, package_type: :nuget) + result = alt_project.package_already_taken?(package.name, package.version, package_type: :nuget) expect(result).to be false end end diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index fa7bb911af8..8c35a1642e2 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -161,8 +161,10 @@ RSpec.describe API::NpmProjectPackages do end end - context 'valid package record' do - let(:params) { upload_params(package_name: package_name) } + context 'valid package params' do + let_it_be(:version) { '1.2.3' } + + let(:params) { upload_params(package_name: package_name, package_version: version) } let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } shared_examples 'handling upload with different authentications' do @@ -211,6 +213,15 @@ RSpec.describe API::NpmProjectPackages do end end + shared_examples 'uploading the package' do + it 'uploads the package' do + expect { upload_package_with_token(package_name, params) } + .to change { project.packages.count }.by(1) + + expect(response).to have_gitlab_http_status(:ok) + end + end + context 'with a scoped name' do let(:package_name) { "@#{group.path}/my_package_name" } @@ -233,24 +244,25 @@ RSpec.describe API::NpmProjectPackages do let_it_be(:second_project) { create(:project, namespace: namespace) } context 'following the naming convention' do - let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@#{group.path}/test") } + let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@#{group.path}/test", version: version) } let(:package_name) { "@#{group.path}/test" } it_behaves_like 'handling invalid record with 400 error' + + context 'with a new version' do + let_it_be(:version) { '4.5.6' } + + it_behaves_like 'uploading the package' + end end context 'not following the naming convention' do - let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@any_scope/test") } + let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@any_scope/test", version: version) } let(:package_name) { "@any_scope/test" } - it "uploads the package" do - expect { upload_package_with_token(package_name, params) } - .to change { project.packages.count }.by(1) - - expect(response).to have_gitlab_http_status(:ok) - end + it_behaves_like 'uploading the package' end end end diff --git a/spec/tooling/danger/product_intelligence_spec.rb b/spec/tooling/danger/product_intelligence_spec.rb index 17ef67e64fe..4ab911b6590 100644 --- a/spec/tooling/danger/product_intelligence_spec.rb +++ b/spec/tooling/danger/product_intelligence_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Tooling::Danger::ProductIntelligence do subject(:product_intelligence) { fake_danger.new(helper: fake_helper) } let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) } - let(:changed_files) { ['metrics/counts_7d/test_metric.yml', 'doc/development/usage_ping/dictionary.md'] } + let(:changed_files) { ['metrics/counts_7d/test_metric.yml'] } let(:changed_lines) { ['+tier: ee'] } before do @@ -20,22 +20,6 @@ RSpec.describe Tooling::Danger::ProductIntelligence do allow(fake_helper).to receive(:changed_lines).and_return(changed_lines) end - describe '#need_dictionary_changes?' do - subject { product_intelligence.need_dictionary_changes? } - - context 'when changed files do not contain dictionary changes' do - let(:changed_files) { ['config/metrics/counts_7d/test_metric.yml'] } - - it { is_expected.to be true } - end - - context 'when changed files already contains dictionary changes' do - let(:changed_files) { ['doc/development/usage_ping/dictionary.md'] } - - it { is_expected.to be false } - end - end - describe '#missing_labels' do subject { product_intelligence.missing_labels } @@ -109,10 +93,6 @@ RSpec.describe Tooling::Danger::ProductIntelligence do end end - context 'with dictionary file not changed' do - it { is_expected.to be_empty } - end - context 'with metrics files changed' do let(:changed_files) { ['config/metrics/counts_7d/test_metric.yml', 'ee/config/metrics/counts_7d/ee_metric.yml'] } diff --git a/spec/views/shared/access_tokens/_table.html.haml_spec.rb b/spec/views/shared/access_tokens/_table.html.haml_spec.rb new file mode 100644 index 00000000000..489675b5683 --- /dev/null +++ b/spec/views/shared/access_tokens/_table.html.haml_spec.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'shared/access_tokens/_table.html.haml' do + let(:type) { 'token' } + let(:type_plural) { 'tokens' } + let(:empty_message) { nil } + let(:token_expiry_enforced?) { false } + let(:impersonation) { false } + + let_it_be(:user) { create(:user) } + let_it_be(:tokens) { [create(:personal_access_token, user: user)] } + let_it_be(:project) { false } + + before do + stub_licensed_features(enforce_personal_access_token_expiration: true) + allow(Gitlab::CurrentSettings).to receive(:enforce_pat_expiration?).and_return(false) + + allow(view).to receive(:personal_access_token_expiration_enforced?).and_return(token_expiry_enforced?) + allow(view).to receive(:show_profile_token_expiry_notification?).and_return(true) + allow(view).to receive(:distance_of_time_in_words_to_now).and_return('4 days') + + if project + project.add_maintainer(user) + end + + # Forcibly removing scopes from one token as it's not possible to do with the current modal on creation + # But the check exists in the template (it may be there for legacy reasons), so we should test the outcome + if tokens.size > 1 + tokens[1].scopes = [] + end + + locals = { + type: type, + type_plural: type_plural, + active_tokens: tokens, + project: project, + impersonation: impersonation, + revoke_route_helper: ->(token) { 'path/' } + } + + if empty_message + locals[:no_active_tokens_message] = empty_message + end + + render partial: 'shared/access_tokens/table', locals: locals + end + + context 'if personal' do + it 'does not show non-personal content', :aggregate_failures do + expect(rendered).not_to have_content 'To see all the user\'s personal access tokens you must impersonate them first.' + expect(rendered).not_to have_selector 'th', text: 'Role' + end + + context 'if token expiration is enforced' do + let(:token_expiry_enforced?) { true } + + it 'does not show the subtext' do + expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.' + end + end + + context 'if token expiration is not enforced' do + let(:token_expiry_enforced?) { false } + + it 'does show the subtext' do + expect(rendered).to have_content 'Personal access tokens are not revoked upon expiration.' + end + end + end + + context 'if impersonation' do + let(:impersonation) { true } + + it 'shows the impersonation content', :aggregate_failures do + expect(rendered).to have_content 'To see all the user\'s personal access tokens you must impersonate them first.' + + expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.' + expect(rendered).not_to have_selector 'th', text: 'Role' + end + end + + context 'if project' do + let_it_be(:project) { create(:project) } + + it 'shows the project content', :aggregate_failures do + expect(rendered).to have_selector 'th', text: 'Role' + expect(rendered).to have_selector 'td', text: 'Maintainer' + + expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.' + expect(rendered).not_to have_content 'To see all the user\'s personal access tokens you must impersonate them first.' + end + end + + context 'without tokens' do + let_it_be(:tokens) { [] } + + it 'has the correct content', :aggregate_failures do + expect(rendered).to have_content 'Active tokens (0)' + expect(rendered).to have_content 'This user has no active tokens.' + end + + context 'with a custom empty text' do + let(:empty_message) { 'Custom empty message' } + + it 'shows the custom empty text' do + expect(rendered).to have_content empty_message + end + end + end + + context 'with tokens' do + let_it_be(:tokens) do + [ + create(:personal_access_token, user: user, name: 'Access token', last_used_at: 1.day.ago, expires_at: nil), + create(:personal_access_token, user: user, expires_at: 5.days.ago), + create(:personal_access_token, user: user, expires_at: Time.now), + create(:personal_access_token, user: user, expires_at: 5.days.from_now, scopes: [:read_api, :read_user]) + ] + end + + it 'has the correct content', :aggregate_failures do + # Heading content + expect(rendered).to have_content 'Active tokens (4)' + + # Table headers + expect(rendered).to have_selector 'th', text: 'Token name' + expect(rendered).to have_selector 'th', text: 'Scopes' + expect(rendered).to have_selector 'th', text: 'Created' + expect(rendered).to have_selector 'th', text: 'Last Used' + expect(rendered).to have_selector 'th', text: 'Expires' + + # Table contents + expect(rendered).to have_content 'Access token' + expect(rendered).to have_content 'read_api, read_user' + expect(rendered).to have_content 'no scopes selected' + expect(rendered).to have_content Time.now.to_date.to_s(:medium) + expect(rendered).to have_content l(1.day.ago, format: "%b %d, %Y") + + # Expiry + expect(rendered).to have_content 'Expired', count: 2 + expect(rendered).to have_content 'In 4 days' + + # Revoke buttons + expect(rendered).to have_link 'Revoke', href: 'path/', class: 'btn-danger-secondary', count: 1 + expect(rendered).to have_link 'Revoke', href: 'path/', count: 4 + end + + context 'without the last used time' do + let_it_be(:tokens) { [create(:personal_access_token, user: user, expires_at: 5.days.ago)] } + + it 'shows the last used empty text' do + expect(rendered).to have_content 'Never' + end + end + + context 'without expired at' do + let_it_be(:tokens) { [create(:personal_access_token, user: user, expires_at: nil, last_used_at: 1.day.ago)] } + + it 'shows the expired at empty text' do + expect(rendered).to have_content 'Never' + end + end + end +end |