summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue7
-rw-r--r--app/assets/javascripts/notes/components/sort_discussion.vue3
-rw-r--r--app/controllers/projects/import/jira_controller.rb8
-rw-r--r--app/models/user.rb8
-rw-r--r--app/serializers/note_user_entity.rb2
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/projects/import/jira/show.html.haml2
-rw-r--r--changelogs/unreleased/199081-remove-open-in-file-view-in-web-ide.yml5
-rw-r--r--changelogs/unreleased/25426_subgroups_runners_in_api.yml6
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/projects/import/jira_controller_spec.rb22
-rw-r--r--spec/frontend/notes/components/sort_discussion_spec.js8
-rw-r--r--spec/models/user_spec.rb265
-rw-r--r--spec/requests/api/runners_spec.rb68
-rw-r--r--spec/views/profiles/show.html.haml_spec.rb44
15 files changed, 373 insertions, 83 deletions
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index c93a95e490a..08850679152 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -11,7 +11,6 @@ import {
FILE_VIEW_MODE_PREVIEW,
} from '../constants';
import Editor from '../lib/editor';
-import ExternalLink from './external_link.vue';
import FileTemplatesBar from './file_templates/bar.vue';
import { __ } from '~/locale';
@@ -19,7 +18,6 @@ export default {
components: {
ContentViewer,
DiffViewer,
- ExternalLink,
FileTemplatesBar,
},
props: {
@@ -275,8 +273,8 @@ export default {
<template>
<div id="ide" class="blob-viewer-container blob-editor-container">
- <div class="ide-mode-tabs clearfix">
- <ul v-if="!shouldHideEditor && isEditModeActive" class="nav-links float-left border-bottom-0">
+ <div v-if="!shouldHideEditor && isEditModeActive" class="ide-mode-tabs clearfix">
+ <ul class="nav-links float-left border-bottom-0">
<li :class="editTabCSS">
<a
href="javascript:void(0);"
@@ -296,7 +294,6 @@ export default {
>
</li>
</ul>
- <external-link :file="file" />
</div>
<file-templates-bar v-if="showFileTemplatesBar(file.name)" />
<div
diff --git a/app/assets/javascripts/notes/components/sort_discussion.vue b/app/assets/javascripts/notes/components/sort_discussion.vue
index 16eded52763..3f82ddde3ef 100644
--- a/app/assets/javascripts/notes/components/sort_discussion.vue
+++ b/app/assets/javascripts/notes/components/sort_discussion.vue
@@ -2,6 +2,7 @@
import { GlIcon } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale';
+import Tracking from '~/tracking';
import { ASC, DESC } from '../constants';
const SORT_OPTIONS = [
@@ -14,6 +15,7 @@ export default {
components: {
GlIcon,
},
+ mixins: [Tracking.mixin()],
computed: {
...mapGetters(['sortDirection']),
selectedOption() {
@@ -31,6 +33,7 @@ export default {
}
this.setDiscussionSortDirection(direction);
+ this.track('change_discussion_sort_direction', { property: direction });
},
isDropdownItemActive(sortDir) {
return sortDir === this.sortDirection;
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index e88d28e9db4..03ae17e18be 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -9,7 +9,13 @@ module Projects
def show
unless @project.import_state&.in_progress?
jira_client = @project.jira_service.client
- @jira_projects = jira_client.Project.all.map { |p| ["#{p.name} (#{p.key})", p.key] }
+ jira_projects = jira_client.Project.all
+
+ if jira_projects.present?
+ @jira_projects = jira_projects.map { |p| ["#{p.name} (#{p.key})", p.key] }
+ else
+ flash[:alert] = 'No projects have been returned from Jira. Please check your Jira configuration.'
+ end
end
flash[:notice] = _("Import %{status}") % { status: @project.import_state.status } if @project.import_state.present? && !@project.import_state.none?
diff --git a/app/models/user.rb b/app/models/user.rb
index 343edfae799..62624f8eec1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1405,7 +1405,7 @@ class User < ApplicationRecord
.select('ci_runners.*')
group_runners = Ci::RunnerNamespace
- .where(namespace_id: owned_groups.select(:id))
+ .where(namespace_id: Gitlab::ObjectHierarchy.new(owned_groups).base_and_descendants.select(:id))
.joins(:runner)
.select('ci_runners.*')
@@ -1696,7 +1696,7 @@ class User < ApplicationRecord
def gitlab_employee?
strong_memoize(:gitlab_employee) do
- if Gitlab.com?
+ if Feature.enabled?(:gitlab_employee_badge) && Gitlab.com?
Mail::Address.new(email).domain == "gitlab.com" && confirmed?
else
false
@@ -1713,6 +1713,10 @@ class User < ApplicationRecord
!confirmed? && !confirmation_period_valid?
end
+ def organization
+ gitlab_employee? ? 'GitLab' : super
+ end
+
protected
# override, from Devise::Validatable
diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb
index ffaf7664dae..8d30bbff5e4 100644
--- a/app/serializers/note_user_entity.rb
+++ b/app/serializers/note_user_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class NoteUserEntity < UserEntity
- expose :gitlab_employee?, as: :is_gitlab_employee, if: ->(user, options) { ::Feature.enabled?(:gitlab_employee_badge) && user.gitlab_employee? }
+ expose :gitlab_employee?, as: :is_gitlab_employee, if: ->(user, options) { user.gitlab_employee? }
unexpose :web_url
end
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 86e157ee042..da2b8c40191 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -101,7 +101,7 @@
- else
= f.text_field :location, label: s_('Profiles|Location'), class: 'input-lg', placeholder: s_("Profiles|City, country")
= f.text_field :job_title, class: 'input-md'
- = f.text_field :organization, label: s_('Profiles|Organization'), class: 'input-md', help: s_("Profiles|Who you represent or work for")
+ = f.text_field :organization, readonly: @user.gitlab_employee?, label: s_('Profiles|Organization'), class: 'input-md', help: s_("Profiles|Who you represent or work for")
= f.text_area :bio, label: s_('Profiles|Bio'), rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters")
%hr
%h5= s_("Private profile")
diff --git a/app/views/projects/import/jira/show.html.haml b/app/views/projects/import/jira/show.html.haml
index f295a241113..c734297d7f3 100644
--- a/app/views/projects/import/jira/show.html.haml
+++ b/app/views/projects/import/jira/show.html.haml
@@ -9,7 +9,7 @@
%h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import in progress')
-- else
+- elsif @jira_projects.present?
%h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import issues from Jira')
diff --git a/changelogs/unreleased/199081-remove-open-in-file-view-in-web-ide.yml b/changelogs/unreleased/199081-remove-open-in-file-view-in-web-ide.yml
new file mode 100644
index 00000000000..610833e69ba
--- /dev/null
+++ b/changelogs/unreleased/199081-remove-open-in-file-view-in-web-ide.yml
@@ -0,0 +1,5 @@
+---
+title: Remove open in file view link from Web IDE
+merge_request: 28705
+author:
+type: removed
diff --git a/changelogs/unreleased/25426_subgroups_runners_in_api.yml b/changelogs/unreleased/25426_subgroups_runners_in_api.yml
new file mode 100644
index 00000000000..cd04c739678
--- /dev/null
+++ b/changelogs/unreleased/25426_subgroups_runners_in_api.yml
@@ -0,0 +1,6 @@
+---
+title: 'Fix for issue 26426: Details of runners of nested groups of an owned group
+ are now available for users with enough permissions'
+merge_request: 24169
+author: nachootal@gmail.com
+type: changed
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a56d4cc2e6e..c385e1e1a91 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6567,6 +6567,9 @@ msgstr ""
msgid "Dependencies|All"
msgstr ""
+msgid "Dependencies|Based on the %{linkStart}latest successful%{linkEnd} scan"
+msgstr ""
+
msgid "Dependencies|Component"
msgstr ""
@@ -7124,9 +7127,6 @@ msgstr ""
msgid "Display source"
msgstr ""
-msgid "Displays dependencies and known vulnerabilities, based on the %{linkStart}latest successful%{linkEnd} scan"
-msgstr ""
-
msgid "Do not display offers from third parties within GitLab"
msgstr ""
diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb
index 3a29c3d4093..33b51320391 100644
--- a/spec/controllers/projects/import/jira_controller_spec.rb
+++ b/spec/controllers/projects/import/jira_controller_spec.rb
@@ -66,13 +66,29 @@ describe Projects::Import::JiraController do
context 'when running jira import first time' do
context 'get show' do
- it 'renders show template' do
- allow(JIRA::Resource::Project).to receive(:all).and_return([])
+ before do
+ allow(JIRA::Resource::Project).to receive(:all).and_return(jira_projects)
+
expect(project.import_state).to be_nil
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
+ end
+
+ context 'when no projects have been retrieved from Jira' do
+ let(:jira_projects) { [] }
+
+ it 'render an error message' do
+ expect(flash[:alert]).to eq('No projects have been returned from Jira. Please check your Jira configuration.')
+ expect(response).to render_template(:show)
+ end
+ end
+
+ context 'when everything is ok' do
+ let(:jira_projects) { [double(name: 'FOO project', key: 'FOO')] }
- expect(response).to render_template :show
+ it 'renders show template' do
+ expect(response).to render_template(:show)
+ end
end
end
diff --git a/spec/frontend/notes/components/sort_discussion_spec.js b/spec/frontend/notes/components/sort_discussion_spec.js
index 785e8c75233..724c77eee3d 100644
--- a/spec/frontend/notes/components/sort_discussion_spec.js
+++ b/spec/frontend/notes/components/sort_discussion_spec.js
@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import SortDiscussion from '~/notes/components/sort_discussion.vue';
import createStore from '~/notes/stores';
import { ASC, DESC } from '~/notes/constants';
+import Tracking from '~/tracking';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -22,6 +23,7 @@ describe('Sort Discussion component', () => {
beforeEach(() => {
store = createStore();
+ jest.spyOn(Tracking, 'event');
});
afterEach(() => {
@@ -37,6 +39,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-newest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC);
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
+ property: DESC,
+ });
});
});
@@ -58,6 +63,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-oldest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC);
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
+ property: ASC,
+ });
});
it('applies the active class to the correct button in the dropdown', () => {
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 12f83e6d8c6..e69d4e18c1e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2835,61 +2835,88 @@ describe User, :do_not_mock_admin_mode do
describe '#ci_owned_runners' do
let(:user) { create(:user) }
- let!(:project) { create(:project) }
- let(:runner) { create(:ci_runner, :project, projects: [project]) }
- context 'without any projects nor groups' do
- it 'does not load' do
- expect(user.ci_owned_runners).to be_empty
+ shared_examples :nested_groups_owner do
+ context 'when the user is the owner of a multi-level group' do
+ before do
+ set_permissions_for_users
+ end
+
+ it 'loads all the runners in the tree of groups' do
+ expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
+ end
end
end
- context 'with personal projects runners' do
- let(:namespace) { create(:namespace, owner: user) }
- let!(:project) { create(:project, namespace: namespace) }
+ shared_examples :group_owner do
+ context 'when the user is the owner of a one level group' do
+ before do
+ group.add_owner(user)
+ end
- it 'loads' do
- expect(user.ci_owned_runners).to contain_exactly(runner)
+ it 'loads the runners in the group' do
+ expect(user.ci_owned_runners).to contain_exactly(group_runner)
+ end
end
end
- context 'with personal group runner' do
- let!(:project) { create(:project) }
- let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
- let!(:group) do
- create(:group).tap do |group|
- group.add_owner(user)
+ shared_examples :project_owner do
+ context 'when the user is the owner of a project' do
+ it 'loads the runner belonging to the project' do
+ expect(user.ci_owned_runners).to contain_exactly(runner)
+ end
+ end
+ end
+
+ shared_examples :project_member do
+ context 'when the user is a maintainer' do
+ before do
+ add_user(:maintainer)
+ end
+
+ it 'loads the runners of the project' do
+ expect(user.ci_owned_runners).to contain_exactly(project_runner)
end
end
- it 'loads' do
- expect(user.ci_owned_runners).to contain_exactly(group_runner)
+ context 'when the user is a developer' do
+ before do
+ add_user(:developer)
+ end
+
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
+ end
end
- end
- context 'with personal project and group runner' do
- let(:namespace) { create(:namespace, owner: user) }
- let!(:project) { create(:project, namespace: namespace) }
- let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ context 'when the user is a reporter' do
+ before do
+ add_user(:reporter)
+ end
- let!(:group) do
- create(:group).tap do |group|
- group.add_owner(user)
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
end
end
- it 'loads' do
- expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
+ context 'when the user is a guest' do
+ before do
+ add_user(:guest)
+ end
+
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
+ end
end
end
- shared_examples :member do
+ shared_examples :group_member do
context 'when the user is a maintainer' do
before do
add_user(:maintainer)
end
- it 'does not load' do
+ it 'does not load the runners of the group' do
expect(user.ci_owned_runners).to be_empty
end
end
@@ -2899,29 +2926,49 @@ describe User, :do_not_mock_admin_mode do
add_user(:developer)
end
- it 'does not load' do
+ it 'does not load any runner' do
expect(user.ci_owned_runners).to be_empty
end
end
- end
- shared_examples :group_member do
- context 'when the user is owner' do
+ context 'when the user is a reporter' do
before do
- add_user(:owner)
+ add_user(:reporter)
end
- it 'loads' do
- expect(user.ci_owned_runners).to contain_exactly(runner)
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
end
end
- it_behaves_like :member
+ context 'when the user is a guest' do
+ before do
+ add_user(:guest)
+ end
+
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
+ end
+ end
end
- context 'with groups projects runners' do
- let(:group) { create(:group) }
- let!(:project) { create(:project, group: group) }
+ context 'without any projects nor groups' do
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
+ end
+ end
+
+ context 'with runner in a personal project' do
+ let!(:namespace) { create(:namespace, owner: user) }
+ let!(:project) { create(:project, namespace: namespace) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ it_behaves_like :project_owner
+ end
+
+ context 'with group runner in a non owned group' do
+ let!(:group) { create(:group) }
+ let!(:runner) { create(:ci_runner, :group, groups: [group]) }
def add_user(access)
group.add_user(user, access)
@@ -2930,37 +2977,114 @@ describe User, :do_not_mock_admin_mode do
it_behaves_like :group_member
end
- context 'with groups runners' do
+ context 'with group runner in an owned group' do
+ let!(:group) { create(:group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+
+ it_behaves_like :group_owner
+ end
+
+ context 'with group runner in an owned group and group runner in a different owner subgroup' do
+ let!(:group) { create(:group) }
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
+ let!(:subgroup) { create(:group, parent: group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
+ let!(:another_user) { create(:user) }
+
+ def set_permissions_for_users
+ group.add_owner(user)
+ subgroup.add_owner(another_user)
+ end
+
+ it_behaves_like :nested_groups_owner
+ end
+
+ context 'with personal project runner in an an owned group and a group runner in that same group' do
let!(:group) { create(:group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ let!(:project) { create(:project, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
- def add_user(access)
- group.add_user(user, access)
+ def set_permissions_for_users
+ group.add_owner(user)
end
- it_behaves_like :group_member
+ it_behaves_like :nested_groups_owner
end
- context 'with other projects runners' do
- let!(:project) { create(:project) }
+ context 'with personal project runner in an owned group and a group runner in a subgroup' do
+ let!(:group) { create(:group) }
+ let!(:subgroup) { create(:group, parent: group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
+ let!(:project) { create(:project, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ def set_permissions_for_users
+ group.add_owner(user)
+ end
+
+ it_behaves_like :nested_groups_owner
+ end
+
+ context 'with personal project runner in an owned group in an owned namespace and a group runner in that group' do
+ let!(:namespace) { create(:namespace, owner: user) }
+ let!(:group) { create(:group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ let!(:project) { create(:project, namespace: namespace, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ def set_permissions_for_users
+ group.add_owner(user)
+ end
+
+ it_behaves_like :nested_groups_owner
+ end
+
+ context 'with personal project runner in an owned namespace, an owned group, a subgroup and a group runner in that subgroup' do
+ let!(:namespace) { create(:namespace, owner: user) }
+ let!(:group) { create(:group) }
+ let!(:subgroup) { create(:group, parent: group) }
+ let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
+ let!(:project) { create(:project, namespace: namespace, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ def set_permissions_for_users
+ group.add_owner(user)
+ end
+
+ it_behaves_like :nested_groups_owner
+ end
+
+ context 'with a project runner that belong to projects that belong to a not owned group' do
+ let!(:group) { create(:group) }
+ let!(:project) { create(:project, group: group) }
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
def add_user(access)
project.add_user(user, access)
end
- it_behaves_like :member
+ it_behaves_like :project_member
end
- context 'with subgroup with different owner for project runner' do
- let(:group) { create(:group) }
- let(:another_user) { create(:user) }
- let(:subgroup) { create(:group, parent: group) }
- let!(:project) { create(:project, group: subgroup) }
+ context 'with project runners that belong to projects that do not belong to any group' do
+ let!(:project) { create(:project) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ it 'does not load any runner' do
+ expect(user.ci_owned_runners).to be_empty
+ end
+ end
+
+ context 'with a group runner that belongs to a subgroup of a group owned by another user' do
+ let!(:group) { create(:group) }
+ let!(:subgroup) { create(:group, parent: group) }
+ let!(:runner) { create(:ci_runner, :group, groups: [subgroup]) }
+ let!(:another_user) { create(:user) }
def add_user(access)
- group.add_user(user, access)
+ subgroup.add_user(user, access)
group.add_user(another_user, :owner)
- subgroup.add_user(another_user, :owner)
end
it_behaves_like :group_member
@@ -4406,6 +4530,16 @@ describe User, :do_not_mock_admin_mode do
it { is_expected.to be false }
end
+
+ context 'when `:gitlab_employee_badge` feature flag is disabled' do
+ let(:user) { build(:user, email: 'test@gitlab.com') }
+
+ before do
+ stub_feature_flags(gitlab_employee_badge: false)
+ end
+
+ it { is_expected.to be false }
+ end
end
describe '#current_highest_access_level' do
@@ -4428,6 +4562,27 @@ describe User, :do_not_mock_admin_mode do
end
end
+ describe '#organization' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:user) { build(:user, organization: 'ACME') }
+
+ subject { user.organization }
+
+ where(:gitlab_employee?, :expected_result) do
+ true | 'GitLab'
+ false | 'ACME'
+ end
+
+ with_them do
+ before do
+ allow(user).to receive(:gitlab_employee?).and_return(gitlab_employee?)
+ end
+
+ it { is_expected.to eql(expected_result) }
+ end
+ end
+
context 'when after_commit :update_highest_role' do
describe 'create user' do
it 'initializes a new Members::UpdateHighestRoleService object' do
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index f6b257e6288..164be8f0da6 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -6,20 +6,28 @@ describe API::Runners do
let(:admin) { create(:user, :admin) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
+ let(:group_guest) { create(:user) }
+ let(:group_reporter) { create(:user) }
+ let(:group_developer) { create(:user) }
let(:group_maintainer) { create(:user) }
let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:group) { create(:group).tap { |group| group.add_owner(user) } }
+ let(:subgroup) { create(:group, parent: group) }
let!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') }
let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
let!(:two_projects_runner) { create(:ci_runner, :project, description: 'Two projects runner', projects: [project, project2]) }
- let!(:group_runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
+ let!(:group_runner_a) { create(:ci_runner, :group, description: 'Group runner A', groups: [group]) }
+ let!(:group_runner_b) { create(:ci_runner, :group, description: 'Group runner B', groups: [subgroup]) }
before do
# Set project access for users
+ create(:group_member, :guest, user: group_guest, group: group)
+ create(:group_member, :reporter, user: group_reporter, group: group)
+ create(:group_member, :developer, user: group_developer, group: group)
create(:group_member, :maintainer, user: group_maintainer, group: group)
create(:project_member, :maintainer, user: user, project: project)
create(:project_member, :maintainer, user: user, project: project2)
@@ -41,7 +49,8 @@ describe API::Runners do
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Group runner')
+ a_hash_including('description' => 'Group runner A'),
+ a_hash_including('description' => 'Group runner B')
]
end
@@ -131,7 +140,8 @@ describe API::Runners do
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Group runner'),
+ a_hash_including('description' => 'Group runner A'),
+ a_hash_including('description' => 'Group runner B'),
a_hash_including('description' => 'Shared runner')
]
end
@@ -156,7 +166,8 @@ describe API::Runners do
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Group runner')
+ a_hash_including('description' => 'Group runner A'),
+ a_hash_including('description' => 'Group runner B')
]
end
@@ -165,7 +176,7 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'filters runners by type' do
+ it 'filters runners by project type' do
get api('/runners/all?type=project_type', admin)
expect(json_response).to match_array [
@@ -174,6 +185,15 @@ describe API::Runners do
]
end
+ it 'filters runners by group type' do
+ get api('/runners/all?type=group_type', admin)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Group runner A'),
+ a_hash_including('description' => 'Group runner B')
+ ]
+ end
+
it 'does not filter by invalid type' do
get api('/runners/all?type=bogus', admin)
@@ -526,15 +546,41 @@ describe API::Runners do
end.to change { Ci::Runner.project_type.count }.by(-1)
end
+ it 'does not delete group runner with guest access' do
+ delete api("/runners/#{group_runner_a.id}", group_guest)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'does not delete group runner with reporter access' do
+ delete api("/runners/#{group_runner_a.id}", group_reporter)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'does not delete group runner with developer access' do
+ delete api("/runners/#{group_runner_a.id}", group_developer)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
it 'does not delete group runner with maintainer access' do
- delete api("/runners/#{group_runner.id}", group_maintainer)
+ delete api("/runners/#{group_runner_a.id}", group_maintainer)
expect(response).to have_gitlab_http_status(:forbidden)
end
- it 'deletes group runner with owner access' do
+ it 'deletes owned group runner with owner access' do
+ expect do
+ delete api("/runners/#{group_runner_a.id}", user)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end.to change { Ci::Runner.group_type.count }.by(-1)
+ end
+
+ it 'deletes inherited group runner with owner access' do
expect do
- delete api("/runners/#{group_runner.id}", user)
+ delete api("/runners/#{group_runner_b.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1)
@@ -842,7 +888,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners", user)
expect(json_response).to match_array([
- a_hash_including('description' => 'Group runner')
+ a_hash_including('description' => 'Group runner A')
])
end
@@ -851,7 +897,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners?type=group_type", user)
expect(json_response).to match_array([
- a_hash_including('description' => 'Group runner')
+ a_hash_including('description' => 'Group runner A')
])
end
@@ -939,7 +985,7 @@ describe API::Runners do
end
it 'does not enable group runner' do
- post api("/projects/#{project.id}/runners", user), params: { runner_id: group_runner.id }
+ post api("/projects/#{project.id}/runners", user), params: { runner_id: group_runner_a.id }
expect(response).to have_gitlab_http_status(:forbidden)
end
diff --git a/spec/views/profiles/show.html.haml_spec.rb b/spec/views/profiles/show.html.haml_spec.rb
index 14e6feed3ab..e1c21f87780 100644
--- a/spec/views/profiles/show.html.haml_spec.rb
+++ b/spec/views/profiles/show.html.haml_spec.rb
@@ -19,4 +19,48 @@ describe 'profiles/show' do
expect(rendered).to have_field('user_id', with: user.id)
end
end
+
+ context 'gitlab.com organization field' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ context 'when `:gitlab_employee_badge` feature flag is enabled' do
+ context 'and when user has an `@gitlab.com` email address' do
+ let(:user) { create(:user, email: 'test@gitlab.com') }
+
+ it 'displays the organization field as `readonly` with a `value` of `GitLab`' do
+ render
+
+ expect(rendered).to have_selector('#user_organization[readonly][value="GitLab"]')
+ end
+ end
+
+ context 'and when a user does not have an `@gitlab.com` email' do
+ let(:user) { create(:user, email: 'test@example.com') }
+
+ it 'displays an editable organization field' do
+ render
+
+ expect(rendered).to have_selector('#user_organization:not([readonly]):not([value="GitLab"])')
+ end
+ end
+ end
+
+ context 'when `:gitlab_employee_badge` feature flag is disabled' do
+ before do
+ stub_feature_flags(gitlab_employee_badge: false)
+ end
+
+ context 'and when a user has an `@gitlab.com` email' do
+ let(:user) { create(:user, email: 'test@gitlab.com') }
+
+ it 'displays an editable organization field' do
+ render
+
+ expect(rendered).to have_selector('#user_organization:not([readonly]):not([value="GitLab"])')
+ end
+ end
+ end
+ end
end