Add e2e test: push mirroring over HTTP
Adds an end-to-end test of push mirroring a repository over HTTP. Includes addition of QA selectors to views
This commit is contained in:
parent
da2da0fa59
commit
1e033f5bd4
10 changed files with 192 additions and 15 deletions
|
@ -8,14 +8,14 @@
|
||||||
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
|
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
|
||||||
= f.select :auth_method,
|
= f.select :auth_method,
|
||||||
options_for_select(auth_options, mirror.auth_method),
|
options_for_select(auth_options, mirror.auth_method),
|
||||||
{}, { class: "form-control js-mirror-auth-type" }
|
{}, { class: "form-control js-mirror-auth-type qa-authentication-method" }
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
.collapse.js-well-changing-auth
|
.collapse.js-well-changing-auth
|
||||||
.changing-auth-method= icon('spinner spin lg')
|
.changing-auth-method= icon('spinner spin lg')
|
||||||
.well-password-auth.collapse.js-well-password-auth
|
.well-password-auth.collapse.js-well-password-auth
|
||||||
= f.label :password, _("Password"), class: "label-bold"
|
= f.label :password, _("Password"), class: "label-bold"
|
||||||
= f.password_field :password, value: mirror.password, class: 'form-control', autocomplete: 'new-password'
|
= f.password_field :password, value: mirror.password, class: 'form-control qa-password', autocomplete: 'new-password'
|
||||||
- unless is_push
|
- unless is_push
|
||||||
.well-ssh-auth.collapse.js-well-ssh-auth
|
.well-ssh-auth.collapse.js-well-ssh-auth
|
||||||
%p.js-ssh-public-key-present{ class: ('collapse' unless ssh_public_key_present) }
|
%p.js-ssh-public-key-present{ class: ('collapse' unless ssh_public_key_present) }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- expanded = Rails.env.test?
|
- expanded = Rails.env.test?
|
||||||
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
|
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
|
||||||
|
|
||||||
%section.settings.project-mirror-settings.js-mirror-settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded) }
|
%section.settings.project-mirror-settings.js-mirror-settings.no-animate.qa-mirroring-repositories-settings#js-push-remote-settings{ class: ('expanded' if expanded) }
|
||||||
.settings-header
|
.settings-header
|
||||||
%h4= _('Mirroring repositories')
|
%h4= _('Mirroring repositories')
|
||||||
%button.btn.js-settings-toggle
|
%button.btn.js-settings-toggle
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
.form-group.has-feedback
|
.form-group.has-feedback
|
||||||
= label_tag :url, _('Git repository URL'), class: 'label-light'
|
= label_tag :url, _('Git repository URL'), class: 'label-light'
|
||||||
= text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
|
= text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url qa-mirror-repository-url-input', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
|
||||||
|
|
||||||
= render 'projects/mirrors/instructions'
|
= render 'projects/mirrors/instructions'
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
|
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
|
||||||
|
|
||||||
.panel-footer
|
.panel-footer
|
||||||
= f.submit _('Mirror repository'), class: 'btn btn-success js-mirror-submit', name: :update_remote_mirror
|
= f.submit _('Mirror repository'), class: 'btn btn-success js-mirror-submit qa-mirror-repository-button', name: :update_remote_mirror
|
||||||
|
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.table-responsive
|
.table-responsive
|
||||||
|
@ -50,10 +50,10 @@
|
||||||
= render_if_exists 'projects/mirrors/table_pull_row'
|
= render_if_exists 'projects/mirrors/table_pull_row'
|
||||||
- @project.remote_mirrors.each_with_index do |mirror, index|
|
- @project.remote_mirrors.each_with_index do |mirror, index|
|
||||||
- if mirror.enabled
|
- if mirror.enabled
|
||||||
%tr
|
%tr.qa-mirrored-repository-row
|
||||||
%td= mirror.safe_url
|
%td.qa-mirror-repository-url= mirror.safe_url
|
||||||
%td= _('Push')
|
%td= _('Push')
|
||||||
%td= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
|
%td.qa-mirror-last-update-at= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
|
||||||
%td
|
%td
|
||||||
- if mirror.last_error.present?
|
- if mirror.last_error.present?
|
||||||
.badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
|
.badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.form-group
|
.form-group
|
||||||
= label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
|
= label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
|
||||||
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control js-mirror-direction', disabled: true
|
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control js-mirror-direction qa-mirror-direction', disabled: true
|
||||||
|
|
||||||
= render partial: "projects/mirrors/mirror_repos_push", locals: { f: f }
|
= render partial: "projects/mirrors/mirror_repos_push", locals: { f: f }
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
%button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') }
|
%button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') }
|
||||||
= icon("refresh spin")
|
= icon("refresh spin")
|
||||||
- else
|
- else
|
||||||
= link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do
|
= link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn qa-update-now-button", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do
|
||||||
= icon("refresh")
|
= icon("refresh")
|
||||||
|
|
1
qa/qa.rb
1
qa/qa.rb
|
@ -184,6 +184,7 @@ module QA
|
||||||
autoload :Runners, 'qa/page/project/settings/runners'
|
autoload :Runners, 'qa/page/project/settings/runners'
|
||||||
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
|
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
|
||||||
autoload :Members, 'qa/page/project/settings/members'
|
autoload :Members, 'qa/page/project/settings/members'
|
||||||
|
autoload :MirroringRepositories, 'qa/page/project/settings/mirroring_repositories'
|
||||||
end
|
end
|
||||||
|
|
||||||
module Issue
|
module Issue
|
||||||
|
|
|
@ -15,7 +15,7 @@ module QA
|
||||||
def_delegators :evaluator, :view, :views
|
def_delegators :evaluator, :view, :views
|
||||||
|
|
||||||
def refresh
|
def refresh
|
||||||
visit current_url
|
page.refresh
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait(max: 60, time: 0.1, reload: true)
|
def wait(max: 60, time: 0.1, reload: true)
|
||||||
|
@ -80,8 +80,8 @@ module QA
|
||||||
page.evaluate_script('xhr.status') == 200
|
page.evaluate_script('xhr.status') == 200
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_element(name)
|
def find_element(name, wait: Capybara.default_max_wait_time)
|
||||||
find(element_selector_css(name))
|
find(element_selector_css(name), wait: wait)
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_elements(name)
|
def all_elements(name)
|
||||||
|
@ -100,6 +100,14 @@ module QA
|
||||||
find_element(name).set(content)
|
find_element(name).set(content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def select_element(name, value)
|
||||||
|
element = find_element(name)
|
||||||
|
|
||||||
|
return if element.text.downcase.to_s == value.to_s
|
||||||
|
|
||||||
|
element.select value.to_s.capitalize
|
||||||
|
end
|
||||||
|
|
||||||
def has_element?(name)
|
def has_element?(name)
|
||||||
has_css?(element_selector_css(name))
|
has_css?(element_selector_css(name))
|
||||||
end
|
end
|
||||||
|
@ -110,6 +118,12 @@ module QA
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def within_element_by_index(name, index)
|
||||||
|
page.within all_elements(name)[index] do
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def scroll_to_element(name, *args)
|
def scroll_to_element(name, *args)
|
||||||
scroll_to(element_selector_css(name), *args)
|
scroll_to(element_selector_css(name), *args)
|
||||||
end
|
end
|
||||||
|
|
91
qa/qa/page/project/settings/mirroring_repositories.rb
Normal file
91
qa/qa/page/project/settings/mirroring_repositories.rb
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Page
|
||||||
|
module Project
|
||||||
|
module Settings
|
||||||
|
class MirroringRepositories < Page::Base
|
||||||
|
view 'app/views/projects/mirrors/_authentication_method.html.haml' do
|
||||||
|
element :authentication_method
|
||||||
|
element :password
|
||||||
|
end
|
||||||
|
|
||||||
|
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
|
||||||
|
element :mirror_repository_url_input
|
||||||
|
element :mirror_repository_button
|
||||||
|
element :mirror_repository_url
|
||||||
|
element :mirror_last_update_at
|
||||||
|
element :mirrored_repository_row
|
||||||
|
end
|
||||||
|
|
||||||
|
view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do
|
||||||
|
element :mirror_direction
|
||||||
|
end
|
||||||
|
|
||||||
|
view 'app/views/shared/_remote_mirror_update_button.html.haml' do
|
||||||
|
element :update_now_button
|
||||||
|
end
|
||||||
|
|
||||||
|
def repository_url=(value)
|
||||||
|
fill_element :mirror_repository_url_input, value
|
||||||
|
end
|
||||||
|
|
||||||
|
def password=(value)
|
||||||
|
fill_element :password, value
|
||||||
|
end
|
||||||
|
|
||||||
|
def mirror_direction=(value)
|
||||||
|
raise ArgumentError, "Mirror direction must be :push or :pull" unless [:push, :pull].include? value
|
||||||
|
|
||||||
|
select_element(:mirror_direction, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authentication_method=(value)
|
||||||
|
raise ArgumentError, "Authentication method must be :password or :none" unless [:password, :none].include? value
|
||||||
|
|
||||||
|
select_element(:authentication_method, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mirror_repository
|
||||||
|
click_element :mirror_repository_button
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(url)
|
||||||
|
row_index = find_repository_row_index url
|
||||||
|
|
||||||
|
within_element_by_index(:mirrored_repository_row, row_index) do
|
||||||
|
click_element :update_now_button
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wait a few seconds for the sync to occur and then refresh the page
|
||||||
|
# so that 'last update' shows 'just now' or a period in seconds
|
||||||
|
sleep 5
|
||||||
|
refresh
|
||||||
|
|
||||||
|
wait(time: 1) do
|
||||||
|
within_element_by_index(:mirrored_repository_row, row_index) do
|
||||||
|
last_update = find_element(:mirror_last_update_at, wait: 0)
|
||||||
|
last_update.has_text?('just now') || last_update.has_text?('seconds')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fail early if the page still shows that there has been no update
|
||||||
|
within_element_by_index(:mirrored_repository_row, row_index) do
|
||||||
|
find_element(:mirror_last_update_at, wait: 0).assert_no_text('Never')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_repository_row_index(target_url)
|
||||||
|
all_elements(:mirror_repository_url).index do |url|
|
||||||
|
# The url might be a sanitized url but the target_url won't be so
|
||||||
|
# we compare just the paths instead of the full url
|
||||||
|
URI.parse(url.text).path == target_url.path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,6 +13,10 @@ module QA
|
||||||
element :protected_branches_settings
|
element :protected_branches_settings
|
||||||
end
|
end
|
||||||
|
|
||||||
|
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
|
||||||
|
element :mirroring_repositories_settings
|
||||||
|
end
|
||||||
|
|
||||||
def expand_deploy_keys(&block)
|
def expand_deploy_keys(&block)
|
||||||
expand_section(:deploy_keys_settings) do
|
expand_section(:deploy_keys_settings) do
|
||||||
DeployKeys.perform(&block)
|
DeployKeys.perform(&block)
|
||||||
|
@ -30,6 +34,12 @@ module QA
|
||||||
DeployTokens.perform(&block)
|
DeployTokens.perform(&block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expand_mirroring_repositories(&block)
|
||||||
|
expand_section(:mirroring_repositories_settings) do
|
||||||
|
MirroringRepositories.perform(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
context 'Create' do
|
||||||
|
describe 'Push mirror a repository over HTTP' do
|
||||||
|
it 'configures and syncs a (push) mirrored repository' do
|
||||||
|
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||||
|
Page::Main::Login.perform(&:sign_in_using_credentials)
|
||||||
|
|
||||||
|
target_project = Resource::Project.fabricate! do |project|
|
||||||
|
project.name = 'push-mirror-target-project'
|
||||||
|
end
|
||||||
|
target_project_uri = target_project.repository_http_location.uri
|
||||||
|
target_project_uri.user = Runtime::User.username
|
||||||
|
|
||||||
|
source_project_push = Resource::Repository::ProjectPush.fabricate! do |push|
|
||||||
|
push.file_name = 'README.md'
|
||||||
|
push.file_content = '# This is a test project'
|
||||||
|
push.commit_message = 'Add README.md'
|
||||||
|
end
|
||||||
|
source_project_push.project.visit!
|
||||||
|
|
||||||
|
Page::Project::Show.perform(&:wait_for_push)
|
||||||
|
|
||||||
|
Page::Project::Menu.perform(&:click_repository_settings)
|
||||||
|
Page::Project::Settings::Repository.perform do |settings|
|
||||||
|
settings.expand_mirroring_repositories do |mirror_settings|
|
||||||
|
# Configure the source project to push to the target project
|
||||||
|
mirror_settings.repository_url = target_project_uri
|
||||||
|
mirror_settings.mirror_direction = :push
|
||||||
|
mirror_settings.authentication_method = :password
|
||||||
|
mirror_settings.password = Runtime::User.password
|
||||||
|
mirror_settings.mirror_repository
|
||||||
|
mirror_settings.update target_project_uri
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check that the target project has the commit from the source
|
||||||
|
target_project.visit!
|
||||||
|
expect(page).to have_content('README.md')
|
||||||
|
expect(page).to have_content('This is a test project')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -37,8 +37,8 @@ module QA
|
||||||
exists
|
exists
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_element(name)
|
def find_element(name, wait: Capybara.default_max_wait_time)
|
||||||
log("finding :#{name}")
|
log("finding :#{name} (wait: #{wait})")
|
||||||
|
|
||||||
element = super
|
element = super
|
||||||
|
|
||||||
|
@ -71,6 +71,12 @@ module QA
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def select_element(name, value)
|
||||||
|
log(%Q(selecting "#{value}" in :#{name}))
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
def has_element?(name)
|
def has_element?(name)
|
||||||
found = super
|
found = super
|
||||||
|
|
||||||
|
@ -89,6 +95,16 @@ module QA
|
||||||
element
|
element
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def within_element_by_index(name, index)
|
||||||
|
log("within elements :#{name} at index #{index}")
|
||||||
|
|
||||||
|
element = super
|
||||||
|
|
||||||
|
log("end within elements :#{name} at index #{index}")
|
||||||
|
|
||||||
|
element
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def log(msg)
|
def log(msg)
|
||||||
|
|
Loading…
Reference in a new issue