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>2022-05-13 12:07:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-13 12:07:54 +0300
commit2705a15deaef07b1a38a53b9539d02f8ad499ce3 (patch)
tree33d51226e6c70efe71e5e531bb8073cb145bdc22 /spec
parent1a397155d654096edc0a900e2b69f6913bc26eb4 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/crm/contacts_root_spec.js2
-rw-r--r--spec/frontend/crm/organizations_root_spec.js4
-rw-r--r--spec/frontend/issuable/components/status_box_spec.js74
-rw-r--r--spec/frontend/issues/issue_spec.js4
-rw-r--r--spec/frontend/sidebar/components/crm_contacts_spec.js11
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js4
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_show_root_spec.js7
-rw-r--r--spec/frontend/vue_shared/issuable/show/mock_data.js5
-rw-r--r--spec/helpers/badges_helper_spec.rb6
-rw-r--r--spec/helpers/issuables_helper_spec.rb35
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb25
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb16
-rw-r--r--spec/requests/projects/issue_links_controller_spec.rb11
-rw-r--r--spec/serializers/issue_board_entity_spec.rb16
-rw-r--r--spec/serializers/issue_entity_spec.rb13
-rw-r--r--spec/serializers/linked_project_issue_entity_spec.rb22
-rw-r--r--spec/services/members/groups/creator_service_spec.rb24
-rw-r--r--spec/services/members/projects/creator_service_spec.rb24
-rw-r--r--spec/support/helpers/trial_status_widget_test_helper.rb9
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb403
-rw-r--r--spec/views/projects/issues/show.html.haml_spec.rb16
21 files changed, 425 insertions, 306 deletions
diff --git a/spec/frontend/crm/contacts_root_spec.js b/spec/frontend/crm/contacts_root_spec.js
index b02d94e9cb1..3a6989a00f1 100644
--- a/spec/frontend/crm/contacts_root_spec.js
+++ b/spec/frontend/crm/contacts_root_spec.js
@@ -105,7 +105,7 @@ describe('Customer relations contacts root app', () => {
const issueLink = findIssuesLinks().at(0);
expect(issueLink.exists()).toBe(true);
- expect(issueLink.attributes('href')).toBe('/issues?scope=all&state=opened&crm_contact_id=16');
+ expect(issueLink.attributes('href')).toBe('/issues?crm_contact_id=16');
});
});
});
diff --git a/spec/frontend/crm/organizations_root_spec.js b/spec/frontend/crm/organizations_root_spec.js
index 231208d938e..1780a5945a6 100644
--- a/spec/frontend/crm/organizations_root_spec.js
+++ b/spec/frontend/crm/organizations_root_spec.js
@@ -102,9 +102,7 @@ describe('Customer relations organizations root app', () => {
const issueLink = findIssuesLinks().at(0);
expect(issueLink.exists()).toBe(true);
- expect(issueLink.attributes('href')).toBe(
- '/issues?scope=all&state=opened&crm_organization_id=2',
- );
+ expect(issueLink.attributes('href')).toBe('/issues?crm_organization_id=2');
});
});
});
diff --git a/spec/frontend/issuable/components/status_box_spec.js b/spec/frontend/issuable/components/status_box_spec.js
index d3e05939527..728b8958b9b 100644
--- a/spec/frontend/issuable/components/status_box_spec.js
+++ b/spec/frontend/issuable/components/status_box_spec.js
@@ -1,65 +1,53 @@
-import { GlSprintf } from '@gitlab/ui';
+import { GlBadge, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import StatusBox from '~/issuable/components/status_box.vue';
let wrapper;
function factory(propsData) {
- wrapper = shallowMount(StatusBox, {
- propsData,
- stubs: { GlSprintf },
- provide: { glFeatures: { updatedMrHeader: true } },
- });
+ wrapper = shallowMount(StatusBox, { propsData, stubs: { GlBadge } });
}
-const testCases = [
- {
- name: 'Open',
- state: 'opened',
- class: 'badge-success',
- },
- {
- name: 'Open',
- state: 'locked',
- class: 'badge-success',
- },
- {
- name: 'Closed',
- state: 'closed',
- class: 'badge-danger',
- },
- {
- name: 'Merged',
- state: 'merged',
- class: 'badge-info',
- },
-];
-
describe('Merge request status box component', () => {
+ const findBadge = () => wrapper.findComponent(GlBadge);
+
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
- testCases.forEach((testCase) => {
- describe(`when merge request is ${testCase.name}`, () => {
- it('renders human readable test', () => {
+ describe.each`
+ issuableType | badgeText | initialState | badgeClass | badgeVariant | badgeIcon
+ ${'merge_request'} | ${'Open'} | ${'opened'} | ${'issuable-status-badge-open'} | ${'success'} | ${'merge-request-open'}
+ ${'merge_request'} | ${'Closed'} | ${'closed'} | ${'issuable-status-badge-closed'} | ${'danger'} | ${'merge-request-close'}
+ ${'merge_request'} | ${'Merged'} | ${'merged'} | ${'issuable-status-badge-merged'} | ${'info'} | ${'merge'}
+ ${'issue'} | ${'Open'} | ${'opened'} | ${'issuable-status-badge-open'} | ${'success'} | ${'issues'}
+ ${'issue'} | ${'Closed'} | ${'closed'} | ${'issuable-status-badge-closed'} | ${'info'} | ${'issue-closed'}
+ `(
+ 'with issuableType set to "$issuableType" and state set to "$initialState"',
+ ({ issuableType, badgeText, initialState, badgeClass, badgeVariant, badgeIcon }) => {
+ beforeEach(() => {
factory({
- initialState: testCase.state,
- issuableType: 'merge_request',
+ initialState,
+ issuableType,
});
+ });
- expect(wrapper.text()).toContain(testCase.name);
+ it(`renders badge with text '${badgeText}'`, () => {
+ expect(findBadge().text()).toBe(badgeText);
});
- it('sets css class', () => {
- factory({
- initialState: testCase.state,
- issuableType: 'merge_request',
- });
+ it(`sets badge css class as '${badgeClass}'`, () => {
+ expect(findBadge().classes()).toContain(badgeClass);
+ });
- expect(wrapper.classes()).toContain(testCase.class);
+ it(`sets badge variant as '${badgeVariant}`, () => {
+ expect(findBadge().props('variant')).toBe(badgeVariant);
});
- });
- });
+
+ it(`sets badge icon as '${badgeIcon}'`, () => {
+ expect(findBadge().findComponent(GlIcon).props('name')).toBe(badgeIcon);
+ });
+ },
+ );
});
diff --git a/spec/frontend/issues/issue_spec.js b/spec/frontend/issues/issue_spec.js
index 8a089b372ff..b4f6118ec20 100644
--- a/spec/frontend/issues/issue_spec.js
+++ b/spec/frontend/issues/issue_spec.js
@@ -24,11 +24,11 @@ describe('Issue', () => {
const getIssueCounter = () => document.querySelector('.issue_counter');
const getOpenStatusBox = () =>
getByText(document, (_, el) => el.textContent.match(/Open/), {
- selector: '.status-box-open',
+ selector: '.issuable-status-badge-open',
});
const getClosedStatusBox = () =>
getByText(document, (_, el) => el.textContent.match(/Closed/), {
- selector: '.status-box-issue-closed',
+ selector: '.issuable-status-badge-closed',
});
describe.each`
diff --git a/spec/frontend/sidebar/components/crm_contacts_spec.js b/spec/frontend/sidebar/components/crm_contacts_spec.js
index 758cff30e2d..6456829258f 100644
--- a/spec/frontend/sidebar/components/crm_contacts_spec.js
+++ b/spec/frontend/sidebar/components/crm_contacts_spec.js
@@ -33,7 +33,7 @@ describe('Issue crm contacts component', () => {
[issueCrmContactsSubscription, subscriptionHandler],
]);
wrapper = shallowMountExtended(CrmContacts, {
- propsData: { issueId: '123' },
+ propsData: { issueId: '123', groupIssuesPath: '/groups/flightjs/-/issues' },
apolloProvider: fakeApollo,
});
};
@@ -71,8 +71,14 @@ describe('Issue crm contacts component', () => {
await waitForPromises();
expect(wrapper.find('#contact_0').text()).toContain('Someone Important');
+ expect(wrapper.find('#contact_0').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=1',
+ );
expect(wrapper.find('#contact_container_0').text()).toContain('si@gitlab.com');
expect(wrapper.find('#contact_1').text()).toContain('Marty McFly');
+ expect(wrapper.find('#contact_1').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=5',
+ );
});
it('renders correct results after subscription update', async () => {
@@ -83,5 +89,8 @@ describe('Issue crm contacts component', () => {
contact.forEach((property) => {
expect(wrapper.find('#contact_container_0').text()).toContain(property);
});
+ expect(wrapper.find('#contact_0').attributes('href')).toBe(
+ '/groups/flightjs/-/issues?crm_contact_id=13',
+ );
});
});
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
index 868b7af6da5..02579e8b3c4 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
@@ -69,9 +69,11 @@ describe('IssuableHeader', () => {
it('renders issuable status icon and text', () => {
createComponent();
const statusBoxEl = wrapper.findByTestId('status');
+ const statusIconEl = statusBoxEl.findComponent(GlIcon);
expect(statusBoxEl.exists()).toBe(true);
- expect(statusBoxEl.find(GlIcon).props('name')).toBe(mockIssuableShowProps.statusIcon);
+ expect(statusIconEl.props('name')).toBe(mockIssuableShowProps.statusIcon);
+ expect(statusIconEl.attributes('class')).toBe(mockIssuableShowProps.statusIconClass);
expect(statusBoxEl.text()).toContain('Open');
});
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_show_root_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_show_root_spec.js
index d1eb1366225..8b027f990a2 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_show_root_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_show_root_spec.js
@@ -49,6 +49,7 @@ describe('IssuableShowRoot', () => {
const {
statusBadgeClass,
statusIcon,
+ statusIconClass,
enableEdit,
enableAutocomplete,
editFormVisible,
@@ -56,7 +57,7 @@ describe('IssuableShowRoot', () => {
descriptionHelpPath,
taskCompletionStatus,
} = mockIssuableShowProps;
- const { blocked, confidential, createdAt, author } = mockIssuable;
+ const { state, blocked, confidential, createdAt, author } = mockIssuable;
it('renders component container element with class `issuable-show-container`', () => {
expect(wrapper.classes()).toContain('issuable-show-container');
@@ -67,15 +68,17 @@ describe('IssuableShowRoot', () => {
expect(issuableHeader.exists()).toBe(true);
expect(issuableHeader.props()).toMatchObject({
+ issuableState: state,
statusBadgeClass,
statusIcon,
+ statusIconClass,
blocked,
confidential,
createdAt,
author,
taskCompletionStatus,
});
- expect(issuableHeader.find('.issuable-status-box').text()).toContain('Open');
+ expect(issuableHeader.find('.issuable-status-badge').text()).toContain('Open');
expect(issuableHeader.find('.detail-page-header-actions button.js-close').exists()).toBe(
true,
);
diff --git a/spec/frontend/vue_shared/issuable/show/mock_data.js b/spec/frontend/vue_shared/issuable/show/mock_data.js
index f5f3ed58655..32bb9edfe08 100644
--- a/spec/frontend/vue_shared/issuable/show/mock_data.js
+++ b/spec/frontend/vue_shared/issuable/show/mock_data.js
@@ -36,8 +36,9 @@ export const mockIssuableShowProps = {
enableTaskList: true,
enableEdit: true,
showFieldTitle: false,
- statusBadgeClass: 'status-box-open',
- statusIcon: 'issue-open-m',
+ statusBadgeClass: 'issuable-status-badge-open',
+ statusIcon: 'issues',
+ statusIconClass: 'gl-sm-display-none',
taskCompletionStatus: {
completedCount: 0,
count: 5,
diff --git a/spec/helpers/badges_helper_spec.rb b/spec/helpers/badges_helper_spec.rb
index 5be3b4a737b..8e1f92305da 100644
--- a/spec/helpers/badges_helper_spec.rb
+++ b/spec/helpers/badges_helper_spec.rb
@@ -89,16 +89,16 @@ RSpec.describe BadgesHelper do
end
describe 'icons' do
- let(:spacing_class_regex) { %r{<svg .*class=".*gl-mr-2.*".*>.*</svg>} }
+ let(:spacing_class_regex) { %r{<svg .*class=".*my-icon-class gl-mr-2".*>.*</svg>} }
describe 'with text' do
- subject { helper.gl_badge_tag(label, icon: "question-o") }
+ subject { helper.gl_badge_tag(label, icon: "question-o", icon_classes: 'my-icon-class') }
it 'renders an icon' do
expect(subject).to match(%r{<svg .*#question-o".*>.*</svg>})
end
- it 'adds a spacing class to the icon' do
+ it 'adds a spacing class and any custom classes to the icon' do
expect(subject).to match(spacing_class_regex)
end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index ee5b0145d13..2244aaee2dc 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -464,6 +464,41 @@ RSpec.describe IssuablesHelper do
end
end
+ describe '#state_name_with_icon' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ context 'for an issue' do
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:issue_closed) { create(:issue, :closed, project: project) }
+
+ it 'returns the correct state name and icon when issue is open' do
+ expect(helper.state_name_with_icon(issue)).to match_array([_('Open'), 'issues'])
+ end
+
+ it 'returns the correct state name and icon when issue is closed' do
+ expect(helper.state_name_with_icon(issue_closed)).to match_array([_('Closed'), 'issue-closed'])
+ end
+ end
+
+ context 'for a merge request' do
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:merge_request_merged) { create(:merge_request, :merged, source_project: project) }
+ let_it_be(:merge_request_closed) { create(:merge_request, :closed, source_project: project) }
+
+ it 'returns the correct state name and icon when merge request is open' do
+ expect(helper.state_name_with_icon(merge_request)).to match_array([_('Open'), 'merge-request-open'])
+ end
+
+ it 'returns the correct state name and icon when merge request is merged' do
+ expect(helper.state_name_with_icon(merge_request_merged)).to match_array([_('Merged'), 'merge'])
+ end
+
+ it 'returns the correct state name and icon when merge request is closed' do
+ expect(helper.state_name_with_icon(merge_request_closed)).to match_array([_('Closed'), 'merge-request-close'])
+ end
+ end
+ end
+
describe '#issuable_display_type' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index f7762069b68..97ad55d9df9 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -5,31 +5,6 @@ require 'spec_helper'
RSpec.describe MergeRequestsHelper do
include ProjectForksHelper
- describe '#state_name_with_icon' do
- using RSpec::Parameterized::TableSyntax
-
- let(:merge_request) { MergeRequest.new }
-
- where(:state, :expected_name, :expected_icon) do
- :merged? | 'Merged' | 'git-merge'
- :closed? | 'Closed' | 'close'
- :opened? | 'Open' | 'issue-open-m'
- end
-
- with_them do
- before do
- allow(merge_request).to receive(state).and_return(true)
- end
-
- it 'returns name and icon' do
- name, icon = helper.state_name_with_icon(merge_request)
-
- expect(name).to eq(expected_name)
- expect(icon).to eq(expected_icon)
- end
- end
- end
-
describe '#format_mr_branch_names' do
describe 'within the same project' do
let(:merge_request) { create(:merge_request) }
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 8e372ba795b..d4f96f1a37f 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -22,6 +22,8 @@ RSpec.describe Gitlab::UrlBuilder do
:group_board | ->(board) { "/groups/#{board.group.full_path}/-/boards/#{board.id}" }
:commit | ->(commit) { "/#{commit.project.full_path}/-/commit/#{commit.id}" }
:issue | ->(issue) { "/#{issue.project.full_path}/-/issues/#{issue.iid}" }
+ [:issue, :task] | ->(issue) { "/#{issue.project.full_path}/-/work_items/#{issue.id}" }
+ :work_item | ->(work_item) { "/#{work_item.project.full_path}/-/work_items/#{work_item.id}" }
:merge_request | ->(merge_request) { "/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}" }
:project_milestone | ->(milestone) { "/#{milestone.project.full_path}/-/milestones/#{milestone.iid}" }
:project_snippet | ->(snippet) { "/#{snippet.project.full_path}/-/snippets/#{snippet.id}" }
@@ -57,7 +59,7 @@ RSpec.describe Gitlab::UrlBuilder do
end
with_them do
- let(:object) { build_stubbed(factory) }
+ let(:object) { build_stubbed(*Array(factory)) }
let(:path) { path_generator.call(object) }
it 'returns the full URL' do
@@ -69,6 +71,18 @@ RSpec.describe Gitlab::UrlBuilder do
end
end
+ context 'when work_items feature flag is disabled' do
+ before do
+ stub_feature_flags(work_items: false)
+ end
+
+ it 'returns an issue path for an issue of type task' do
+ task = create(:issue, :task)
+
+ expect(subject.build(task, only_path: true)).to eq("/#{task.project.full_path}/-/issues/#{task.iid}")
+ end
+ end
+
context 'when passing a compare' do
# NOTE: The Compare requires an actual repository, which isn't available
# with the `build_stubbed` strategy used by the table tests above
diff --git a/spec/requests/projects/issue_links_controller_spec.rb b/spec/requests/projects/issue_links_controller_spec.rb
index d22955718f8..3447ff83ed8 100644
--- a/spec/requests/projects/issue_links_controller_spec.rb
+++ b/spec/requests/projects/issue_links_controller_spec.rb
@@ -24,6 +24,17 @@ RSpec.describe Projects::IssueLinksController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(list_service_response.as_json)
end
+
+ context 'when linked issue is a task' do
+ let(:issue_b) { create :issue, :task, project: project }
+
+ it 'returns a work item path for the linked task' do
+ get namespace_project_issue_links_path(issue_links_params)
+
+ expect(json_response.count).to eq(1)
+ expect(json_response.first).to include('path' => project_work_items_path(issue_b.project, issue_b.id))
+ end
+ end
end
describe 'POST /*namespace_id/:project_id/issues/:issue_id/links' do
diff --git a/spec/serializers/issue_board_entity_spec.rb b/spec/serializers/issue_board_entity_spec.rb
index 30423ceba6d..b8e2bfeaa3d 100644
--- a/spec/serializers/issue_board_entity_spec.rb
+++ b/spec/serializers/issue_board_entity_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe IssueBoardEntity do
+ include Gitlab::Routing.url_helpers
+
let_it_be(:project) { create(:project) }
let_it_be(:resource) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
@@ -40,4 +42,18 @@ RSpec.describe IssueBoardEntity do
expect(subject).to include(labels: array_including(hash_including(:id, :title, :color, :description, :text_color, :priority)))
end
+
+ describe 'real_path' do
+ it 'has an issue path' do
+ expect(subject[:real_path]).to eq(project_issue_path(project, resource.iid))
+ end
+
+ context 'when issue is of type task' do
+ let(:resource) { create(:issue, :task, project: project) }
+
+ it 'has a work item path' do
+ expect(subject[:real_path]).to eq(project_work_items_path(project, resource.id))
+ end
+ end
+ end
end
diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb
index 76f8cf644c6..6ccb3dbc657 100644
--- a/spec/serializers/issue_entity_spec.rb
+++ b/spec/serializers/issue_entity_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe IssueEntity do
+ include Gitlab::Routing.url_helpers
+
let(:project) { create(:project) }
let(:resource) { create(:issue, project: project) }
let(:user) { create(:user) }
@@ -11,6 +13,17 @@ RSpec.describe IssueEntity do
subject { described_class.new(resource, request: request).as_json }
+ describe 'web_url' do
+ context 'when issue is of type task' do
+ let(:resource) { create(:issue, :task, project: project) }
+
+ # This was already a path and not a url when the work items change was introduced
+ it 'has a work item path' do
+ expect(subject[:web_url]).to eq(project_work_items_path(project, resource.id))
+ end
+ end
+ end
+
it 'has Issuable attributes' do
expect(subject).to include(:id, :iid, :author_id, :description, :lock_version, :milestone_id,
:title, :updated_by_id, :created_at, :updated_at, :milestone, :labels)
diff --git a/spec/serializers/linked_project_issue_entity_spec.rb b/spec/serializers/linked_project_issue_entity_spec.rb
index 864b5c45599..b28b00bd8e1 100644
--- a/spec/serializers/linked_project_issue_entity_spec.rb
+++ b/spec/serializers/linked_project_issue_entity_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe LinkedProjectIssueEntity do
+ include Gitlab::Routing.url_helpers
+
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:issue_link) { create(:issue_link) }
@@ -17,7 +19,25 @@ RSpec.describe LinkedProjectIssueEntity do
issue_link.target.project.add_developer(user)
end
+ subject(:serialized_entity) { entity.as_json }
+
describe 'issue_link_type' do
- it { expect(entity.as_json).to include(link_type: 'relates_to') }
+ it { is_expected.to include(link_type: 'relates_to') }
+ end
+
+ describe 'path' do
+ it 'returns an issue path' do
+ expect(serialized_entity).to include(path: project_issue_path(related_issue.project, related_issue.iid))
+ end
+
+ context 'when related issue is a task' do
+ before do
+ related_issue.update!(issue_type: :task, work_item_type: WorkItems::Type.default_by_type(:task))
+ end
+
+ it 'returns a work items path' do
+ expect(serialized_entity).to include(path: project_work_items_path(related_issue.project, related_issue.id))
+ end
+ end
end
end
diff --git a/spec/services/members/groups/creator_service_spec.rb b/spec/services/members/groups/creator_service_spec.rb
index 4427c4e7d9f..c3ba7c0374d 100644
--- a/spec/services/members/groups/creator_service_spec.rb
+++ b/spec/services/members/groups/creator_service_spec.rb
@@ -3,14 +3,28 @@
require 'spec_helper'
RSpec.describe Members::Groups::CreatorService do
- it_behaves_like 'member creation' do
- let_it_be(:source, reload: true) { create(:group, :public) }
- let_it_be(:member_type) { GroupMember }
- end
-
describe '.access_levels' do
it 'returns Gitlab::Access.options_with_owner' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
end
end
+
+ describe '#execute' do
+ let_it_be(:source, reload: true) { create(:group, :public) }
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'member creation' do
+ let_it_be(:member_type) { GroupMember }
+ end
+
+ context 'authorized projects update' do
+ it 'schedules a single project authorization update job when called multiple times' do
+ expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait).once
+
+ 1.upto(3) do
+ described_class.new(source, user, :maintainer).execute
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/members/projects/creator_service_spec.rb b/spec/services/members/projects/creator_service_spec.rb
index 7ba183759bc..7605238c3c5 100644
--- a/spec/services/members/projects/creator_service_spec.rb
+++ b/spec/services/members/projects/creator_service_spec.rb
@@ -3,14 +3,28 @@
require 'spec_helper'
RSpec.describe Members::Projects::CreatorService do
- it_behaves_like 'member creation' do
- let_it_be(:source, reload: true) { create(:project, :public) }
- let_it_be(:member_type) { ProjectMember }
- end
-
describe '.access_levels' do
it 'returns Gitlab::Access.sym_options_with_owner' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
end
end
+
+ describe '#execute' do
+ let_it_be(:source, reload: true) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'member creation' do
+ let_it_be(:member_type) { ProjectMember }
+ end
+
+ context 'authorized projects update' do
+ it 'schedules a single project authorization update job when called multiple times' do
+ expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to receive(:bulk_perform_in).once
+
+ 1.upto(3) do
+ described_class.new(source, user, :maintainer).execute
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/helpers/trial_status_widget_test_helper.rb b/spec/support/helpers/trial_status_widget_test_helper.rb
new file mode 100644
index 00000000000..d75620d17ee
--- /dev/null
+++ b/spec/support/helpers/trial_status_widget_test_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module TrialStatusWidgetTestHelper
+ def purchase_href(group)
+ new_subscriptions_path(namespace_id: group.id, plan_id: 'ultimate-plan-id')
+ end
+end
+
+TrialStatusWidgetTestHelper.prepend_mod
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index a329a6dca91..e293d10964b 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -77,312 +77,309 @@ RSpec.shared_examples '#valid_level_roles' do |entity_name|
end
RSpec.shared_examples_for "member creation" do
- let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
- describe '#execute' do
- it 'returns a Member object', :aggregate_failures do
- member = described_class.new(source, user, :maintainer).execute
-
- expect(member).to be_a member_type
- expect(member).to be_persisted
- end
+ it 'returns a Member object', :aggregate_failures do
+ member = described_class.new(source, user, :maintainer).execute
- context 'when adding a project_bot' do
- let_it_be(:project_bot) { create(:user, :project_bot) }
-
- before_all do
- source.add_owner(user)
- end
+ expect(member).to be_a member_type
+ expect(member).to be_persisted
+ end
- context 'when project_bot is already a member' do
- before do
- source.add_developer(project_bot)
- end
+ context 'when adding a project_bot' do
+ let_it_be(:project_bot) { create(:user, :project_bot) }
- it 'does not update the member' do
- member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
+ before_all do
+ source.add_owner(user)
+ end
- expect(source.users.reload).to include(project_bot)
- expect(member).to be_persisted
- expect(member.access_level).to eq(Gitlab::Access::DEVELOPER)
- expect(member.errors.full_messages).to include(/not authorized to update member/)
- end
+ context 'when project_bot is already a member' do
+ before do
+ source.add_developer(project_bot)
end
- context 'when project_bot is not already a member' do
- it 'adds the member' do
- member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
+ it 'does not update the member' do
+ member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
- expect(source.users.reload).to include(project_bot)
- expect(member).to be_persisted
- end
+ expect(source.users.reload).to include(project_bot)
+ expect(member).to be_persisted
+ expect(member.access_level).to eq(Gitlab::Access::DEVELOPER)
+ expect(member.errors.full_messages).to include(/not authorized to update member/)
end
end
- context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do
- it 'sets members.created_by to the given admin current_user' do
- member = described_class.new(source, user, :maintainer, current_user: admin).execute
+ context 'when project_bot is not already a member' do
+ it 'adds the member' do
+ member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
+ expect(source.users.reload).to include(project_bot)
expect(member).to be_persisted
- expect(source.users.reload).to include(user)
- expect(member.created_by).to eq(admin)
end
end
+ end
- context 'when admin mode is disabled' do
- it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do
- member = described_class.new(source, user, :maintainer, current_user: admin).execute
+ context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do
+ it 'sets members.created_by to the given admin current_user' do
+ member = described_class.new(source, user, :maintainer, current_user: admin).execute
- expect(member).not_to be_persisted
- expect(source.users.reload).not_to include(user)
- expect(member.errors.full_messages).to include(/not authorized to create member/)
- end
+ expect(member).to be_persisted
+ expect(source.users.reload).to include(user)
+ expect(member.created_by).to eq(admin)
end
+ end
- it 'sets members.expires_at to the given expires_at' do
- member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute
+ context 'when admin mode is disabled' do
+ it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do
+ member = described_class.new(source, user, :maintainer, current_user: admin).execute
- expect(member.expires_at).to eq(Date.new(2016, 9, 22))
+ expect(member).not_to be_persisted
+ expect(source.users.reload).not_to include(user)
+ expect(member.errors.full_messages).to include(/not authorized to create member/)
end
+ end
- described_class.access_levels.each do |sym_key, int_access_level|
- it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do
- expect(source.users).not_to include(user)
+ it 'sets members.expires_at to the given expires_at' do
+ member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute
- member = described_class.new(source, user.id, sym_key).execute
+ expect(member.expires_at).to eq(Date.new(2016, 9, 22))
+ end
- expect(member.access_level).to eq(int_access_level)
- expect(source.users.reload).to include(user)
- end
+ described_class.access_levels.each do |sym_key, int_access_level|
+ it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do
+ expect(source.users).not_to include(user)
+
+ member = described_class.new(source, user.id, sym_key).execute
- it "accepts the #{int_access_level} integer as access level", :aggregate_failures do
+ expect(member.access_level).to eq(int_access_level)
+ expect(source.users.reload).to include(user)
+ end
+
+ it "accepts the #{int_access_level} integer as access level", :aggregate_failures do
+ expect(source.users).not_to include(user)
+
+ member = described_class.new(source, user.id, int_access_level).execute
+
+ expect(member.access_level).to eq(int_access_level)
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'with no current_user' do
+ context 'when called with a known user id' do
+ it 'adds the user as a member' do
expect(source.users).not_to include(user)
- member = described_class.new(source, user.id, int_access_level).execute
+ described_class.new(source, user.id, :maintainer).execute
- expect(member.access_level).to eq(int_access_level)
expect(source.users.reload).to include(user)
end
end
- context 'with no current_user' do
- context 'when called with a known user id' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
+ context 'when called with an unknown user id' do
+ it 'does not add the user as a member' do
+ expect(source.users).not_to include(user)
- described_class.new(source, user.id, :maintainer).execute
+ described_class.new(source, non_existing_record_id, :maintainer).execute
- expect(source.users.reload).to include(user)
- end
+ expect(source.users.reload).not_to include(user)
end
+ end
- context 'when called with an unknown user id' do
- it 'does not add the user as a member' do
- expect(source.users).not_to include(user)
+ context 'when called with a user object' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
- described_class.new(source, non_existing_record_id, :maintainer).execute
+ described_class.new(source, user, :maintainer).execute
- expect(source.users.reload).not_to include(user)
- end
+ expect(source.users.reload).to include(user)
+ end
+ end
+
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
end
- context 'when called with a user object' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
+ it 'adds the requester as a member', :aggregate_failures do
+ expect(source.users).not_to include(user)
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
+ expect do
described_class.new(source, user, :maintainer).execute
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
- expect(source.users.reload).to include(user)
- end
+ expect(source.users.reload).not_to include(user)
+ expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
end
+ end
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
-
- it 'adds the requester as a member', :aggregate_failures do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
+ context 'when called with a known user email' do
+ it 'adds the user as a member' do
+ expect(source.users).not_to include(user)
- expect do
- described_class.new(source, user, :maintainer).execute
- end.to raise_error(Gitlab::Access::AccessDeniedError)
+ described_class.new(source, user.email, :maintainer).execute
- expect(source.users.reload).not_to include(user)
- expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
- end
+ expect(source.users.reload).to include(user)
end
+ end
- context 'when called with a known user email' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
+ context 'when called with an unknown user email' do
+ it 'creates an invited member' do
+ expect(source.users).not_to include(user)
- described_class.new(source, user.email, :maintainer).execute
+ described_class.new(source, 'user@example.com', :maintainer).execute
- expect(source.users.reload).to include(user)
- end
+ expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
end
+ end
- context 'when called with an unknown user email' do
- it 'creates an invited member' do
- expect(source.users).not_to include(user)
+ context 'when called with an unknown user email starting with a number' do
+ it 'creates an invited member', :aggregate_failures do
+ email_starting_with_number = "#{user.id}_email@example.com"
- described_class.new(source, 'user@example.com', :maintainer).execute
+ described_class.new(source, email_starting_with_number, :maintainer).execute
- expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
- end
+ expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
+ expect(source.users.reload).not_to include(user)
end
+ end
+ end
- context 'when called with an unknown user email starting with a number' do
- it 'creates an invited member', :aggregate_failures do
- email_starting_with_number = "#{user.id}_email@example.com"
+ context 'when current_user can update member', :enable_admin_mode do
+ it 'creates the member' do
+ expect(source.users).not_to include(user)
- described_class.new(source, email_starting_with_number, :maintainer).execute
+ described_class.new(source, user, :maintainer, current_user: admin).execute
- expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
- expect(source.users.reload).not_to include(user)
- end
- end
+ expect(source.users.reload).to include(user)
end
- context 'when current_user can update member', :enable_admin_mode do
- it 'creates the member' do
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
+ end
+
+ it 'adds the requester as a member', :aggregate_failures do
expect(source.users).not_to include(user)
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
described_class.new(source, user, :maintainer, current_user: admin).execute
expect(source.users.reload).to include(user)
+ expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
end
+ end
+ end
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
+ context 'when current_user cannot update member' do
+ it 'does not create the member', :aggregate_failures do
+ expect(source.users).not_to include(user)
- it 'adds the requester as a member', :aggregate_failures do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
+ member = described_class.new(source, user, :maintainer, current_user: user).execute
- described_class.new(source, user, :maintainer, current_user: admin).execute
+ expect(source.users.reload).not_to include(user)
+ expect(member).not_to be_persisted
+ end
- expect(source.users.reload).to include(user)
- expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
- end
+ context 'when called with a requester user object' do
+ before do
+ source.request_access(user)
end
- end
- context 'when current_user cannot update member' do
- it 'does not create the member', :aggregate_failures do
+ it 'does not destroy the requester', :aggregate_failures do
expect(source.users).not_to include(user)
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
- member = described_class.new(source, user, :maintainer, current_user: user).execute
+ described_class.new(source, user, :maintainer, current_user: user).execute
expect(source.users.reload).not_to include(user)
- expect(member).not_to be_persisted
+ expect(source.requesters.exists?(user_id: user)).to be_truthy
end
+ end
+ end
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
+ context 'when member already exists' do
+ before do
+ source.add_user(user, :developer)
+ end
- it 'does not destroy the requester', :aggregate_failures do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
+ context 'with no current_user' do
+ it 'updates the member' do
+ expect(source.users).to include(user)
- described_class.new(source, user, :maintainer, current_user: user).execute
+ described_class.new(source, user, :maintainer).execute
- expect(source.users.reload).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
- end
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
- context 'when member already exists' do
- before do
- source.add_user(user, :developer)
- end
-
- context 'with no current_user' do
- it 'updates the member' do
- expect(source.users).to include(user)
+ context 'when current_user can update member', :enable_admin_mode do
+ it 'updates the member' do
+ expect(source.users).to include(user)
- described_class.new(source, user, :maintainer).execute
+ described_class.new(source, user, :maintainer, current_user: admin).execute
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
- end
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
+ end
- context 'when current_user can update member', :enable_admin_mode do
- it 'updates the member' do
- expect(source.users).to include(user)
+ context 'when current_user cannot update member' do
+ it 'does not update the member' do
+ expect(source.users).to include(user)
- described_class.new(source, user, :maintainer, current_user: admin).execute
+ described_class.new(source, user, :maintainer, current_user: user).execute
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
- end
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
end
+ end
+ end
- context 'when current_user cannot update member' do
- it 'does not update the member' do
- expect(source.users).to include(user)
+ context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do
+ let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source }
- described_class.new(source, user, :maintainer, current_user: user).execute
+ it 'creates a member_task with the correct attributes', :aggregate_failures do
+ described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
- end
- end
- end
+ member = source.members.last
- context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do
- let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source }
+ expect(member.tasks_to_be_done).to match_array([:ci, :code])
+ expect(member.member_task.project).to eq(task_project)
+ end
- it 'creates a member_task with the correct attributes', :aggregate_failures do
- described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute
+ context 'with an already existing member' do
+ before do
+ source.add_user(user, :developer)
+ end
- member = source.members.last
+ it 'does not update tasks to be done if tasks already exist', :aggregate_failures do
+ member = source.members.find_by(user_id: user.id)
+ create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci))
- expect(member.tasks_to_be_done).to match_array([:ci, :code])
+ expect do
+ described_class.new(source,
+ user,
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id).execute
+ end.not_to change(MemberTask, :count)
+
+ member.reset
+ expect(member.tasks_to_be_done).to match_array([:code, :ci])
expect(member.member_task.project).to eq(task_project)
end
- context 'with an already existing member' do
- before do
- source.add_user(user, :developer)
- end
-
- it 'does not update tasks to be done if tasks already exist', :aggregate_failures do
- member = source.members.find_by(user_id: user.id)
- create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci))
-
- expect do
- described_class.new(source,
- user,
- :developer,
- tasks_to_be_done: %w(issues),
- tasks_project_id: task_project.id).execute
- end.not_to change(MemberTask, :count)
-
- member.reset
- expect(member.tasks_to_be_done).to match_array([:code, :ci])
- expect(member.member_task.project).to eq(task_project)
- end
-
- it 'adds tasks to be done if they do not exist', :aggregate_failures do
- expect do
- described_class.new(source,
- user,
- :developer,
- tasks_to_be_done: %w(issues),
- tasks_project_id: task_project.id).execute
- end.to change(MemberTask, :count).by(1)
-
- member = source.members.find_by(user_id: user.id)
- expect(member.tasks_to_be_done).to match_array([:issues])
- expect(member.member_task.project).to eq(task_project)
- end
+ it 'adds tasks to be done if they do not exist', :aggregate_failures do
+ expect do
+ described_class.new(source,
+ user,
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id).execute
+ end.to change(MemberTask, :count).by(1)
+
+ member = source.members.find_by(user_id: user.id)
+ expect(member.tasks_to_be_done).to match_array([:issues])
+ expect(member.member_task.project).to eq(task_project)
end
end
end
diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb
index b2d208f038a..3f1496a24ce 100644
--- a/spec/views/projects/issues/show.html.haml_spec.rb
+++ b/spec/views/projects/issues/show.html.haml_spec.rb
@@ -26,14 +26,14 @@ RSpec.describe 'projects/issues/show' do
it 'shows "Closed (moved)" if an issue has been moved and closed' do
render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
end
it 'shows "Closed (moved)" if an issue has been moved and discussion is locked' do
allow(issue).to receive(:discussion_locked).and_return(true)
render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
end
it 'links "moved" to the new issue the original issue was moved to' do
@@ -47,7 +47,7 @@ RSpec.describe 'projects/issues/show' do
render
- expect(rendered).not_to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ expect(rendered).not_to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
end
end
@@ -75,7 +75,7 @@ RSpec.describe 'projects/issues/show' do
it 'shows "Closed (duplicated)" if an issue has been duplicated' do
render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (duplicated)')
+ expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (duplicated)')
end
it 'links "duplicated" to the new issue the original issue was duplicated to' do
@@ -97,14 +97,14 @@ RSpec.describe 'projects/issues/show' do
it 'shows "Closed" if an issue has not been moved or duplicated' do
render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed')
+ expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed')
end
it 'shows "Closed" if discussion is locked' do
allow(issue).to receive(:discussion_locked).and_return(true)
render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed')
+ expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed')
end
end
@@ -117,14 +117,14 @@ RSpec.describe 'projects/issues/show' do
it 'shows "Open" if an issue has been moved' do
render
- expect(rendered).to have_selector('.status-box-open:not(.hidden)', text: 'Open')
+ expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open')
end
it 'shows "Open" if discussion is locked' do
allow(issue).to receive(:discussion_locked).and_return(true)
render
- expect(rendered).to have_selector('.status-box-open:not(.hidden)', text: 'Open')
+ expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open')
end
end