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>2021-08-11 12:10:00 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-11 12:10:00 +0300
commit564919dfc6c6b352163d4c6dc01827a5f12ffc88 (patch)
tree9e91b8dde12a3054fffc513c6e0ab0400a039a88 /spec
parent2000704b7a6cc9eb37dd597ed03567265eda9308 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/deprecation_toolkit_env.rb8
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb3
-rw-r--r--spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js50
-rw-r--r--spec/frontend/commit/mock_data.js117
-rw-r--r--spec/frontend/issues_list/components/issues_list_app_spec.js2
-rw-r--r--spec/frontend/issues_list/mock_data.js6
-rw-r--r--spec/frontend/repository/components/delete_blob_modal_spec.js45
-rw-r--r--spec/graphql/gitlab_schema_spec.rb2
-rw-r--r--spec/lib/gitlab/fake_application_settings_spec.rb16
-rw-r--r--spec/models/packages/package_spec.rb144
-rw-r--r--spec/models/personal_access_token_spec.rb8
-rw-r--r--spec/models/project_spec.rb45
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb32
-rw-r--r--spec/tooling/danger/product_intelligence_spec.rb22
-rw-r--r--spec/views/shared/access_tokens/_table.html.haml_spec.rb166
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