Commit ba067de6 authored by Tomislav Nikic's avatar Tomislav Nikic Committed by Mark Lapierre

File Locking test

Created a file locking test and added all the needed page objects
and selectors.
parent a874e8f0
......@@ -57,6 +57,7 @@ module EE
html_options['data-toggle'] = 'tooltip'
html_options[:title] = title
html_options[:class] = "#{html_options[:class]} disabled has-tooltip"
html_options['data-qa-selector'] = 'disabled_lock_button'
content_tag :span, label, html_options
end
......@@ -65,6 +66,7 @@ module EE
html_options['data-toggle'] = 'tooltip'
html_options[:title] = title
html_options[:class] = "#{html_options[:class]} has-tooltip"
html_options['data-qa-selector'] = 'lock_button'
link_to label, '#', html_options
end
......
%li
%li{ data: { qa_selector: 'locked_file_content' } }
%div
%span.item-title
%span.item-title{ data: { qa_selector: 'locked_file_title_content' } }
= icon('lock')
= path_lock.path
.controls
- if can_unlock?(path_lock)
= link_to project_path_lock_path(@project, path_lock), class: 'btn btn-grouped btn-sm btn-remove remove-row has-tooltip', title: _("Unlock"), method: :delete, data: { confirm: _("Are you sure you want to unlock %{path_lock_path}?") % { path_lock_path: path_lock.path }, container: 'body' }, remote: true do
= link_to project_path_lock_path(@project, path_lock), class: 'btn btn-grouped btn-sm btn-remove remove-row has-tooltip', title: _("Unlock"), method: :delete, data: { confirm: _("Are you sure you want to unlock %{path_lock_path}?") % { path_lock_path: path_lock.path }, container: 'body', qa_selector: 'unlock_button' }, remote: true do
= icon("trash-o")
= _("locked by %{path_lock_user_name} %{created_at}").html_safe % { path_lock_user_name: path_lock.user.name, created_at: time_ago_with_tooltip(path_lock.created_at) }
- return unless @project.feature_available?(:file_locks)
= nav_link(controller: [:path_locks]) do
= link_to project_path_locks_path(@project) do
= link_to project_path_locks_path(@project), data: { qa_selector: 'path_locks_link' } do
= _('Locked Files')
......@@ -66,6 +66,7 @@ module QA
autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key'
autoload :Snippet, 'qa/resource/snippet'
autoload :ProjectMember, 'qa/resource/project_member'
module Events
autoload :Base, 'qa/resource/events/base'
......
......@@ -82,6 +82,7 @@ module QA
module SubMenus
autoload :SecurityCompliance, 'qa/ee/page/project/sub_menus/security_compliance'
autoload :Repository, 'qa/ee/page/project/sub_menus/repository'
end
module Issue
......@@ -122,6 +123,10 @@ module QA
autoload :Show, 'qa/ee/page/project/secure/show'
autoload :DependencyList, 'qa/ee/page/project/secure/dependency_list'
end
module PathLocks
autoload :Index, 'qa/ee/page/project/path_locks/index'
end
end
module MergeRequest
......
......@@ -11,8 +11,37 @@ module QA
element :file_owner_content
element :link_file_owner
end
view 'ee/app/helpers/ee/lock_helper.rb' do
element :lock_button
element :disabled_lock_button
end
end
end
def lock
click_element :lock_button
begin
has_element? :lock_button, text: 'Unlock'
rescue
raise ElementNotFound, %q(Button did not show expected state)
end
end
def unlock
click_element :lock_button
begin
has_element? :lock_button, text: 'Lock'
rescue
raise ElementNotFound, %q(Button did not show expected state)
end
end
def has_lock_button_disabled?
has_element? :disabled_lock_button
end
end
end
end
......
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module PathLocks
class Index < QA::Page::Base
view 'ee/app/views/projects/path_locks/_path_lock.html.haml' do
element :locked_file_content
element :locked_file_title_content
element :unlock_button
end
def has_file_with_title?(file_title)
has_element? :locked_file_title_content, text: file_title
end
def unlock_file(file_title)
within_element :locked_file_content, text: file_title do
click_element :unlock_button
page.accept_alert 'Are you sure you want to unlock file?'
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module SubMenus
module Repository
def self.included(page)
page.class_eval do
view 'ee/app/views/projects/sidebar/_repository_locked_files.html.haml' do
element :path_locks_link
end
end
end
def go_to_repository_locked_files
hover_repository do
within_submenu do
click_element :path_locks_link
end
end
end
end
end
end
end
end
end
......@@ -85,6 +85,8 @@ module QA
end
def add_file(name, contents)
FileUtils.mkdir_p(::File.dirname(name))
::File.write(name, contents)
if use_lfs?
......
......@@ -148,6 +148,12 @@ module QA
click_element :saml_login_button
end
def sign_out_and_sign_in_as(user:)
Menu.perform(&:sign_out)
has_sign_in_tab?
sign_in_using_credentials(user)
end
private
def sign_in_using_gitlab_credentials(user)
......
......@@ -119,7 +119,7 @@ module QA
has_element?(:description, text: description)
end
def merge!
def try_to_merge!
# The merge button is disabled on load
wait do
has_element?(:merge_button)
......@@ -131,6 +131,10 @@ module QA
end
merge_immediately
end
def merge!
try_to_merge!
success = wait do
has_text?('The changes were merged into')
......
......@@ -44,3 +44,5 @@ module QA
end
end
end
QA::Page::Project::SubMenus::Repository.prepend_if_ee('QA::EE::Page::Project::SubMenus::Repository')
......@@ -11,6 +11,7 @@ module QA
ResourceNotFoundError = Class.new(RuntimeError)
ResourceFabricationFailedError = Class.new(RuntimeError)
ResourceURLMissingError = Class.new(RuntimeError)
ResourceNotDeletedError = Class.new(RuntimeError)
attr_reader :api_resource, :api_response
attr_writer :api_client
......@@ -30,6 +31,10 @@ module QA
resource_web_url(api_post)
end
def remove_via_api!
api_delete
end
def eager_load_api_client!
return unless api_client.nil?
......@@ -79,6 +84,17 @@ module QA
process_api_response(parse_body(response))
end
def api_delete
url = Runtime::API::Request.new(api_client, api_delete_path).url
response = delete(url)
unless response.code == HTTP_STATUS_NO_CONTENT
raise ResourceNotDeletedError, "Resource at #{url} could not be deleted (#{response.code}): `#{response}`."
end
response
end
def api_client
@api_client ||= begin
Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: user)
......
......@@ -47,6 +47,18 @@ module QA
end
end
def self.remove_via_api!(*args, &prepare_block)
options = args.extract_options!
resource = options.fetch(:resource) { new }
parents = options.fetch(:parents) { [] }
resource.eager_load_api_client!
do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
log_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
end
end
def fabricate!(*_args)
raise NotImplementedError
end
......
......@@ -72,6 +72,18 @@ module QA
end
end
end
def self.unprotect_via_api!(&block)
self.remove_via_api!(&block)
end
def api_get_path
"/projects/#{@project.api_resource[:id]}/protected_branches/#{@branch_name}"
end
def api_delete_path
"/projects/#{@project.api_resource[:id]}/protected_branches/#{@branch_name}"
end
end
end
end
......@@ -17,6 +17,7 @@ module QA
:labels,
:file_name,
:file_content
attr_writer :no_preparation
attribute :project do
Project.fabricate! do |resource|
......@@ -58,6 +59,7 @@ module QA
@file_name = "added_file.txt"
@file_content = "File Added"
@target_new_branch = true
@no_preparation = false
end
def fabricate!
......@@ -80,7 +82,7 @@ module QA
end
def fabricate_via_api!
populate(:target, :source)
populate(:target, :source) unless @no_preparation
super
end
......
# frozen_string_literal: true
module QA
module Resource
class ProjectMember < Base
attr_accessor :user, :project, :access_level
attr_reader :level
def initialize
@level = {
guest: 10,
reporter: 20,
developer: 30,
maintainer: 40,
owner: 50
}
end
def api_get_path
"/projects/#{project.api_resource[:id]}/members/#{user.api_resource[:id]}"
end
def api_post_path
"/projects/#{project.api_resource[:id]}/members"
end
def api_post_body
{
user_id: user.api_resource[:id],
access_level: access_level
}
end
end
end
end
# frozen_string_literal: true
module QA
context 'Create' do
describe 'File Locking' do
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
@user_one = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
@user_two = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2)
@project = Resource::Project.fabricate_via_api! do |project|
project.name = 'file_locking'
end
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = @project
push.file_name = 'file'
push.file_content = SecureRandom.hex(100000)
end
add_to_project user: @user_one
add_to_project user: @user_two
Resource::Branch.unprotect_via_api! do |branch|
branch.project = @project
branch.branch_name = 'master'
end
end
it 'locks a directory and tries to push as a second user' do
push branch: 'master', file: 'directory/file', as_user: @user_one
sign_out_and_sign_in_as user: @user_one
go_to_directory
click_lock
expect_error_on_push for_file: 'directory/file', as_user: @user_two
expect_no_error_on_push for_file: 'directory/file', as_user: @user_one
end
it 'locks a file and tries to push as a second user' do
sign_out_and_sign_in_as user: @user_one
go_to_file
click_lock
expect_error_on_push as_user: @user_two
expect_no_error_on_push as_user: @user_one
end
it 'checks file locked by other user to be disabled' do
go_to_file
click_lock
sign_out_and_sign_in_as user: @user_one
go_to_file
Page::File::Show.perform do |show|
expect(show).to have_lock_button_disabled
end
end
it 'creates a merge request and fails to merge' do
push branch: 'test', as_user: @user_one
merge_request = Resource::MergeRequest.fabricate_via_api! do |merge_request|
merge_request.project = @project
merge_request.source_branch = 'test'
merge_request.target_branch = 'master'
merge_request.no_preparation = true
end
go_to_file
click_lock
sign_out_and_sign_in_as user: @user_one
try_to_merge merge_request: merge_request
expect(page).to have_text("locked by Administrator")
end
it 'locks a file and unlocks in list' do
sign_out_and_sign_in_as user: @user_one
go_to_file
click_lock
@project.visit!
Page::Project::Menu.perform(&:go_to_repository_locked_files)
EE::Page::Project::PathLocks::Index.perform do |list|
expect(list).to have_file_with_title 'file'
list.unlock_file 'file'
end
expect_no_error_on_push as_user: @user_two
end
def try_to_merge(merge_request:)
merge_request.visit!
Page::MergeRequest::Show.perform do |show|
show.try_to_merge!
end
end
def sign_out_and_sign_in_as(user:)
Page::Main::Login.perform do |login|
login.sign_out_and_sign_in_as user: user
end
end
def go_to_file
@project.visit!
Page::Project::Show.perform do |project_page|
project_page.click_file 'file'
end
end
def go_to_directory
@project.visit!
Page::Project::Show.perform do |project_page|
project_page.click_file 'directory'
end
end
def click_lock
Page::File::Show.perform(&:lock)
end
def add_to_project(user:)
Resource::ProjectMember.fabricate_via_api! do |member|
member.user = user
member.project = @project
member.access_level = member.level[:developer]
end
end
def push(branch: 'master', file: 'file', as_user:)
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = @project
push.new_branch = false unless branch != 'master'
push.file_name = file
push.file_content = SecureRandom.hex(100000)
push.user = as_user
push.branch_name = branch
end
end
def expect_error_on_push(for_file: 'file', as_user:)
expect { push branch: 'master', file: for_file, as_user: as_user }.to raise_error(
QA::Git::Repository::RepositoryCommandError)
end
def expect_no_error_on_push(for_file: 'file', as_user:)
expect { push branch: 'master', file: for_file, as_user: as_user }.not_to raise_error
end
end
end
end
......@@ -5,6 +5,7 @@ module QA
module Api
HTTP_STATUS_OK = 200
HTTP_STATUS_CREATED = 201
HTTP_STATUS_NO_CONTENT = 204
def post(url, payload)
RestClient::Request.execute(
......
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