Commit c1a50b81 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b6847c62
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
FILE_VIEW_MODE_PREVIEW, FILE_VIEW_MODE_PREVIEW,
} from '../constants'; } from '../constants';
import Editor from '../lib/editor'; import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
import FileTemplatesBar from './file_templates/bar.vue'; import FileTemplatesBar from './file_templates/bar.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -19,7 +18,6 @@ export default { ...@@ -19,7 +18,6 @@ export default {
components: { components: {
ContentViewer, ContentViewer,
DiffViewer, DiffViewer,
ExternalLink,
FileTemplatesBar, FileTemplatesBar,
}, },
props: { props: {
...@@ -275,8 +273,8 @@ export default { ...@@ -275,8 +273,8 @@ export default {
<template> <template>
<div id="ide" class="blob-viewer-container blob-editor-container"> <div id="ide" class="blob-viewer-container blob-editor-container">
<div class="ide-mode-tabs clearfix"> <div v-if="!shouldHideEditor && isEditModeActive" class="ide-mode-tabs clearfix">
<ul v-if="!shouldHideEditor && isEditModeActive" class="nav-links float-left border-bottom-0"> <ul class="nav-links float-left border-bottom-0">
<li :class="editTabCSS"> <li :class="editTabCSS">
<a <a
href="javascript:void(0);" href="javascript:void(0);"
...@@ -296,7 +294,6 @@ export default { ...@@ -296,7 +294,6 @@ export default {
> >
</li> </li>
</ul> </ul>
<external-link :file="file" />
</div> </div>
<file-templates-bar v-if="showFileTemplatesBar(file.name)" /> <file-templates-bar v-if="showFileTemplatesBar(file.name)" />
<div <div
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlIcon } from '@gitlab/ui'; import { GlIcon } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Tracking from '~/tracking';
import { ASC, DESC } from '../constants'; import { ASC, DESC } from '../constants';
const SORT_OPTIONS = [ const SORT_OPTIONS = [
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
components: { components: {
GlIcon, GlIcon,
}, },
mixins: [Tracking.mixin()],
computed: { computed: {
...mapGetters(['sortDirection']), ...mapGetters(['sortDirection']),
selectedOption() { selectedOption() {
...@@ -31,6 +33,7 @@ export default { ...@@ -31,6 +33,7 @@ export default {
} }
this.setDiscussionSortDirection(direction); this.setDiscussionSortDirection(direction);
this.track('change_discussion_sort_direction', { property: direction });
}, },
isDropdownItemActive(sortDir) { isDropdownItemActive(sortDir) {
return sortDir === this.sortDirection; return sortDir === this.sortDirection;
......
...@@ -9,7 +9,13 @@ module Projects ...@@ -9,7 +9,13 @@ module Projects
def show def show
unless @project.import_state&.in_progress? unless @project.import_state&.in_progress?
jira_client = @project.jira_service.client 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 end
flash[:notice] = _("Import %{status}") % { status: @project.import_state.status } if @project.import_state.present? && !@project.import_state.none? flash[:notice] = _("Import %{status}") % { status: @project.import_state.status } if @project.import_state.present? && !@project.import_state.none?
......
...@@ -1405,7 +1405,7 @@ class User < ApplicationRecord ...@@ -1405,7 +1405,7 @@ class User < ApplicationRecord
.select('ci_runners.*') .select('ci_runners.*')
group_runners = Ci::RunnerNamespace 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) .joins(:runner)
.select('ci_runners.*') .select('ci_runners.*')
...@@ -1696,7 +1696,7 @@ class User < ApplicationRecord ...@@ -1696,7 +1696,7 @@ class User < ApplicationRecord
def gitlab_employee? def gitlab_employee?
strong_memoize(:gitlab_employee) do strong_memoize(:gitlab_employee) do
if Gitlab.com? if Feature.enabled?(:gitlab_employee_badge) && Gitlab.com?
Mail::Address.new(email).domain == "gitlab.com" && confirmed? Mail::Address.new(email).domain == "gitlab.com" && confirmed?
else else
false false
...@@ -1713,6 +1713,10 @@ class User < ApplicationRecord ...@@ -1713,6 +1713,10 @@ class User < ApplicationRecord
!confirmed? && !confirmation_period_valid? !confirmed? && !confirmation_period_valid?
end end
def organization
gitlab_employee? ? 'GitLab' : super
end
protected protected
# override, from Devise::Validatable # override, from Devise::Validatable
......
# frozen_string_literal: true # frozen_string_literal: true
class NoteUserEntity < UserEntity 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 unexpose :web_url
end end
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
- else - else
= f.text_field :location, label: s_('Profiles|Location'), class: 'input-lg', placeholder: s_("Profiles|City, country") = 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 :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") = 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 %hr
%h5= s_("Private profile") %h5= s_("Private profile")
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%h3.page-title.d-flex.align-items-center %h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1') = sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import in progress') = _('Import in progress')
- else - elsif @jira_projects.present?
%h3.page-title.d-flex.align-items-center %h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1') = sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import issues from Jira') = _('Import issues from Jira')
......
---
title: Remove open in file view link from Web IDE
merge_request: 28705
author:
type: removed
---
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
...@@ -6567,6 +6567,9 @@ msgstr "" ...@@ -6567,6 +6567,9 @@ msgstr ""
msgid "Dependencies|All" msgid "Dependencies|All"
msgstr "" msgstr ""
msgid "Dependencies|Based on the %{linkStart}latest successful%{linkEnd} scan"
msgstr ""
msgid "Dependencies|Component" msgid "Dependencies|Component"
msgstr "" msgstr ""
...@@ -7124,9 +7127,6 @@ msgstr "" ...@@ -7124,9 +7127,6 @@ msgstr ""
msgid "Display source" msgid "Display source"
msgstr "" 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" msgid "Do not display offers from third parties within GitLab"
msgstr "" msgstr ""
......
...@@ -66,13 +66,29 @@ describe Projects::Import::JiraController do ...@@ -66,13 +66,29 @@ describe Projects::Import::JiraController do
context 'when running jira import first time' do context 'when running jira import first time' do
context 'get show' do context 'get show' do
it 'renders show template' do before do
allow(JIRA::Resource::Project).to receive(:all).and_return([]) allow(JIRA::Resource::Project).to receive(:all).and_return(jira_projects)
expect(project.import_state).to be_nil expect(project.import_state).to be_nil
get :show, params: { namespace_id: project.namespace.to_param, project_id: project } 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
end end
......
...@@ -3,6 +3,7 @@ import Vuex from 'vuex'; ...@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import SortDiscussion from '~/notes/components/sort_discussion.vue'; import SortDiscussion from '~/notes/components/sort_discussion.vue';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import { ASC, DESC } from '~/notes/constants'; import { ASC, DESC } from '~/notes/constants';
import Tracking from '~/tracking';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -22,6 +23,7 @@ describe('Sort Discussion component', () => { ...@@ -22,6 +23,7 @@ describe('Sort Discussion component', () => {
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
jest.spyOn(Tracking, 'event');
}); });
afterEach(() => { afterEach(() => {
...@@ -37,6 +39,9 @@ describe('Sort Discussion component', () => { ...@@ -37,6 +39,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-newest-first').trigger('click'); wrapper.find('.js-newest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC); 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', () => { ...@@ -58,6 +63,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-oldest-first').trigger('click'); wrapper.find('.js-oldest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC); 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', () => { it('applies the active class to the correct button in the dropdown', () => {
......
...@@ -2835,61 +2835,88 @@ describe User, :do_not_mock_admin_mode do ...@@ -2835,61 +2835,88 @@ describe User, :do_not_mock_admin_mode do
describe '#ci_owned_runners' do describe '#ci_owned_runners' do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project) }
let(:runner) { create(:ci_runner, :project, projects: [project]) }
context 'without any projects nor groups' do shared_examples :nested_groups_owner do
it 'does not load' do context 'when the user is the owner of a multi-level group' do
expect(user.ci_owned_runners).to be_empty 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
end end
context 'with personal projects runners' do shared_examples :group_owner do
let(:namespace) { create(:namespace, owner: user) } context 'when the user is the owner of a one level group' do
let!(:project) { create(:project, namespace: namespace) } before do
group.add_owner(user)
end
it 'loads' do it 'loads the runners in the group' do
expect(user.ci_owned_runners).to contain_exactly(runner) expect(user.ci_owned_runners).to contain_exactly(group_runner)
end
end end
end end
context 'with personal group runner' do shared_examples :project_owner do
let!(:project) { create(:project) } context 'when the user is the owner of a project' do
let(:group_runner) { create(:ci_runner, :group, groups: [group]) } it 'loads the runner belonging to the project' do
let!(:group) do expect(user.ci_owned_runners).to contain_exactly(runner)
create(:group).tap do |group| end
group.add_owner(user) 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
end end
it 'loads' do context 'when the user is a developer' do
expect(user.ci_owned_runners).to contain_exactly(group_runner) before do
add_user(:developer)
end
it 'does not load any runner' do
expect(user.ci_owned_runners).to be_empty
end
end end
end
context 'with personal project and group runner' do context 'when the user is a reporter' do
let(:namespace) { create(:namespace, owner: user) } before do
let!(:project) { create(:project, namespace: namespace) } add_user(:reporter)
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) } end
let!(:group) do it 'does not load any runner' do
create(:group).tap do |group| expect(user.ci_owned_runners).to be_empty
group.add_owner(user)
end end
end end
it 'loads' do context 'when the user is a guest' do
expect(user.ci_owned_runners).to contain_exactly(runner, group_runner) before do
add_user(:guest)
end
it 'does not load any runner' do
expect(user.ci_owned_runners).to be_empty
end
end end
end end
shared_examples :member do shared_examples :group_member do
context 'when the user is a maintainer' do context 'when the user is a maintainer' do
before do before do
add_user(:maintainer) add_user(:maintainer)
end end
it 'does not load' do it 'does not load the runners of the group' do
expect(user.ci_owned_runners).to be_empty expect(user.ci_owned_runners).to be_empty
end end
end end
...@@ -2899,29 +2926,49 @@ describe User, :do_not_mock_admin_mode do ...@@ -2899,29 +2926,49 @@ describe User, :do_not_mock_admin_mode do
add_user(:developer) add_user(:developer)
end end
it 'does not load' do it 'does not load any runner' do
expect(user.ci_owned_runners).to be_empty expect(user.ci_owned_runners).to be_empty
end end
end end
end
shared_examples :group_member do context 'when the user is a reporter' do
context 'when the user is owner' do
before do before do
add_user(:owner) add_user(:reporter)
end end
it 'loads' do it 'does not load any runner' do
expect(user.ci_owned_runners).to contain_exactly(runner) expect(user.ci_owned_runners).to be_empty
end end
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 end
context 'with groups projects runners' do context 'without any projects nor groups' do
let(:group) { create(:group) } it 'does not load any runner' do
let!(:project) { create(:project, group: group) } 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) def add_user(access)
group.add_user(user, access) group.add_user(user, access)
...@@ -2930,37 +2977,114 @@ describe User, :do_not_mock_admin_mode do ...@@ -2930,37 +2977,114 @@ describe User, :do_not_mock_admin_mode do
it_behaves_like :group_member it_behaves_like :group_member
end 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!(: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) { 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) def set_permissions_for_users
group.add_user(user, access) group.add_owner(user)
end end
it_behaves_like :group_member it_behaves_like :nested_groups_owner
end end
context 'with other projects runners' do context 'with personal project runner in an owned group and a group runner in a subgroup' do
let!(:project) { create(:project) } 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) def add_user(access)
project.add_user(user, access) project.add_user(user, access)
end end
it_behaves_like :member it_behaves_like :project_member
end end
context 'with subgroup with different owner for project runner' do context 'with project runners that belong to projects that do not belong to any group' do
let(:group) { create(:group) } let!(:project) { create(:project) }
let(:another_user) { create(:user) } let!(:runner) { create(:ci_runner, :project, projects: [project]) }
let(:subgroup) { create(:group, parent: group) }
let!(:project) { create(:project, group: subgroup) } 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) def add_user(access)
group.add_user(user, access) subgroup.add_user(user, access)
group.add_user(another_user, :owner) group.add_user(another_user, :owner)
subgroup.add_user(another_user, :owner)
end end
it_behaves_like :group_member it_behaves_like :group_member
...@@ -4406,6 +4530,16 @@ describe User, :do_not_mock_admin_mode do ...@@ -4406,6 +4530,16 @@ describe User, :do_not_mock_admin_mode do
it { is_expected.to be false } it { is_expected.to be false }
end 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 end
describe '#current_highest_access_level' do describe '#current_highest_access_level' do
...@@ -4428,6 +4562,27 @@ describe User, :do_not_mock_admin_mode do ...@@ -4428,6 +4562,27 @@ describe User, :do_not_mock_admin_mode do
end end
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 context 'when after_commit :update_highest_role' do
describe 'create user' do describe 'create user' do
it 'initializes a new Members::UpdateHighestRoleService object' do it 'initializes a new Members::UpdateHighestRoleService object' do
......
...@@ -6,20 +6,28 @@ describe API::Runners do ...@@ -6,20 +6,28 @@ describe API::Runners do
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { 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(:group_maintainer) { create(:user) }
let(:project) { create(:project, creator_id: user.id) } let(:project) { create(:project, creator_id: user.id) }
let(:project2) { 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(: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!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') }
let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) } 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!(: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 before do
# Set project access for users # 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(:group_member, :maintainer, user: group_maintainer, group: group)
create(:project_member, :maintainer, user: user, project: project) create(:project_member, :maintainer, user: user, project: project)
create(:project_member, :maintainer, user: user, project: project2) create(:project_member, :maintainer, user: user, project: project2)
...@@ -41,7 +49,8 @@ describe API::Runners do ...@@ -41,7 +49,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects 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 end
...@@ -131,7 +140,8 @@ describe API::Runners do ...@@ -131,7 +140,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects 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') a_hash_including('description' => 'Shared runner')
] ]
end end
...@@ -156,7 +166,8 @@ describe API::Runners do ...@@ -156,7 +166,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects 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 end
...@@ -165,7 +176,7 @@ describe API::Runners do ...@@ -165,7 +176,7 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
it 'filters runners by type' do it 'filters runners by project type' do
get api('/runners/all?type=project_type', admin) get api('/runners/all?type=project_type', admin)
expect(json_response).to match_array [ expect(json_response).to match_array [
...@@ -174,6 +185,15 @@ describe API::Runners do ...@@ -174,6 +185,15 @@ describe API::Runners do
] ]
end 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 it 'does not filter by invalid type' do
get api('/runners/all?type=bogus', admin) get api('/runners/all?type=bogus', admin)
...@@ -526,15 +546,41 @@ describe API::Runners do ...@@ -526,15 +546,41 @@ describe API::Runners do
end.to change { Ci::Runner.project_type.count }.by(-1) end.to change { Ci::Runner.project_type.count }.by(-1)
end 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 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) expect(response).to have_gitlab_http_status(:forbidden)
end 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 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) expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1) end.to change { Ci::Runner.group_type.count }.by(-1)
...@@ -842,7 +888,7 @@ describe API::Runners do ...@@ -842,7 +888,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners", user) get api("/groups/#{group.id}/runners", user)
expect(json_response).to match_array([ expect(json_response).to match_array([
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A')
]) ])
end end
...@@ -851,7 +897,7 @@ describe API::Runners do ...@@ -851,7 +897,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners?type=group_type", user) get api("/groups/#{group.id}/runners?type=group_type", user)
expect(json_response).to match_array([ expect(json_response).to match_array([
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A')
]) ])
end end
...@@ -939,7 +985,7 @@ describe API::Runners do ...@@ -939,7 +985,7 @@ describe API::Runners do
end end
it 'does not enable group runner' do 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) expect(response).to have_gitlab_http_status(:forbidden)
end end
......
...@@ -19,4 +19,48 @@ describe 'profiles/show' do ...@@ -19,4 +19,48 @@ describe 'profiles/show' do
expect(rendered).to have_field('user_id', with: user.id) expect(rendered).to have_field('user_id', with: user.id)
end end
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 end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment