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-04-29 12:10:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-29 12:10:11 +0300
commit38e4bfea582e8c755dd21613bf21658b1771449b (patch)
tree0856b453061d24face108bd980cd5a63b09ead21 /spec
parent64d80d99a907c9b5ac0d72b6a958916c496e31b1 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/deprecation_toolkit_env.rb1
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb25
-rw-r--r--spec/frontend/boards/stores/actions_spec.js9
-rw-r--r--spec/frontend/invite_member/components/invite_member_modal_spec.js67
-rw-r--r--spec/frontend/invite_member/components/invite_member_trigger_mock_data.js7
-rw-r--r--spec/frontend/invite_member/components/invite_member_trigger_spec.js48
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js12
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js38
-rw-r--r--spec/frontend/users_select/test_helper.js8
-rw-r--r--spec/helpers/invite_members_helper_spec.rb47
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_spec.rb36
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb101
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb36
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb1
-rw-r--r--spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb66
-rw-r--r--spec/models/concerns/integration_spec.rb (renamed from spec/models/integration_spec.rb)0
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb27
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb100
18 files changed, 301 insertions, 328 deletions
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
index 4bd04eabe69..2359765fedd 100644
--- a/spec/deprecation_toolkit_env.rb
+++ b/spec/deprecation_toolkit_env.rb
@@ -57,7 +57,6 @@ module DeprecationToolkitEnv
%w[
activerecord-6.0.3.6/lib/active_record/migration.rb
activesupport-6.0.3.6/lib/active_support/cache.rb
- carrierwave-1.3.1/lib/carrierwave/sanitized_file.rb
activerecord-6.0.3.6/lib/active_record/relation.rb
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
]
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 04b4caa52fe..0566ce968d2 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -130,30 +130,7 @@ RSpec.describe 'Issue Sidebar' do
end
end
- context 'when invite_members_version_b experiment is enabled' do
- before do
- stub_experiment_for_subject(invite_members_version_b: true)
- end
-
- it 'shows a link for inviting members and follows through to modal' do
- project.add_developer(user)
- visit_issue(project, issue2)
-
- open_assignees_dropdown
-
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite members', href: '#')
- expect(page).to have_selector('[data-track-event="click_invite_members_version_b"]')
- expect(page).to have_selector('[data-track-label="edit_assignee"]')
- end
-
- click_link 'Invite members'
-
- expect(page).to have_content("Oops, this feature isn't ready yet")
- end
- end
-
- context 'when invite_members_version_b experiment is disabled' do
+ context 'when user cannot invite members in assignee dropdown' do
it 'shows author in assignee dropdown and no invite link' do
project.add_developer(user)
visit_issue(project, issue2)
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 8417ac9a41a..3758723c571 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -9,7 +9,7 @@ import {
formatIssue,
getMoveData,
} from '~/boards/boards_util';
-import { inactiveId, ISSUABLE, ListType } from '~/boards/constants';
+import { inactiveId, ISSUABLE, ListType, issuableTypes } from '~/boards/constants';
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '~/boards/graphql/issue_create.mutation.graphql';
import actions, { gqlClient } from '~/boards/stores/actions';
@@ -459,7 +459,7 @@ describe('updateList', () => {
boardType: 'group',
disabled: false,
boardLists: [{ type: 'closed' }],
- issuableType: 'issue',
+ issuableType: issuableTypes.issue,
};
testAction(
@@ -503,6 +503,7 @@ describe('removeList', () => {
beforeEach(() => {
state = {
boardLists: mockListsById,
+ issuableType: issuableTypes.issue,
};
});
@@ -1375,7 +1376,7 @@ describe('setActiveItemSubscribed', () => {
[mockActiveIssue.id]: mockActiveIssue,
},
fullPath: 'gitlab-org',
- issuableType: 'issue',
+ issuableType: issuableTypes.issue,
};
const getters = { activeBoardItem: mockActiveIssue, isEpicBoard: false };
const subscribedState = true;
@@ -1483,7 +1484,7 @@ describe('setActiveIssueMilestone', () => {
describe('setActiveItemTitle', () => {
const state = {
boardItems: { [mockIssue.id]: mockIssue },
- issuableType: 'issue',
+ issuableType: issuableTypes.issue,
fullPath: 'path/f',
};
const getters = { activeBoardItem: mockIssue, isEpicBoard: false };
diff --git a/spec/frontend/invite_member/components/invite_member_modal_spec.js b/spec/frontend/invite_member/components/invite_member_modal_spec.js
deleted file mode 100644
index 03e3da2d5ef..00000000000
--- a/spec/frontend/invite_member/components/invite_member_modal_spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { GlLink, GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { stubComponent } from 'helpers/stub_component';
-import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
-import InviteMemberModal from '~/invite_member/components/invite_member_modal.vue';
-
-const memberPath = 'member_path';
-
-const GlEmoji = { template: '<img />' };
-const createComponent = () => {
- return shallowMount(InviteMemberModal, {
- propsData: {
- membersPath: memberPath,
- },
- stubs: {
- GlEmoji,
- GlModal: stubComponent(GlModal, {
- template: '<div><slot name="modal-title"></slot><slot></slot></div>',
- }),
- },
- });
-};
-
-describe('InviteMemberModal', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findLink = () => wrapper.find(GlLink);
-
- describe('rendering the modal', () => {
- it('renders the modal with the correct title', () => {
- expect(wrapper.text()).toContain("Oops, this feature isn't ready yet");
- });
-
- describe('rendering the see who link', () => {
- it('renders the correct link', () => {
- expect(findLink().attributes('href')).toBe(memberPath);
- });
- });
- });
-
- describe('tracking', () => {
- let trackingSpy;
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('send an event when go to pipelines is clicked', () => {
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
-
- triggerEvent(findLink().element);
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_who_can_invite_link', {
- label: 'invite_members_message',
- });
- });
- });
-});
diff --git a/spec/frontend/invite_member/components/invite_member_trigger_mock_data.js b/spec/frontend/invite_member/components/invite_member_trigger_mock_data.js
deleted file mode 100644
index 9b34a8027e9..00000000000
--- a/spec/frontend/invite_member/components/invite_member_trigger_mock_data.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const triggerProvides = {
- displayText: 'Invite member',
- event: 'click_invite_members_version_b',
- label: 'edit_assignee',
-};
-
-export default triggerProvides;
diff --git a/spec/frontend/invite_member/components/invite_member_trigger_spec.js b/spec/frontend/invite_member/components/invite_member_trigger_spec.js
deleted file mode 100644
index 630e2dbfc16..00000000000
--- a/spec/frontend/invite_member/components/invite_member_trigger_spec.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
-import InviteMemberTrigger from '~/invite_member/components/invite_member_trigger.vue';
-import triggerProvides from './invite_member_trigger_mock_data';
-
-const createComponent = () => {
- return shallowMount(InviteMemberTrigger, { propsData: triggerProvides });
-};
-
-describe('InviteMemberTrigger', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findLink = () => wrapper.find(GlLink);
-
- describe('displayText', () => {
- it('includes the correct displayText for the link', () => {
- expect(findLink().text()).toBe(triggerProvides.displayText);
- });
- });
-
- describe('tracking', () => {
- let trackingSpy;
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('send an event when go to pipelines is clicked', () => {
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
-
- triggerEvent(findLink().element);
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', triggerProvides.event, {
- label: triggerProvides.label,
- });
- });
- });
-});
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
index 543bc1c128a..2973c25b936 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -533,7 +533,7 @@ describe('Sidebar assignees widget', () => {
expect(findInviteMembersLink().exists()).toBe(false);
});
- it('does not render invite members link if `directlyInviteMembers` and `indirectlyInviteMembers` were not passed', async () => {
+ it('does not render invite members link if `directlyInviteMembers` was not passed', async () => {
createComponent();
await waitForPromises();
expect(findInviteMembersLink().exists()).toBe(false);
@@ -548,14 +548,4 @@ describe('Sidebar assignees widget', () => {
await waitForPromises();
expect(findInviteMembersLink().exists()).toBe(true);
});
-
- it('renders invite members link if `indirectlyInviteMembers` is true', async () => {
- createComponent({
- provide: {
- indirectlyInviteMembers: true,
- },
- });
- await waitForPromises();
- expect(findInviteMembersLink().exists()).toBe(true);
- });
});
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
index 06f7da3d1ab..cfbe7227915 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
@@ -1,25 +1,14 @@
import { shallowMount } from '@vue/test-utils';
-import InviteMemberModal from '~/invite_member/components/invite_member_modal.vue';
-import InviteMemberTrigger from '~/invite_member/components/invite_member_trigger.vue';
import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import SidebarInviteMembers from '~/sidebar/components/assignees/sidebar_invite_members.vue';
-const testProjectMembersPath = 'test-path';
-
describe('Sidebar invite members component', () => {
let wrapper;
const findDirectInviteLink = () => wrapper.findComponent(InviteMembersTrigger);
- const findIndirectInviteLink = () => wrapper.findComponent(InviteMemberTrigger);
- const findInviteModal = () => wrapper.findComponent(InviteMemberModal);
- const createComponent = ({ directlyInviteMembers = false } = {}) => {
- wrapper = shallowMount(SidebarInviteMembers, {
- provide: {
- directlyInviteMembers,
- projectMembersPath: testProjectMembersPath,
- },
- });
+ const createComponent = () => {
+ wrapper = shallowMount(SidebarInviteMembers);
};
afterEach(() => {
@@ -28,32 +17,11 @@ describe('Sidebar invite members component', () => {
describe('when directly inviting members', () => {
beforeEach(() => {
- createComponent({ directlyInviteMembers: true });
+ createComponent();
});
it('renders a direct link to project members path', () => {
expect(findDirectInviteLink().exists()).toBe(true);
});
-
- it('does not render invite members trigger and modal components', () => {
- expect(findIndirectInviteLink().exists()).toBe(false);
- expect(findInviteModal().exists()).toBe(false);
- });
- });
-
- describe('when indirectly inviting members', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('does not render a direct link to project members path', () => {
- expect(findDirectInviteLink().exists()).toBe(false);
- });
-
- it('does not render invite members trigger and modal components', () => {
- expect(findIndirectInviteLink().exists()).toBe(true);
- expect(findInviteModal().exists()).toBe(true);
- expect(findInviteModal().props('membersPath')).toBe(testProjectMembersPath);
- });
});
});
diff --git a/spec/frontend/users_select/test_helper.js b/spec/frontend/users_select/test_helper.js
index 89bbbba9913..c5adbe9bb09 100644
--- a/spec/frontend/users_select/test_helper.js
+++ b/spec/frontend/users_select/test_helper.js
@@ -1,7 +1,7 @@
-import { waitFor } from '@testing-library/dom';
import MockAdapter from 'axios-mock-adapter';
import { memoize, cloneDeep } from 'lodash';
import { getFixture, getJSONFixture } from 'helpers/fixtures';
+import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import UsersSelect from '~/users_select';
@@ -103,8 +103,10 @@ export const setAssignees = (...users) => {
);
};
export const toggleDropdown = () => findUserSearchButton().click();
-export const waitForDropdownItems = () =>
- waitFor(() => expect(findDropdownItem(getUsersFixtureAt(0))).not.toBeNull());
+export const waitForDropdownItems = async () => {
+ await axios.waitForAll();
+ await waitForPromises();
+};
// assertion helpers ---------------------------------------------------------
export const createUnassignedExpectation = () => {
diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb
index 7ddf7d059e5..dbe4f970a99 100644
--- a/spec/helpers/invite_members_helper_spec.rb
+++ b/spec/helpers/invite_members_helper_spec.rb
@@ -12,21 +12,6 @@ RSpec.describe InviteMembersHelper do
helper.extend(Gitlab::Experimentation::ControllerConcern)
end
- describe '#show_invite_members_track_event' do
- it 'shows values when can directly invite members' do
- allow(helper).to receive(:directly_invite_members?).and_return(true)
-
- expect(helper.show_invite_members_track_event).to eq 'show_invite_members'
- end
-
- it 'shows values when can indirectly invite members' do
- allow(helper).to receive(:directly_invite_members?).and_return(false)
- allow(helper).to receive(:indirectly_invite_members?).and_return(true)
-
- expect(helper.show_invite_members_track_event).to eq 'show_invite_members_version_b'
- end
- end
-
context 'with project' do
before do
assign(:project, project)
@@ -87,38 +72,6 @@ RSpec.describe InviteMembersHelper do
end
end
end
-
- describe "#indirectly_invite_members?" do
- context 'when a user is a developer' do
- before do
- allow(helper).to receive(:current_user) { developer }
- end
-
- it 'returns false' do
- allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { false }
-
- expect(helper.indirectly_invite_members?).to eq false
- end
-
- it 'returns true' do
- allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { true }
-
- expect(helper.indirectly_invite_members?).to eq true
- end
- end
-
- context 'when a user is an owner' do
- before do
- allow(helper).to receive(:current_user) { owner }
- end
-
- it 'returns false' do
- allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { true }
-
- expect(helper.indirectly_invite_members?).to eq false
- end
- end
- end
end
context 'with group' do
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
index abee1fec80a..78e0b7627e9 100644
--- a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -9,6 +9,42 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
it { is_expected.to belong_to(:batched_migration).with_foreign_key(:batched_background_migration_id) }
end
+ describe 'scopes' do
+ let_it_be(:fixed_time) { Time.new(2021, 04, 27, 10, 00, 00, 00) }
+
+ let_it_be(:pending_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time) }
+ let_it_be(:running_job) { create(:batched_background_migration_job, status: :running, updated_at: fixed_time) }
+ let_it_be(:stuck_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
+ let_it_be(:failed_job) { create(:batched_background_migration_job, status: :failed, attempts: 1) }
+
+ before_all do
+ create(:batched_background_migration_job, status: :failed, attempts: described_class::MAX_ATTEMPTS)
+ create(:batched_background_migration_job, status: :succeeded)
+ end
+
+ before do
+ travel_to fixed_time
+ end
+
+ describe '.active' do
+ it 'returns active jobs' do
+ expect(described_class.active).to contain_exactly(pending_job, running_job, stuck_job)
+ end
+ end
+
+ describe '.stuck' do
+ it 'returns stuck jobs' do
+ expect(described_class.stuck).to contain_exactly(stuck_job)
+ end
+ end
+
+ describe '.retriable' do
+ it 'returns retriable jobs' do
+ expect(described_class.retriable).to contain_exactly(failed_job, stuck_job)
+ end
+ end
+ end
+
describe 'delegated batched_migration attributes' do
let(:batched_job) { build(:batched_background_migration_job) }
let(:batched_migration) { batched_job.batched_migration }
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
index 79b21172dc6..9f0493ab0d7 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
@@ -17,9 +17,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
end
it 'marks the migration as finished' do
- relation = Gitlab::Database::BackgroundMigration::BatchedMigration.finished.where(id: migration.id)
+ runner.run_migration_job(migration)
- expect { runner.run_migration_job(migration) }.to change { relation.count }.by(1)
+ expect(migration.reload).to be_finished
end
end
@@ -92,7 +92,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
let!(:event3) { create(:event) }
let!(:migration) do
- create(:batched_background_migration, :active, batch_size: 2, min_value: event1.id, max_value: event3.id)
+ create(:batched_background_migration, :active, batch_size: 2, min_value: event1.id, max_value: event2.id)
end
let!(:previous_job) do
@@ -101,14 +101,24 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
min_value: event1.id,
max_value: event2.id,
batch_size: 2,
- sub_batch_size: 1)
+ sub_batch_size: 1,
+ status: :succeeded
+ )
end
let(:job_relation) do
Gitlab::Database::BackgroundMigration::BatchedJob.where(batched_background_migration_id: migration.id)
end
+ context 'when the migration has no batches remaining' do
+ it_behaves_like 'it has completed the migration'
+ end
+
context 'when the migration has batches to process' do
+ before do
+ migration.update!(max_value: event3.id)
+ end
+
it 'runs the migration job for the next batch' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
@@ -132,17 +142,82 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
end
end
- context 'when the migration has no batches remaining' do
+ context 'when migration has failed jobs' do
before do
- create(:batched_background_migration_job,
- batched_migration: migration,
- min_value: event3.id,
- max_value: event3.id,
- batch_size: 2,
- sub_batch_size: 1)
+ previous_job.update!(status: :failed)
end
- it_behaves_like 'it has completed the migration'
+ it 'retries the failed job' do
+ expect(migration_wrapper).to receive(:perform) do |job_record|
+ expect(job_record).to eq(previous_job)
+ end
+
+ expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(0)
+ end
+
+ context 'when failed job has reached the maximum number of attempts' do
+ before do
+ previous_job.update!(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
+ end
+
+ it 'marks the migration as failed' do
+ expect(migration_wrapper).not_to receive(:perform)
+
+ expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(0)
+
+ expect(migration).to be_failed
+ end
+ end
+ end
+
+ context 'when migration has stuck jobs' do
+ before do
+ previous_job.update!(status: :running, updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ end
+
+ it 'retries the stuck job' do
+ expect(migration_wrapper).to receive(:perform) do |job_record|
+ expect(job_record).to eq(previous_job)
+ end
+
+ expect { runner.run_migration_job(migration.reload) }.to change { job_relation.count }.by(0)
+ end
+ end
+
+ context 'when migration has possible stuck jobs' do
+ before do
+ previous_job.update!(status: :running, updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ end
+
+ it 'keeps the migration active' do
+ expect(migration_wrapper).not_to receive(:perform)
+
+ expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(0)
+
+ expect(migration.reload).to be_active
+ end
+ end
+
+ context 'when the migration has batches to process and failed jobs' do
+ before do
+ migration.update!(max_value: event3.id)
+ previous_job.update!(status: :failed)
+ end
+
+ it 'runs next batch then retries the failed job' do
+ expect(migration_wrapper).to receive(:perform) do |job_record|
+ expect(job_record).to eq(job_relation.last)
+ job_record.update!(status: :succeeded)
+ end
+
+ expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(1)
+
+ expect(migration_wrapper).to receive(:perform) do |job_record|
+ expect(job_record).to eq(previous_job)
+ end
+
+ expect { runner.run_migration_job(migration.reload) }.to change { job_relation.count }.by(0)
+ end
end
end
end
@@ -189,10 +264,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
it 'runs all jobs inline until finishing the migration' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.first)
+ job_record.update!(status: :succeeded)
end
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
+ job_record.update!(status: :succeeded)
end
expect { runner.run_entire_migration(migration) }.to change { job_relation.count }.by(2)
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index fdbc2286502..987f2c5a935 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -49,6 +49,42 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
end
end
+ context 'when running a job that failed previously' do
+ let!(:job_record) do
+ create(:batched_background_migration_job,
+ batched_migration: active_migration,
+ pause_ms: pause_ms,
+ attempts: 1,
+ status: :failed,
+ finished_at: 1.hour.ago,
+ metrics: { 'my_metrics' => 'some_value' }
+ )
+ end
+
+ it 'increments attempts and updates other fields' do
+ updated_metrics = { 'updated_metrics' => 'some_value' }
+
+ expect(job_instance).to receive(:perform)
+ expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
+
+ expect(job_record).to receive(:update!).with(
+ hash_including(attempts: 2, status: :running, finished_at: nil, metrics: {})
+ ).and_call_original
+
+ freeze_time do
+ subject
+
+ job_record.reload
+
+ expect(job_record).not_to be_failed
+ expect(job_record.attempts).to eq(2)
+ expect(job_record.started_at).to eq(Time.current)
+ expect(job_record.finished_at).to eq(Time.current)
+ expect(job_record.metrics).to eq(updated_metrics)
+ end
+ end
+ end
+
context 'reporting prometheus metrics' do
let(:labels) { job_record.batched_migration.prometheus_labels }
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index 5fef14bd2a0..10bfa9e8d0e 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -7,7 +7,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Experimentation::EXPERIMENTS do
it 'temporarily ensures we know what experiments exist for backwards compatibility' do
expected_experiment_keys = [
- :invite_members_version_b,
:invite_members_empty_group_version_a,
:contact_sales_btn_in_app
]
diff --git a/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb
new file mode 100644
index 00000000000..89b03e1c918
--- /dev/null
+++ b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::Menus::CiCdMenu do
+ let(:project) { build(:project) }
+ let(:user) { project.owner }
+ let(:can_view_pipeline_editor) { true }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: 'master', can_view_pipeline_editor: can_view_pipeline_editor) }
+
+ subject { described_class.new(context) }
+
+ describe '#render?' do
+ context 'when user cannot read builds' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ expect(subject.render?).to eq false
+ end
+ end
+
+ context 'when user can read builds' do
+ it 'returns true' do
+ expect(subject.render?).to eq true
+ end
+ end
+ end
+
+ describe 'Pipelines Editor' do
+ subject { described_class.new(context).items.index { |e| e.item_id == :pipelines_editor } }
+
+ context 'when user cannot view pipeline editor' do
+ let(:can_view_pipeline_editor) { false }
+
+ it 'does not include pipeline editor menu item' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when user can view pipeline editor' do
+ it 'includes pipeline editor menu item' do
+ is_expected.not_to be_nil
+ end
+ end
+ end
+
+ describe 'Artifacts' do
+ subject { described_class.new(context).items.index { |e| e.item_id == :artifacts } }
+
+ context 'when feature flag :artifacts_management_page is disabled' do
+ it 'does not include artifacts menu item' do
+ stub_feature_flags(artifacts_management_page: false)
+
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when feature flag :artifacts_management_page is enabled' do
+ it 'includes artifacts menu item' do
+ stub_feature_flags(artifacts_management_page: true)
+
+ is_expected.not_to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/models/integration_spec.rb b/spec/models/concerns/integration_spec.rb
index 781e2aece56..781e2aece56 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/concerns/integration_spec.rb
diff --git a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
index 49c3674277d..736c353c2aa 100644
--- a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
@@ -22,32 +22,7 @@ RSpec.shared_examples 'issuable invite members experiments' do
end
end
- context 'when invite_members_version_b experiment is enabled' do
- before do
- stub_experiment_for_subject(invite_members_version_b: true)
- end
-
- it 'shows a link for inviting members and follows through to modal' do
- project.add_developer(user)
- visit issuable_path
-
- find('.block.assignee .edit-link').click
-
- wait_for_requests
-
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite Members', href: '#')
- expect(page).to have_selector('[data-track-event="click_invite_members_version_b"]')
- expect(page).to have_selector('[data-track-label="edit_assignee"]')
- end
-
- click_link 'Invite Members'
-
- expect(page).to have_content("Oops, this feature isn't ready yet")
- end
- end
-
- context 'when invite_members_version_b experiment is disabled' do
+ context 'when user cannot invite members in assignee dropdown' do
it 'shows author in assignee dropdown and no invite link' do
project.add_developer(user)
visit issuable_path
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index c501c418466..16362aed1cd 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -257,6 +257,64 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
+ describe 'CI/CD' do
+ it 'has a link to pipelines page' do
+ render
+
+ expect(rendered).to have_link('CI/CD', href: project_pipelines_path(project))
+ end
+
+ describe 'Artifacts' do
+ it 'has a link to the artifacts page' do
+ render
+
+ expect(rendered).to have_link('Artifacts', href: project_artifacts_path(project))
+ end
+ end
+
+ describe 'Jobs' do
+ it 'has a link to the jobs page' do
+ render
+
+ expect(rendered).to have_link('Jobs', href: project_jobs_path(project))
+ end
+ end
+
+ describe 'Pipeline Schedules' do
+ it 'has a link to the pipeline schedules page' do
+ render
+
+ expect(rendered).to have_link('Schedules', href: pipeline_schedules_path(project))
+ end
+ end
+
+ describe 'Pipelines' do
+ it 'has a link to the pipelines page' do
+ render
+
+ expect(rendered).to have_link('Pipelines', href: project_pipelines_path(project))
+ end
+ end
+
+ describe 'Pipeline Editor' do
+ it 'has a link to the pipeline editor' do
+ render
+
+ expect(rendered).to have_link('Editor', href: project_ci_pipeline_editor_path(project))
+ end
+
+ context 'when user cannot access pipeline editor' do
+ it 'does not has a link to the pipeline editor' do
+ allow(view).to receive(:can_view_pipeline_editor?).and_return(false)
+
+ render
+
+ expect(rendered).not_to have_link('Editor', href: project_ci_pipeline_editor_path(project))
+ end
+ end
+ end
+ end
+
describe 'packages tab' do
before do
stub_container_registry_config(enabled: true)
@@ -419,48 +477,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
- describe 'ci/cd settings tab' do
- before do
- project.update!(archived: project_archived)
- end
-
- context 'when project is archived' do
- let(:project_archived) { true }
-
- it 'does not show the ci/cd settings tab' do
- render
-
- expect(rendered).not_to have_link('CI/CD', href: project_settings_ci_cd_path(project))
- end
- end
-
- context 'when project is active' do
- let(:project_archived) { false }
-
- it 'shows the ci/cd settings tab' do
- render
-
- expect(rendered).to have_link('CI/CD', href: project_settings_ci_cd_path(project))
- end
- end
- end
-
- describe 'pipeline editor link' do
- it 'shows the pipeline editor link' do
- render
-
- expect(rendered).to have_link('Editor', href: project_ci_pipeline_editor_path(project))
- end
-
- it 'does not show the pipeline editor link' do
- allow(view).to receive(:can_view_pipeline_editor?).and_return(false)
-
- render
-
- expect(rendered).not_to have_link('Editor', href: project_ci_pipeline_editor_path(project))
- end
- end
-
describe 'operations settings tab' do
describe 'archive projects' do
before do