Commit 0a6450f4 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'qa-ml-add-restricted-protected-branch-test' into 'master'

Add restricted user/group protected branch test

Closes gitlab-org/quality/testcases#117

See merge request gitlab-org/gitlab!16358
parents 214441d1 0d8410f4
...@@ -29,4 +29,4 @@ ...@@ -29,4 +29,4 @@
= yield :push_access_levels = yield :push_access_levels
.card-footer .card-footer
= f.submit 'Protect', class: 'btn-success btn', disabled: true = f.submit 'Protect', class: 'btn-success btn', disabled: true, data: { qa_selector: 'protect_button' }
...@@ -61,6 +61,7 @@ module QA ...@@ -61,6 +61,7 @@ module QA
autoload :KubernetesCluster, 'qa/resource/kubernetes_cluster' autoload :KubernetesCluster, 'qa/resource/kubernetes_cluster'
autoload :User, 'qa/resource/user' autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone' autoload :ProjectMilestone, 'qa/resource/project_milestone'
autoload :Members, 'qa/resource/members'
autoload :Wiki, 'qa/resource/wiki' autoload :Wiki, 'qa/resource/wiki'
autoload :File, 'qa/resource/file' autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork' autoload :Fork, 'qa/resource/fork'
......
...@@ -20,6 +20,23 @@ module QA ...@@ -20,6 +20,23 @@ module QA
end end
end end
end end
private
def select_allowed(action, allowed)
click_element :"allowed_to_#{action}_select"
allowed[:roles] = QA::Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
within_element(:"allowed_to_#{action}_dropdown") do
click_on allowed[:roles]
allowed[:users].each { |user| click_on user.username } if allowed.key?(:users)
allowed[:groups].each { |group| click_on group.name } if allowed.key?(:groups)
end
# Click the select element again to close the dropdown
click_element :protected_branch_select
end
end end
end end
end end
......
...@@ -44,7 +44,7 @@ module QA ...@@ -44,7 +44,7 @@ module QA
def sign_in_using_credentials(user = nil) def sign_in_using_credentials(user = nil)
# Don't try to log-in if we're already logged-in # Don't try to log-in if we're already logged-in
return if Page::Main::Menu.perform { |menu| menu.has_personal_area?(wait: 0) } return if Page::Main::Menu.perform(&:signed_in?)
using_wait_time 0 do using_wait_time 0 do
set_initial_password_if_present set_initial_password_if_present
...@@ -75,10 +75,7 @@ module QA ...@@ -75,10 +75,7 @@ module QA
end end
def sign_in_using_ldap_credentials(user) def sign_in_using_ldap_credentials(user)
# Log out if already logged in Page::Main::Menu.perform(&:sign_out_if_signed_in)
Page::Main::Menu.perform do |menu|
menu.sign_out if menu.has_personal_area?(wait: 0)
end
using_wait_time 0 do using_wait_time 0 do
set_initial_password_if_present set_initial_password_if_present
...@@ -149,7 +146,7 @@ module QA ...@@ -149,7 +146,7 @@ module QA
end end
def sign_out_and_sign_in_as(user:) def sign_out_and_sign_in_as(user:)
Menu.perform(&:sign_out) Menu.perform(&:sign_out_if_signed_in)
has_sign_in_tab? has_sign_in_tab?
sign_in_using_credentials(user) sign_in_using_credentials(user)
end end
......
...@@ -55,6 +55,10 @@ module QA ...@@ -55,6 +55,10 @@ module QA
within_top_menu { click_element :admin_area_link } within_top_menu { click_element :admin_area_link }
end end
def signed_in?
has_personal_area?(wait: 0)
end
def sign_out def sign_out
within_user_menu do within_user_menu do
click_element :sign_out_link click_element :sign_out_link
...@@ -62,7 +66,7 @@ module QA ...@@ -62,7 +66,7 @@ module QA
end end
def sign_out_if_signed_in def sign_out_if_signed_in
sign_out if has_personal_area?(wait: 0) sign_out if signed_in?
end end
def click_settings_link def click_settings_link
......
...@@ -25,6 +25,10 @@ module QA ...@@ -25,6 +25,10 @@ module QA
element :protected_branches_list element :protected_branches_list
end end
view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
element :protect_button
end
def select_branch(branch_name) def select_branch(branch_name)
click_element :protected_branch_select click_element :protected_branch_select
...@@ -33,40 +37,31 @@ module QA ...@@ -33,40 +37,31 @@ module QA
end end
end end
def allow_no_one_to_push def select_allowed_to_merge(allowed)
go_to_allow(:push, 'No one') select_allowed(:merge, allowed)
end
def allow_devs_and_maintainers_to_push
go_to_allow(:push, 'Developers + Maintainers')
end end
# @deprecated def select_allowed_to_push(allowed)
alias_method :allow_devs_and_masters_to_push, :allow_devs_and_maintainers_to_push select_allowed(:push, allowed)
def allow_no_one_to_merge
go_to_allow(:merge, 'No one')
end end
def allow_devs_and_maintainers_to_merge
go_to_allow(:merge, 'Developers + Maintainers')
end
# @deprecated
alias_method :allow_devs_and_masters_to_merge, :allow_devs_and_maintainers_to_merge
def protect_branch def protect_branch
click_on 'Protect' click_element :protect_button
end end
private private
def go_to_allow(action, text) def select_allowed(action, allowed)
click_element :"allowed_to_#{action}_select" click_element :"allowed_to_#{action}_select"
allowed[:roles] = Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
within_element(:"allowed_to_#{action}_dropdown") do within_element(:"allowed_to_#{action}_dropdown") do
click_on text click_on allowed[:roles]
end end
# Click the select element again to close the dropdown
click_element :protected_branch_select
end end
end end
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module QA module QA
module Resource module Resource
class Group < Base class Group < Base
include Members
attr_accessor :path, :description attr_accessor :path, :description
attribute :sandbox do attribute :sandbox do
...@@ -48,19 +50,10 @@ module QA ...@@ -48,19 +50,10 @@ module QA
super super
end end
def add_member(user, access_level = '30')
# 30 = developer access
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_get_path def api_get_path
"/groups/#{CGI.escape("#{sandbox.path}/#{path}")}" "/groups/#{CGI.escape("#{sandbox.path}/#{path}")}"
end end
def api_members_path
"#{api_get_path}/members"
end
def api_post_path def api_post_path
'/groups' '/groups'
end end
......
# frozen_string_literal: true
module QA
module Resource
#
# Included in Resource::Project and Resource::Group to allow changes to
# project/group membership
#
module Members
def add_member(user, access_level = AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_members_path
"#{api_get_path}/members"
end
class AccessLevel
NO_ACCESS = 0
GUEST = 10
REPORTER = 20
DEVELOPER = 30
MAINTAINER = 40
OWNER = 50
end
end
end
end
...@@ -6,6 +6,7 @@ module QA ...@@ -6,6 +6,7 @@ module QA
module Resource module Resource
class Project < Base class Project < Base
include Events::Project include Events::Project
include Members
attr_writer :initialize_with_readme attr_writer :initialize_with_readme
attr_writer :visibility attr_writer :visibility
...@@ -75,11 +76,6 @@ module QA ...@@ -75,11 +76,6 @@ module QA
super super
end end
def add_member(user, access_level = '30')
# 30 = developer access
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_get_path def api_get_path
"/projects/#{CGI.escape(path_with_namespace)}" "/projects/#{CGI.escape(path_with_namespace)}"
end end
...@@ -112,6 +108,10 @@ module QA ...@@ -112,6 +108,10 @@ module QA
post_body post_body
end end
def share_with_group(invitee, access_level = Resource::Members::AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, "/projects/#{id}/share").url, { group_id: invitee.id, group_access: access_level }
end
private private
def transform_api_resource(api_resource) def transform_api_resource(api_resource)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
module Resource module Resource
class ProtectedBranch < Base class ProtectedBranch < Base
attr_accessor :branch_name, :allow_to_push, :allow_to_merge, :protected attr_accessor :branch_name, :allowed_to_push, :allowed_to_merge, :protected
attribute :project do attribute :project do
Project.fabricate_via_api! do |resource| Project.fabricate_via_api! do |resource|
...@@ -25,8 +25,12 @@ module QA ...@@ -25,8 +25,12 @@ module QA
def initialize def initialize
@branch_name = 'test/branch' @branch_name = 'test/branch'
@allow_to_push = true @allowed_to_push = {
@allow_to_merge = true roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
}
@allowed_to_merge = {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
}
@protected = false @protected = false
end end
...@@ -35,29 +39,14 @@ module QA ...@@ -35,29 +39,14 @@ module QA
project.wait_for_push_new_branch @branch_name project.wait_for_push_new_branch @branch_name
# The upcoming process will make it access the Protected Branches page,
# select the already created branch and protect it according
# to `allow_to_push` variable.
return branch unless @protected
project.visit! project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings) Page::Project::Menu.perform(&:go_to_repository_settings)
Page::Project::Settings::Repository.perform do |setting| Page::Project::Settings::Repository.perform do |setting|
setting.expand_protected_branches do |page| setting.expand_protected_branches do |page|
page.select_branch(branch_name) page.select_branch(branch_name)
page.select_allowed_to_merge(allowed_to_merge)
if allow_to_push page.select_allowed_to_push(allowed_to_push)
page.allow_devs_and_maintainers_to_push
else
page.allow_no_one_to_push
end
if allow_to_merge
page.allow_devs_and_maintainers_to_merge
else
page.allow_no_one_to_merge
end
page.wait(reload: false) do page.wait(reload: false) do
!page.first('.btn-success').disabled? !page.first('.btn-success').disabled?
...@@ -79,6 +68,12 @@ module QA ...@@ -79,6 +68,12 @@ module QA
def api_delete_path def api_delete_path
"/projects/#{@project.api_resource[:id]}/protected_branches/#{@branch_name}" "/projects/#{@project.api_resource[:id]}/protected_branches/#{@branch_name}"
end end
class Roles
NO_ONE = 'No one'
DEVS_AND_MAINTAINERS = 'Developers + Maintainers'
MAINTAINERS = 'Maintainers'
end
end end
end end
end end
...@@ -17,16 +17,11 @@ module QA ...@@ -17,16 +17,11 @@ module QA
Page::Main::Login.perform(&:sign_in_using_credentials) Page::Main::Login.perform(&:sign_in_using_credentials)
end end
after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
end
context 'when developers and maintainers are allowed to push to a protected branch' do context 'when developers and maintainers are allowed to push to a protected branch' do
it 'user with push rights successfully pushes to the protected branch' do it 'user with push rights successfully pushes to the protected branch' do
create_protected_branch(allow_to_push: true) create_protected_branch(allowed_to_push: {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
})
push = push_new_file(branch_name) push = push_new_file(branch_name)
...@@ -36,18 +31,19 @@ module QA ...@@ -36,18 +31,19 @@ module QA
context 'when developers and maintainers are not allowed to push to a protected branch' do context 'when developers and maintainers are not allowed to push to a protected branch' do
it 'user without push rights fails to push to the protected branch' do it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false) create_protected_branch(allowed_to_push: {
roles: Resource::ProtectedBranch::Roles::NO_ONE
})
expect { push_new_file(branch_name) }.to raise_error(QA::Git::Repository::RepositoryCommandError, /remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/) expect { push_new_file(branch_name) }.to raise_error(QA::Git::Repository::RepositoryCommandError, /remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end end
end end
def create_protected_branch(allow_to_push:) def create_protected_branch(allowed_to_push:)
Resource::ProtectedBranch.fabricate! do |resource| Resource::ProtectedBranch.fabricate! do |resource|
resource.branch_name = branch_name resource.branch_name = branch_name
resource.project = project resource.project = project
resource.allow_to_push = allow_to_push resource.allowed_to_push = allowed_to_push
resource.protected = true
end end
end end
......
# frozen_string_literal: true
module QA
context 'Create' do
describe 'Restricted protected branch push and merge' do
let(:user_developer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:user_maintainer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
shared_examples 'only user with access pushes and merges' do
it 'unselected maintainer user fails to push' do
expect { push_new_file(branch_name, as_user: user_maintainer) }.to raise_error(
QA::Git::Repository::RepositoryCommandError,
/remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
it 'selected developer user pushes and merges' do
push = push_new_file(branch_name, as_user: user_developer)
expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
Resource::MergeRequest.fabricate_via_api! do |merge_request|
merge_request.project = project
merge_request.target_new_branch = false
merge_request.source_branch = branch_name
merge_request.no_preparation = true
end.visit!
Page::MergeRequest::Show.perform(&:merge!)
expect(page).to have_content('The changes were merged')
end
end
context 'when only one user is allowed to merge and push to a protected branch' do
let(:project) do
Resource::Project.fabricate! do |resource|
resource.name = 'user-with-access-to-protected-branch'
resource.initialize_with_readme = true
end
end
before do
project.add_member(user_developer, Resource::Members::AccessLevel::DEVELOPER)
project.add_member(user_maintainer, Resource::Members::AccessLevel::MAINTAINER)
login
Resource::ProtectedBranch.fabricate! do |protected_branch|
protected_branch.branch_name = branch_name
protected_branch.project = project
protected_branch.allowed_to_merge = {
users: [user_developer]
}
protected_branch.allowed_to_push = {
users: [user_developer]
}
end
end
it_behaves_like 'only user with access pushes and merges'
end
context 'when only one group is allowed to merge and push to a protected branch' do
let(:group) do
Resource::Group.fabricate_via_api! do |group|
group.path = "access-to-protected-branch-#{SecureRandom.hex(8)}"
end
end
let(:project) do
Resource::Project.fabricate! do |resource|
resource.name = 'group-with-access-to-protected-branch'
resource.initialize_with_readme = true
end
end
before do
login
group.add_member(user_developer, Resource::Members::AccessLevel::DEVELOPER)
project.share_with_group(group, Resource::Members::AccessLevel::DEVELOPER)
project.add_member(user_maintainer, Resource::Members::AccessLevel::MAINTAINER)
Resource::ProtectedBranch.fabricate! do |protected_branch|
protected_branch.branch_name = branch_name
protected_branch.project = project
protected_branch.allowed_to_merge = {
groups: [group]
}
protected_branch.allowed_to_push = {
groups: [group]
}
end
end
it_behaves_like 'only user with access pushes and merges'
end
def login(as_user: Runtime::User)
Page::Main::Menu.perform(&:sign_out_if_signed_in)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login|
login.sign_in_using_credentials(as_user)
end
end
def push_new_file(branch, as_user: user)
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = project.repository_http_location.uri
push.branch_name = branch_name
push.new_branch = false
push.user = as_user
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