Commit ab690af8 authored by Sanad Liaquat's avatar Sanad Liaquat

Merge branch 'qa/tn/new_test/push_rules' into 'master'

Push Rules test implementation

Closes gitlab-org/quality/testcases#89, gitlab-org/quality/testcases#90, gitlab-org/quality/testcases#91, gitlab-org/quality/testcases#92, gitlab-org/quality/testcases#93, gitlab-org/quality/testcases#94, gitlab-org/quality/testcases#95, gitlab-org/quality/testcases#96, gitlab-org/quality/testcases#97, and gitlab-org/quality/testcases#98

See merge request gitlab-org/gitlab!16454
parents b3512685 cb71eba4
- return unless @project.feature_available?(:push_rules) - return unless @project.feature_available?(:push_rules)
- expanded = expanded_by_default? - expanded = expanded_by_default?
%section.settings.no-animate#js-push-rules{ class: ('expanded' if expanded) } %section.settings.no-animate#js-push-rules{ class: ('expanded' if expanded), data: { qa_selector: 'push_rules_content' } }
.settings-header .settings-header
%h4 %h4
Push Rules Push Rules
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- push_rule = local_assigns.fetch(:push_rule) - push_rule = local_assigns.fetch(:push_rule)
.form-check .form-check
= form.check_box :commit_committer_check, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :commit_committer_check) = form.check_box :commit_committer_check, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :commit_committer_check), data: { qa_selector: 'committer_restriction_checkbox' }
= form.label :commit_committer_check, class: "label-bold form-check-label" do = form.label :commit_committer_check, class: "label-bold form-check-label" do
= s_("PushRule|Committer restriction") = s_("PushRule|Committer restriction")
%p.text-muted %p.text-muted
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render 'shared/push_rules/reject_unsigned_commits_setting', form: f, push_rule: f.object = render 'shared/push_rules/reject_unsigned_commits_setting', form: f, push_rule: f.object
.form-check .form-check
= f.check_box :deny_delete_tag, class: "form-check-input" = f.check_box :deny_delete_tag, class: "form-check-input", data: { qa_selector: 'deny_delete_tag_checkbox' }
= f.label :deny_delete_tag, class: "label-bold form-check-label" do = f.label :deny_delete_tag, class: "label-bold form-check-label" do
Do not allow users to remove git tags with Do not allow users to remove git tags with
%code git push %code git push
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
Tags can still be deleted through the web UI. Tags can still be deleted through the web UI.
.form-check .form-check
= f.check_box :member_check, class: "form-check-input" = f.check_box :member_check, class: "form-check-input", data: { qa_selector: 'restrict_author_checkbox' }
= f.label :member_check, "Check whether author is a GitLab user", class: "label-bold form-check-label" = f.label :member_check, "Check whether author is a GitLab user", class: "label-bold form-check-label"
%p.text-muted %p.text-muted
Restrict commits by author (email) to existing GitLab users Restrict commits by author (email) to existing GitLab users
.form-check .form-check
= f.check_box :prevent_secrets, class: "form-check-input" = f.check_box :prevent_secrets, class: "form-check-input", data: { qa_selector: 'prevent_secrets_checkbox' }
= f.label :prevent_secrets, "Prevent committing secrets to Git", class: "label-bold form-check-label" = f.label :prevent_secrets, "Prevent committing secrets to Git", class: "label-bold form-check-label"
%p.text-muted %p.text-muted
GitLab will reject any files that are likely to contain secrets. GitLab will reject any files that are likely to contain secrets.
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
.form-group .form-group
= f.label :commit_message_regex, "Commit message", class: "label-bold" = f.label :commit_message_regex, "Commit message", class: "label-bold"
= f.text_field :commit_message_regex, class: "form-control", placeholder: 'Example: Fixes \d+\..*' = f.text_field :commit_message_regex, class: "form-control", placeholder: 'Example: Fixes \d+\..*', data: { qa_selector: 'commit_message_field' }
.form-text.text-muted .form-text.text-muted
All commit messages must match this All commit messages must match this
= link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax' = link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax'
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
.form-group .form-group
= f.label :commit_message_negative_regex, "Commit message negative match", class: 'label-bold' = f.label :commit_message_negative_regex, "Commit message negative match", class: 'label-bold'
= f.text_field :commit_message_negative_regex, class: "form-control", placeholder: 'Example: ssh\:\/\/' = f.text_field :commit_message_negative_regex, class: "form-control", placeholder: 'Example: ssh\:\/\/', data: { qa_selector: 'deny_commit_message_field' }
.form-text.text-muted .form-text.text-muted
No commit message is allowed to match this No commit message is allowed to match this
= link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax' = link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax'
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
.form-group .form-group
= f.label :branch_name_regex, "Branch name", class: "label-bold" = f.label :branch_name_regex, "Branch name", class: "label-bold"
= f.text_field :branch_name_regex, class: "form-control", placeholder: 'Example: (feature|hotfix)\/*' = f.text_field :branch_name_regex, class: "form-control", placeholder: 'Example: (feature|hotfix)\/*', data: { qa_selector: 'branch_name_field' }
.form-text.text-muted .form-text.text-muted
All branch names must match this All branch names must match this
= link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax' = link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax'
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
.form-group .form-group
= f.label :author_email_regex, "Commit author's email", class: "label-bold" = f.label :author_email_regex, "Commit author's email", class: "label-bold"
= f.text_field :author_email_regex, class: "form-control", placeholder: 'Example: @my-company.com$' = f.text_field :author_email_regex, class: "form-control", placeholder: 'Example: @my-company.com$', data: { qa_selector: 'author_email_field' }
.form-text.text-muted .form-text.text-muted
All commit author's email must match this All commit author's email must match this
= link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax' = link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax'
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
.form-group .form-group
= f.label :file_name_regex, "Prohibited file names", class: "label-bold" = f.label :file_name_regex, "Prohibited file names", class: "label-bold"
= f.text_field :file_name_regex, class: "form-control", placeholder: 'Example: (jar|exe)$' = f.text_field :file_name_regex, class: "form-control", placeholder: 'Example: (jar|exe)$', data: { qa_selector: 'file_name_field' }
.form-text.text-muted .form-text.text-muted
All commited filenames must not match this All commited filenames must not match this
= link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax' = link_to 'regular expression', 'https://github.com/google/re2/wiki/Syntax'
...@@ -73,9 +73,9 @@ ...@@ -73,9 +73,9 @@
.form-group .form-group
= f.label :max_file_size, "Maximum file size (MB)", class: "label-bold" = f.label :max_file_size, "Maximum file size (MB)", class: "label-bold"
= f.number_field :max_file_size, class: "form-control", min: 0 = f.number_field :max_file_size, class: "form-control", min: 0, data: { qa_selector: 'file_size_field' }
.form-text.text-muted .form-text.text-muted
Pushes that contain added or updated files that exceed this file size are rejected. Pushes that contain added or updated files that exceed this file size are rejected.
Set to 0 to allow files of any size. Set to 0 to allow files of any size.
= f.submit "Save Push Rules", class: "btn btn-success" = f.submit "Save Push Rules", class: "btn btn-success", data: { qa_selector: 'submit_settings_button' }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- push_rule = local_assigns.fetch(:push_rule) - push_rule = local_assigns.fetch(:push_rule)
.form-check .form-check
= form.check_box :reject_unsigned_commits, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :reject_unsigned_commits) = form.check_box :reject_unsigned_commits, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :reject_unsigned_commits), data: { qa_selector: 'reject_unsigned_commits_checkbox' }
= form.label :reject_unsigned_commits, class: "label-bold form-check-label" do = form.label :reject_unsigned_commits, class: "label-bold form-check-label" do
Reject unsigned commits Reject unsigned commits
%p.text-muted %p.text-muted
......
...@@ -23,6 +23,7 @@ module QA ...@@ -23,6 +23,7 @@ module QA
autoload :Feature, 'qa/runtime/feature' autoload :Feature, 'qa/runtime/feature'
autoload :Fixtures, 'qa/runtime/fixtures' autoload :Fixtures, 'qa/runtime/fixtures'
autoload :Logger, 'qa/runtime/logger' autoload :Logger, 'qa/runtime/logger'
autoload :GPG, 'qa/runtime/gpg'
module API module API
autoload :Client, 'qa/runtime/api/client' autoload :Client, 'qa/runtime/api/client'
...@@ -67,7 +68,9 @@ module QA ...@@ -67,7 +68,9 @@ module QA
autoload :Fork, 'qa/resource/fork' autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key' autoload :SSHKey, 'qa/resource/ssh_key'
autoload :Snippet, 'qa/resource/snippet' autoload :Snippet, 'qa/resource/snippet'
autoload :Tag, 'qa/resource/tag'
autoload :ProjectMember, 'qa/resource/project_member' autoload :ProjectMember, 'qa/resource/project_member'
autoload :UserGPG, 'qa/resource/user_gpg'
module Events module Events
autoload :Base, 'qa/resource/events/base' autoload :Base, 'qa/resource/events/base'
......
...@@ -109,6 +109,8 @@ module QA ...@@ -109,6 +109,8 @@ module QA
autoload :MirroringRepositories, 'qa/ee/page/project/settings/mirroring_repositories' autoload :MirroringRepositories, 'qa/ee/page/project/settings/mirroring_repositories'
autoload :Main, 'qa/ee/page/project/settings/main' autoload :Main, 'qa/ee/page/project/settings/main'
autoload :MergeRequestApproval, 'qa/ee/page/project/settings/merge_request_approval' autoload :MergeRequestApproval, 'qa/ee/page/project/settings/merge_request_approval'
autoload :Repository, 'qa/ee/page/project/settings/repository'
autoload :PushRules, 'qa/ee/page/project/settings/push_rules'
end end
module Operations module Operations
......
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Settings
class PushRules < QA::Page::Base
view 'ee/app/views/shared/push_rules/_form.html.haml' do
element :deny_delete_tag_checkbox
element :restrict_author_checkbox
element :prevent_secrets_checkbox
element :commit_message_field
element :deny_commit_message_field
element :branch_name_field
element :author_email_field
element :file_name_field
element :file_size_field
element :submit_settings_button
end
view 'ee/app/views/shared/push_rules/_reject_unsigned_commits_setting.html.haml' do
element :reject_unsigned_commits_checkbox
end
view 'ee/app/views/shared/push_rules/_commit_committer_check_setting.html.haml' do
element :committer_restriction_checkbox
end
def check_reject_unsigned_commits
check_element :reject_unsigned_commits_checkbox
end
def check_committer_restriction
check_element :committer_restriction_checkbox
end
def check_deny_delete_tag
check_element :deny_delete_tag_checkbox
end
def check_restrict_author
check_element :restrict_author_checkbox
end
def check_prevent_secrets
check_element :prevent_secrets_checkbox
end
def fill_commit_message_rule(message)
fill_element :commit_message_field, message
end
def fill_deny_commit_message_rule(message)
fill_element :deny_commit_message_field, message
end
def fill_branch_name(name)
fill_element :branch_name_field, name
end
def fill_author_email(email)
fill_element :author_email_field, email
end
def fill_file_name(file_name)
fill_element :file_name_field, file_name
end
def fill_file_size(file_size)
fill_element :file_size_field, file_size
end
def click_submit
click_element :submit_settings_button
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Settings
module Repository
def self.prepended(page)
page.module_eval do
view 'ee/app/views/projects/push_rules/_index.html.haml' do
element :push_rules_content
end
end
end
def expand_push_rules(&block)
expand_section(:push_rules_content) do
PushRules.perform(&block)
end
end
end
end
end
end
end
end
...@@ -14,7 +14,7 @@ module QA ...@@ -14,7 +14,7 @@ module QA
include Scenario::Actable include Scenario::Actable
RepositoryCommandError = Class.new(StandardError) RepositoryCommandError = Class.new(StandardError)
attr_writer :use_lfs attr_writer :use_lfs, :gpg_key_id
attr_accessor :env_vars attr_accessor :env_vars
InvalidCredentialsError = Class.new(RuntimeError) InvalidCredentialsError = Class.new(RuntimeError)
...@@ -25,6 +25,7 @@ module QA ...@@ -25,6 +25,7 @@ module QA
# .netrc can be utilised # .netrc can be utilised
self.env_vars = [%Q{HOME="#{tmp_home_dir}"}] self.env_vars = [%Q{HOME="#{tmp_home_dir}"}]
@use_lfs = false @use_lfs = false
@gpg_key_id = nil
end end
def self.perform(*args) def self.perform(*args)
...@@ -99,10 +100,18 @@ module QA ...@@ -99,10 +100,18 @@ module QA
git_lfs_track_result.to_s + git_add_result.to_s git_lfs_track_result.to_s + git_add_result.to_s
end end
def delete_tag(tag_name)
run(%Q{git push origin --delete #{tag_name}}).to_s
end
def commit(message) def commit(message)
run(%Q{git commit -m "#{message}"}).to_s run(%Q{git commit -m "#{message}"}).to_s
end end
def commit_with_gpg(message)
run(%Q{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(which gpg) && git commit -S -m "#{message}"}).to_s
end
def push_changes(branch = 'master') def push_changes(branch = 'master')
run("git push #{uri} #{branch}").to_s run("git push #{uri} #{branch}").to_s
end end
......
...@@ -47,3 +47,5 @@ module QA ...@@ -47,3 +47,5 @@ module QA
end end
end end
end end
QA::Page::Project::Settings::Repository.prepend_if_ee('QA::EE::Page::Project::Settings::Repository')
...@@ -8,9 +8,9 @@ module QA ...@@ -8,9 +8,9 @@ module QA
class Push < Base class Push < Base
attr_accessor :file_name, :file_content, :commit_message, attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :output, :repository_http_uri, :branch_name, :new_branch, :output, :repository_http_uri,
:repository_ssh_uri, :ssh_key, :user, :use_lfs :repository_ssh_uri, :ssh_key, :user, :use_lfs, :tag_name
attr_writer :remote_branch attr_writer :remote_branch, :gpg_key_id
def initialize def initialize
@file_name = 'file.txt' @file_name = 'file.txt'
...@@ -21,6 +21,8 @@ module QA ...@@ -21,6 +21,8 @@ module QA
@repository_http_uri = "" @repository_http_uri = ""
@ssh_key = nil @ssh_key = nil
@use_lfs = false @use_lfs = false
@tag_name = nil
@gpg_key_id = nil
end end
def remote_branch def remote_branch
...@@ -67,29 +69,43 @@ module QA ...@@ -67,29 +69,43 @@ module QA
email = user.email email = user.email
end end
unless @gpg_key_id.nil?
repository.gpg_key_id = @gpg_key_id
end
@output += repository.clone @output += repository.clone
repository.configure_identity(username, email) repository.configure_identity(username, email)
@output += repository.checkout(branch_name, new_branch: new_branch) @output += repository.checkout(branch_name, new_branch: new_branch)
if @directory if @tag_name
@directory.each_child do |f| @output += repository.delete_tag(@tag_name)
@output += repository.add_file(f.basename, f.read) if f.file?
end
elsif @files
@files.each do |f|
repository.add_file(f[:name], f[:content])
end
else else
@output += repository.add_file(file_name, file_content) if @directory
end @directory.each_child do |f|
@output += repository.add_file(f.basename, f.read) if f.file?
end
elsif @files
@files.each do |f|
repository.add_file(f[:name], f[:content])
end
else
@output += repository.add_file(file_name, file_content)
end
@output += repository.commit(commit_message) @output += commit_to repository
@output += repository.push_changes("#{branch_name}:#{remote_branch}") @output += repository.push_changes("#{branch_name}:#{remote_branch}")
end
repository.delete_ssh_key repository.delete_ssh_key
end end
end end
private
def commit_to(repository)
@gpg_key_id.nil? ? repository.commit(@commit_message) : repository.commit_with_gpg(@commit_message)
end
end end
end end
end end
......
# frozen_string_literal: true
module QA
module Resource
class Tag < Base
attr_accessor :project, :name, :ref
def resource_web_url(resource)
super
rescue ResourceURLMissingError
# this particular resource does not expose a web_url property
end
def api_get_path
"/projects/#{project.id}/repository/tags/#{name}"
end
def api_post_path
"/projects/#{project.id}/repository/tags"
end
def api_post_body
{
tag_name: name,
ref: ref
}
end
end
end
end
# frozen_string_literal: true
module QA
module Resource
class UserGPG < Base
attr_accessor :id, :gpg
attr_reader :key_id
def initialize
@gpg = Runtime::GPG.new
@key_id = @gpg.key_id
end
def fabricate_via_api!
super
@id = self.api_response[:id]
rescue ResourceFabricationFailedError => error
if error.message.include? 'has already been taken'
self
else
raise ResourceFabricationFailedError error
end
end
def resource_web_url(resource)
super
rescue ResourceURLMissingError
# this particular resource does not expose a web_url property
end
def api_get_path
"/user/gpg_keys/#{@id}"
end
def api_post_path
'/user/gpg_keys'
end
def api_post_body
{
key: @gpg.key
}
end
end
end
end
# frozen_string_literal: true
module QA
module Runtime
class GPG
attr_reader :key, :key_id
def initialize
@key_id = 'B8358D73048DACC4'
import_key(File.expand_path('qa/ee/fixtures/gpg/admin.asc'))
@key = collect_key.first
end
private
def import_key(path)
import_key = "gpg --import #{path}"
execute(import_key)
end
def collect_key
get_ascii_format = "gpg --armor --export #{@key_id}"
execute(get_ascii_format)
end
def execute(command)
Open3.capture2e(*command) do |stdin, out, wait|
out.each_char { |char| print char }
if wait.value.exited? && wait.value.exitstatus.nonzero?
raise CommandError, "Command `#{command}` failed!"
end
end
end
end
end
end
...@@ -25,6 +25,10 @@ module QA ...@@ -25,6 +25,10 @@ module QA
Runtime::Env.user_password || default_password Runtime::Env.user_password || default_password
end end
def email
default_email
end
def ldap_user? def ldap_user?
Runtime::Env.ldap_username && Runtime::Env.ldap_password Runtime::Env.ldap_username && Runtime::Env.ldap_password
end end
......
# frozen_string_literal: true
module QA
context 'Create' do
context 'Push Rules' do
describe 'using non signed commits' do
file_name_limitation = 'denied_file'
file_size_limitation = 1
authors_email_limitation = '(admin@example.com|root@gitlab.com)'
branch_name_limitation = 'master'
needed_phrase_limitation = 'allowed commit'
deny_message_phrase_limitation = 'denied commit'
before :context do
prepare
Page::Project::Settings::Repository.perform do |repository|
repository.expand_push_rules do |push_rules|
push_rules.fill_file_name file_name_limitation
push_rules.fill_file_size file_size_limitation
push_rules.fill_author_email authors_email_limitation
push_rules.fill_branch_name branch_name_limitation
push_rules.fill_commit_message_rule needed_phrase_limitation
push_rules.fill_deny_commit_message_rule deny_message_phrase_limitation
push_rules.check_prevent_secrets
push_rules.check_restrict_author
push_rules.check_deny_delete_tag
push_rules.click_submit
end
end
end
it 'restricts files by name and size' do
large_file = [{
name: 'file',
content: SecureRandom.hex(1000000)
}]
wrongly_named_file = [{
name: file_name_limitation,
content: SecureRandom.hex(100)
}]
expect_no_error_on_push file: standard_file
expect_error_on_push file: large_file
expect_error_on_push file: wrongly_named_file
end
it 'restricts users by email format' do
gitlab_user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
expect_no_error_on_push file: standard_file
expect_error_on_push file: standard_file, user: gitlab_user
end
it 'restricts branches by branch name' do
expect_no_error_on_push file: standard_file
expect_error_on_push file: standard_file, branch: 'forbidden_branch'
end
it 'restricts commit by message format' do
expect_no_error_on_push file: standard_file, commit_message: needed_phrase_limitation
expect_error_on_push file: standard_file, commit_message: 'forbidden message'
expect_error_on_push file: standard_file, commit_message: "#{needed_phrase_limitation} - #{deny_message_phrase_limitation}"
end
it 'restricts committing files with secrets' do
secret_file = [{
name: 'id_rsa',
content: SecureRandom.hex(100)
}]
expect_no_error_on_push file: standard_file
expect_error_on_push file: secret_file
end
it 'restricts commits by user' do
expect_no_error_on_push file: standard_file
expect_error_on_push file: standard_file, user: root_user
end
it 'restricts removal of tag' do
tag = Resource::Tag.fabricate_via_api! do |tag|
tag.project = @project
tag.ref = 'master'
tag.name = 'test_tag'
end
expect_no_error_on_push file: standard_file
expect_error_on_push file: standard_file, tag: tag.name
end
end
describe 'using signed commits' do
before :context do
prepare
Page::Project::Settings::Repository.perform do |repository|
repository.expand_push_rules do |push_rules|
push_rules.check_reject_unsigned_commits
push_rules.check_committer_restriction
push_rules.click_submit
end
end
@gpg = Resource::UserGPG.fabricate_via_api!
end
it 'restricts to signed commits' do
expect_no_error_on_push file: standard_file, gpg: @gpg
expect_error_on_push file: standard_file
end
it 'restricts commits to current authenticated user' do
gitlab_user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
expect_no_error_on_push file: standard_file, gpg: @gpg
expect_error_on_push file: standard_file, gpg: @gpg, user: gitlab_user
end
end
def standard_file
[{
name: 'file',
content: SecureRandom.hex(100)
}]
end
def root_user
Resource::User.new.tap do |user|
user.username = 'root'
user.name = 'GitLab QA'
user.email = 'root@gitlab.com'
user.password = nil
end
end
def push(commit_message:, branch:, file:, user:, tag:, gpg:)
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = @project
push.commit_message = commit_message
push.new_branch = branch != 'master'
push.branch_name = branch
push.user = user
push.files = file if tag.nil?
push.tag_name = tag unless tag.nil?
push.gpg_key_id = gpg.key_id unless gpg.nil?
end
end
def expect_no_error_on_push(commit_message: 'allowed commit', branch: 'master', file:, user: Runtime::User, tag: nil, gpg: nil)
expect do
push commit_message: commit_message, branch: branch, file: file, user: user, tag: tag, gpg: gpg
end.not_to raise_error
end
def expect_error_on_push(commit_message: 'allowed commit', branch: 'master', file:, user: Runtime::User, tag: nil, gpg: nil)
expect do
push commit_message: commit_message, branch: branch, file: file, user: user, tag: tag, gpg: gpg
end.to raise_error(QA::Git::Repository::RepositoryCommandError)
end
def prepare
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
@project = Resource::Project.fabricate_via_api! do |project|
project.name = 'push_rules'
end
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = @project
push.files = standard_file
end
@project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings)
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