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:
Diffstat (limited to 'spec')
-rw-r--r--spec/deprecation_toolkit_env.rb6
-rw-r--r--spec/frontend/issues_list/components/issues_list_app_spec.js115
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js62
-rw-r--r--spec/frontend/vue_shared/components/issue/issue_assignees_spec.js4
-rw-r--r--spec/graphql/mutations/commits/create_spec.rb5
-rw-r--r--spec/helpers/issues_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb25
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb32
-rw-r--r--spec/lib/gitlab/git/remote_repository_spec.rb41
-rw-r--r--spec/models/ci/build_spec.rb20
-rw-r--r--spec/workers/build_hooks_worker_spec.rb16
11 files changed, 248 insertions, 80 deletions
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
index 57d32b5423c..00d66ff3cdc 100644
--- a/spec/deprecation_toolkit_env.rb
+++ b/spec/deprecation_toolkit_env.rb
@@ -55,9 +55,9 @@ module DeprecationToolkitEnv
# one by one
def self.allowed_kwarg_warning_paths
%w[
- activerecord-6.0.3.6/lib/active_record/migration.rb
- activesupport-6.0.3.6/lib/active_support/cache.rb
- activerecord-6.0.3.6/lib/active_record/relation.rb
+ activerecord-6.0.3.7/lib/active_record/migration.rb
+ activesupport-6.0.3.7/lib/active_support/cache.rb
+ activerecord-6.0.3.7/lib/active_record/relation.rb
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
attr_encrypted-3.1.0/lib/attr_encrypted/adapters/active_record.rb
]
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 5d83bf0142f..8a91dca51c2 100644
--- a/spec/frontend/issues_list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues_list/components/issues_list_app_spec.js
@@ -18,6 +18,15 @@ import {
PAGE_SIZE_MANUAL,
PARAM_DUE_DATE,
RELATIVE_POSITION_DESC,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_CONFIDENTIAL,
+ TOKEN_TYPE_EPIC,
+ TOKEN_TYPE_ITERATION,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
+ TOKEN_TYPE_WEIGHT,
urlSortParams,
} from '~/issues_list/constants';
import eventHub from '~/issues_list/eventhub';
@@ -39,8 +48,8 @@ describe('IssuesListApp component', () => {
endpoint: 'api/endpoint',
exportCsvPath: 'export/csv/path',
hasBlockedIssuesFeature: true,
- hasIssues: true,
hasIssueWeightsFeature: true,
+ hasProjectIssues: true,
isSignedIn: false,
issuesPath: 'path/to/issues',
jiraIntegrationPath: 'jira/integration/path',
@@ -320,7 +329,7 @@ describe('IssuesListApp component', () => {
beforeEach(async () => {
global.jsdom.reconfigure({ url: `${TEST_HOST}?search=no+results` });
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -336,7 +345,7 @@ describe('IssuesListApp component', () => {
describe('when "Open" tab has no issues', () => {
beforeEach(async () => {
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -356,7 +365,7 @@ describe('IssuesListApp component', () => {
url: setUrlParams({ state: IssuableStates.Closed }, TEST_HOST),
});
- wrapper = mountComponent({ provide: { hasIssues: true }, mountFn: mount });
+ wrapper = mountComponent({ provide: { hasProjectIssues: true }, mountFn: mount });
await waitForPromises();
});
@@ -374,7 +383,7 @@ describe('IssuesListApp component', () => {
describe('when user is logged in', () => {
beforeEach(() => {
wrapper = mountComponent({
- provide: { hasIssues: false, isSignedIn: true },
+ provide: { hasProjectIssues: false, isSignedIn: true },
mountFn: mount,
});
});
@@ -413,7 +422,7 @@ describe('IssuesListApp component', () => {
describe('when user is logged out', () => {
beforeEach(() => {
wrapper = mountComponent({
- provide: { hasIssues: false, isSignedIn: false },
+ provide: { hasProjectIssues: false, isSignedIn: false },
});
});
@@ -430,6 +439,100 @@ describe('IssuesListApp component', () => {
});
});
+ describe('tokens', () => {
+ describe('when user is signed out', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ isSignedIn: false,
+ },
+ });
+ });
+
+ it('does not render My-Reaction or Confidential tokens', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_MY_REACTION },
+ { type: TOKEN_TYPE_CONFIDENTIAL },
+ ]);
+ });
+ });
+
+ describe('when iterations are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ projectIterationsPath: '',
+ },
+ });
+ });
+
+ it('does not render Iteration token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_ITERATION },
+ ]);
+ });
+ });
+
+ describe('when epics are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ groupEpicsPath: '',
+ },
+ });
+ });
+
+ it('does not render Epic token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_EPIC },
+ ]);
+ });
+ });
+
+ describe('when weights are not available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ groupEpicsPath: '',
+ },
+ });
+ });
+
+ it('does not render Weight token', () => {
+ expect(findIssuableList().props('searchTokens')).not.toMatchObject([
+ { type: TOKEN_TYPE_WEIGHT },
+ ]);
+ });
+ });
+
+ describe('when all tokens are available', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ provide: {
+ isSignedIn: true,
+ projectIterationsPath: 'project/iterations/path',
+ groupEpicsPath: 'group/epics/path',
+ hasIssueWeightsFeature: true,
+ },
+ });
+ });
+
+ it('renders all tokens', () => {
+ expect(findIssuableList().props('searchTokens')).toMatchObject([
+ { type: TOKEN_TYPE_AUTHOR },
+ { type: TOKEN_TYPE_ASSIGNEE },
+ { type: TOKEN_TYPE_MILESTONE },
+ { type: TOKEN_TYPE_LABEL },
+ { type: TOKEN_TYPE_MY_REACTION },
+ { type: TOKEN_TYPE_CONFIDENTIAL },
+ { type: TOKEN_TYPE_ITERATION },
+ { type: TOKEN_TYPE_EPIC },
+ { type: TOKEN_TYPE_WEIGHT },
+ ]);
+ });
+ });
+ });
+
describe('events', () => {
describe('when "click-tab" event is emitted by IssuableList', () => {
beforeEach(() => {
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
index 3b50927dcc6..e2f8dbd8ceb 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
@@ -3,6 +3,7 @@ import {
GlFilteredSearchTokenSegment,
GlFilteredSearchSuggestion,
GlDropdownDivider,
+ GlLoadingIcon,
} from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
@@ -35,6 +36,7 @@ function createComponent(options = {}) {
value = { data: '' },
active = false,
stubs = defaultStubs,
+ data = {},
} = options;
return mount(AuthorToken, {
propsData: {
@@ -47,20 +49,32 @@ function createComponent(options = {}) {
alignSuggestions: function fakeAlignSuggestions() {},
suggestionsListClass: 'custom-class',
},
+ data() {
+ return { ...data };
+ },
stubs,
});
}
describe('AuthorToken', () => {
+ const originalGon = window.gon;
+ const currentUserLength = 1;
let mock;
let wrapper;
beforeEach(() => {
+ window.gon = {
+ ...originalGon,
+ current_user_id: 13,
+ current_user_fullname: 'Administrator',
+ current_username: 'root',
+ current_user_avatar_url: 'avatar/url',
+ };
mock = new MockAdapter(axios);
- wrapper = createComponent();
});
afterEach(() => {
+ window.gon = originalGon;
mock.restore();
wrapper.destroy();
});
@@ -91,6 +105,8 @@ describe('AuthorToken', () => {
describe('fetchAuthorBySearchTerm', () => {
it('calls `config.fetchAuthors` with provided searchTerm param', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors');
wrapper.vm.fetchAuthorBySearchTerm(mockAuthors[0].username);
@@ -102,6 +118,8 @@ describe('AuthorToken', () => {
});
it('sets response to `authors` when request is succesful', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockResolvedValue(mockAuthors);
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -112,6 +130,8 @@ describe('AuthorToken', () => {
});
it('calls `createFlash` with flash error message when request fails', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -122,6 +142,8 @@ describe('AuthorToken', () => {
});
it('sets `loading` to false when request completes', () => {
+ wrapper = createComponent();
+
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
wrapper.vm.fetchAuthorBySearchTerm('root');
@@ -133,21 +155,16 @@ describe('AuthorToken', () => {
});
describe('template', () => {
- beforeEach(() => {
- wrapper.setData({
- authors: mockAuthors,
- });
-
- return wrapper.vm.$nextTick();
- });
-
it('renders gl-filtered-search-token component', () => {
+ wrapper = createComponent({ data: { authors: mockAuthors } });
+
expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
});
it('renders token item when value is selected', () => {
- wrapper.setProps({
+ wrapper = createComponent({
value: { data: mockAuthors[0].username },
+ data: { authors: mockAuthors },
});
return wrapper.vm.$nextTick(() => {
@@ -172,7 +189,7 @@ describe('AuthorToken', () => {
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(defaultAuthors.length);
+ expect(suggestions).toHaveLength(defaultAuthors.length + currentUserLength);
defaultAuthors.forEach((label, index) => {
expect(suggestions.at(index).text()).toBe(label.text);
});
@@ -189,7 +206,6 @@ describe('AuthorToken', () => {
suggestionsSegment.vm.$emit('activate');
await wrapper.vm.$nextTick();
- expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false);
expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
});
@@ -206,8 +222,28 @@ describe('AuthorToken', () => {
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(1);
+ expect(suggestions).toHaveLength(1 + currentUserLength);
expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_ANY.text);
});
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockAuthorToken, defaultAuthors: [] },
+ stubs: { Portal: true },
+ });
+ });
+
+ it('shows loading icon', () => {
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('shows current user', () => {
+ const firstSuggestion = wrapper.findComponent(GlFilteredSearchSuggestion).text();
+ expect(firstSuggestion).toContain('Administrator');
+ expect(firstSuggestion).toContain('@root');
+ });
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js b/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
index 5c29c267c99..2658fa4a706 100644
--- a/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
+++ b/spec/frontend/vue_shared/components/issue/issue_assignees_spec.js
@@ -91,7 +91,7 @@ describe('IssueAssigneesComponent', () => {
});
it('computes alt text for assignee avatar', () => {
- expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Avatar for Terrell Graham');
+ expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Assigned to Terrell Graham');
});
it('renders component root element with class `issue-assignees`', () => {
@@ -106,7 +106,7 @@ describe('IssueAssigneesComponent', () => {
const expected = mockAssigneesList.slice(0, TEST_MAX_VISIBLE - 1).map((x) =>
expect.objectContaining({
linkHref: x.web_url,
- imgAlt: `Avatar for ${x.name}`,
+ imgAlt: `Assigned to ${x.name}`,
imgCssClasses: TEST_CSS_CLASSES,
imgSrc: x.avatar_url,
imgSize: TEST_ICON_SIZE,
diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb
index 152b5d87da0..097e70bada6 100644
--- a/spec/graphql/mutations/commits/create_spec.rb
+++ b/spec/graphql/mutations/commits/create_spec.rb
@@ -74,6 +74,10 @@ RSpec.describe Mutations::Commits::Create do
expect(commit_pipeline_path).to match(%r(pipelines/sha/\w+))
end
+ it 'returns the content of the commit' do
+ expect(subject[:content]).to eq(actions.pluck(:content))
+ end
+
it 'returns a new commit' do
expect(mutated_commit).to have_attributes(message: message, project: project)
expect(subject[:errors]).to be_empty
@@ -166,6 +170,7 @@ RSpec.describe Mutations::Commits::Create do
it 'returns a new commit' do
expect(mutated_commit).to have_attributes(message: message, project: project)
expect(subject[:errors]).to be_empty
+ expect(subject[:content]).to eq(actions.pluck(:content))
expect_to_contain_deltas([
a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: 'ANOTHER_FILE.md')
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 17fe90c830e..ceca83d107b 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -304,7 +304,7 @@ RSpec.describe IssuesHelper do
empty_state_svg_path: '#',
endpoint: expose_path(api_v4_projects_issues_path(id: project.id)),
export_csv_path: export_csv_project_issues_path(project),
- has_issues: project_issues(project).exists?.to_s,
+ has_project_issues: project_issues(project).exists?.to_s,
import_csv_issues_path: '#',
initial_email: project.new_issuable_address(current_user, 'issue'),
is_signed_in: current_user.present?.to_s,
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index e3507df5736..47f9bcc08d2 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -356,4 +356,29 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject
end
end
+
+ describe '.for_configuration' do
+ let!(:migration) do
+ create(
+ :batched_background_migration,
+ job_class_name: 'MyJobClass',
+ table_name: :projects,
+ column_name: :id,
+ job_arguments: [[:id], [:id_convert_to_bigint]]
+ )
+ end
+
+ before do
+ create(:batched_background_migration, job_class_name: 'OtherClass')
+ create(:batched_background_migration, table_name: 'other_table')
+ create(:batched_background_migration, column_name: 'other_column')
+ create(:batched_background_migration, job_arguments: %w[other arguments])
+ end
+
+ it 'finds the migration matching the given configuration parameters' do
+ actual = described_class.for_configuration('MyJobClass', :projects, :id, [[:id], [:id_convert_to_bigint]])
+
+ expect(actual).to contain_exactly(migration)
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index c6d456964cf..85083bccaca 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -269,6 +269,38 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
allow(Gitlab::Database::PgClass).to receive(:for_table).and_call_original
end
+ context 'when such migration already exists' do
+ it 'does not create duplicate migration' do
+ create(
+ :batched_background_migration,
+ job_class_name: 'MyJobClass',
+ table_name: :projects,
+ column_name: :id,
+ interval: 10.minutes,
+ min_value: 5,
+ max_value: 1005,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 200,
+ sub_batch_size: 20,
+ job_arguments: [[:id], [:id_convert_to_bigint]]
+ )
+
+ expect do
+ model.queue_batched_background_migration(
+ 'MyJobClass',
+ :projects,
+ :id,
+ [:id], [:id_convert_to_bigint],
+ job_interval: 5.minutes,
+ batch_min_value: 5,
+ batch_max_value: 1000,
+ batch_class_name: 'MyBatchClass',
+ batch_size: 100,
+ sub_batch_size: 10)
+ end.not_to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }
+ end
+ end
+
it 'creates the database record for the migration' do
expect(Gitlab::Database::PgClass).to receive(:for_table).with(:projects).and_return(pgclass_info)
diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb
index 84c17234ae4..c7bc81573a6 100644
--- a/spec/lib/gitlab/git/remote_repository_spec.rb
+++ b/spec/lib/gitlab/git/remote_repository_spec.rb
@@ -58,45 +58,4 @@ RSpec.describe Gitlab::Git::RemoteRepository, :seed_helper do
it { expect(subject.same_repository?(other_repository)).to eq(result) }
end
end
-
- describe '#fetch_env' do
- let(:remote_repository) { described_class.new(repository) }
-
- let(:gitaly_client) { double(:gitaly_client) }
- let(:address) { 'fake-address' }
- let(:token) { 'fake-token' }
-
- subject { remote_repository.fetch_env }
-
- before do
- allow(remote_repository).to receive(:gitaly_client).and_return(gitaly_client)
-
- expect(gitaly_client).to receive(:address).with(repository.storage).and_return(address)
- expect(gitaly_client).to receive(:token).with(repository.storage).and_return(token)
- end
-
- it { expect(subject).to be_a(Hash) }
- it { expect(subject['GITALY_ADDRESS']).to eq(address) }
- it { expect(subject['GITALY_TOKEN']).to eq(token) }
- it { expect(subject['GITALY_WD']).to eq(Dir.pwd) }
-
- it 'creates a plausible GIT_SSH_COMMAND' do
- git_ssh_command = subject['GIT_SSH_COMMAND']
-
- expect(git_ssh_command).to start_with('/')
- expect(git_ssh_command).to end_with('/gitaly-ssh upload-pack')
- end
-
- it 'creates a plausible GITALY_PAYLOAD' do
- req = Gitaly::SSHUploadPackRequest.decode_json(subject['GITALY_PAYLOAD'])
-
- expect(remote_repository.gitaly_repository).to eq(req.repository)
- end
-
- context 'when the token is blank' do
- let(:token) { '' }
-
- it { expect(subject.keys).not_to include('GITALY_TOKEN') }
- end
- end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 33233171813..c2f49b10639 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1880,6 +1880,26 @@ RSpec.describe Ci::Build do
it { is_expected.not_to be_retryable }
end
+
+ context 'when a canceled build has been retried already' do
+ before do
+ project.add_developer(user)
+ build.cancel!
+ described_class.retry(build, user)
+ end
+
+ context 'when prevent_retry_of_retried_jobs feature flag is enabled' do
+ it { is_expected.not_to be_retryable }
+ end
+
+ context 'when prevent_retry_of_retried_jobs feature flag is disabled' do
+ before do
+ stub_feature_flags(prevent_retry_of_retried_jobs: false)
+ end
+
+ it { is_expected.to be_retryable }
+ end
+ end
end
end
diff --git a/spec/workers/build_hooks_worker_spec.rb b/spec/workers/build_hooks_worker_spec.rb
index 62e1a4fd294..5f7e7e5fb00 100644
--- a/spec/workers/build_hooks_worker_spec.rb
+++ b/spec/workers/build_hooks_worker_spec.rb
@@ -24,20 +24,8 @@ RSpec.describe BuildHooksWorker do
end
describe '.perform_async' do
- context 'when delayed_perform_for_build_hooks_worker feature flag is disabled' do
- before do
- stub_feature_flags(delayed_perform_for_build_hooks_worker: false)
- end
-
- it 'delays scheduling a job by calling perform_in with default delay' do
- expect(described_class).to receive(:perform_in).with(ApplicationWorker::DEFAULT_DELAY_INTERVAL.second, 123)
-
- described_class.perform_async(123)
- end
- end
-
- it 'delays scheduling a job by calling perform_in' do
- expect(described_class).to receive(:perform_in).with(described_class::DATA_CONSISTENCY_DELAY.second, 123)
+ it 'delays scheduling a job by calling perform_in with default delay' do
+ expect(described_class).to receive(:perform_in).with(ApplicationWorker::DEFAULT_DELAY_INTERVAL.second, 123)
described_class.perform_async(123)
end