Commit bcbeeaa2 authored by Mark Lapierre's avatar Mark Lapierre

Add e2e test SSH pull mirroring with private key

Includes required QA selectors

Changes mirror direction and auth method selection to use
strings rather than symbols (which could not easily allow
"SSH public key" to be selected if specified as a symbol)
parent 3873a8a1
- expanded = expanded_by_default? - expanded = expanded_by_default?
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|') - protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
%section.settings.project-mirror-settings.js-mirror-settings.no-animate.qa-mirroring-repositories-settings#js-push-remote-settings{ class: ('expanded' if expanded) } %section.settings.project-mirror-settings.js-mirror-settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded), data: { qa_selector: 'mirroring_repositories_settings_section' } }
.settings-header .settings-header
%h4= _('Mirroring repositories') %h4= _('Mirroring repositories')
%button.btn.js-settings-toggle %button.btn.js-settings-toggle
...@@ -59,10 +59,10 @@ ...@@ -59,10 +59,10 @@
- if mirror.disabled? - if mirror.disabled?
= render 'projects/mirrors/disabled_mirror_badge' = render 'projects/mirrors/disabled_mirror_badge'
- 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', qa_selector: 'mirror_error_badge' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
%td %td
.btn-group.mirror-actions-group.pull-right{ role: 'group' } .btn-group.mirror-actions-group.pull-right{ role: 'group' }
- if mirror.ssh_key_auth? - if mirror.ssh_key_auth?
= clipboard_button(text: mirror.ssh_public_key, class: 'btn btn-default', title: _('Copy SSH public key')) = clipboard_button(text: mirror.ssh_public_key, class: 'btn btn-default', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button')
= render 'shared/remote_mirror_update_button', remote_mirror: mirror = render 'shared/remote_mirror_update_button', remote_mirror: mirror
%button.js-delete-mirror.qa-delete-mirror.rspec-delete-mirror.btn.btn-danger{ type: 'button', data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' }, title: _('Remove') }= icon('trash-o') %button.js-delete-mirror.qa-delete-mirror.rspec-delete-mirror.btn.btn-danger{ type: 'button', data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' }, title: _('Remove') }= icon('trash-o')
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
- verified_at = mirror.ssh_known_hosts_verified_at - verified_at = mirror.ssh_known_hosts_verified_at
.form-group.js-ssh-host-keys-section{ class: ('collapse' unless mirror.ssh_mirror_url?) } .form-group.js-ssh-host-keys-section{ class: ('collapse' unless mirror.ssh_mirror_url?) }
%button.btn.btn-inverted.btn-secondary.inline.js-detect-host-keys.append-right-10{ type: 'button' } %button.btn.btn-inverted.btn-secondary.inline.js-detect-host-keys.append-right-10{ type: 'button', data: { qa_selector: 'detect_host_keys' } }
= icon('spinner spin', class: 'js-spinner d-none') = icon('spinner spin', class: 'js-spinner d-none')
= _('Detect host keys') = _('Detect host keys')
.fingerprint-ssh-info.js-fingerprint-ssh-info.prepend-top-10.append-bottom-10{ class: ('collapse' unless mirror.ssh_mirror_url?) } .fingerprint-ssh-info.js-fingerprint-ssh-info.prepend-top-10.append-bottom-10{ class: ('collapse' unless mirror.ssh_mirror_url?) }
%label.label-bold %label.label-bold
= _('Fingerprints') = _('Fingerprints')
.fingerprints-list.js-fingerprints-list .fingerprints-list.js-fingerprints-list{ data: { qa_selector: 'fingerprints_list' } }
- mirror.ssh_known_hosts_fingerprints.each do |fp| - mirror.ssh_known_hosts_fingerprints.each do |fp|
%code= fp.fingerprint %code= fp.fingerprint
- if verified_at - if verified_at
......
- if remote_mirror.update_in_progress? - if remote_mirror.update_in_progress?
%button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') } %button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body', qa_selector: 'updating_button' }, title: _('Updating') }
= icon("refresh spin") = icon("refresh spin")
- elsif remote_mirror.enabled? - elsif remote_mirror.enabled?
= link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn qa-update-now-button rspec-update-now-button", 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 rspec-update-now-button", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do
......
...@@ -19,9 +19,9 @@ ...@@ -19,9 +19,9 @@
.btn-group.mirror-actions-group.pull-right{ role: 'group' } .btn-group.mirror-actions-group.pull-right{ role: 'group' }
- ssh_public_key = @project.import_data.ssh_public_key - ssh_public_key = @project.import_data.ssh_public_key
- if ssh_public_key - if ssh_public_key
= clipboard_button(text: ssh_public_key, class: 'btn btn-default qa-copy-ssh-public-key', title: _('Copy SSH public key')) = clipboard_button(text: ssh_public_key, class: 'btn btn-default rspec-copy-ssh-public-key', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button')
- if import_state.mirror_update_due? || import_state.updating_mirror? - if import_state.mirror_update_due? || import_state.updating_mirror?
%button.btn.disabled{ type: 'button', data: { container: 'body', toggle: 'tooltip' }, title: _('Updating') }= icon("refresh spin") %button.btn.disabled{ type: 'button', data: { container: 'body', toggle: 'tooltip', qa_selector: 'updating_button' }, title: _('Updating') }= icon("refresh spin")
- else - else
= link_to update_now_project_mirror_path(@project), method: :post, class: 'btn js-force-update-mirror', data: { container: 'body', toggle: 'tooltip', qa_selector: 'update_now_button' }, title: _('Update now') do = link_to update_now_project_mirror_path(@project), method: :post, class: 'btn js-force-update-mirror', data: { container: 'body', toggle: 'tooltip', qa_selector: 'update_now_button' }, title: _('Update now') do
= icon("refresh") = icon("refresh")
......
...@@ -148,7 +148,7 @@ describe 'Project mirror', :js do ...@@ -148,7 +148,7 @@ describe 'Project mirror', :js do
expect(page).to have_content('Mirroring settings were successfully updated') expect(page).to have_content('Mirroring settings were successfully updated')
expect(page).not_to have_content('Verified by') expect(page).not_to have_content('Verified by')
expect(find('.qa-copy-ssh-public-key')['data-clipboard-text']).to eq(import_data.ssh_public_key) expect(find('.rspec-copy-ssh-public-key')['data-clipboard-text']).to eq(import_data.ssh_public_key)
expect(project.mirror?).to be_truthy expect(project.mirror?).to be_truthy
expect(project.username_only_import_url).to eq('ssh://user@example.com') expect(project.username_only_import_url).to eq('ssh://user@example.com')
expect(import_data.auth_method).to eq('ssh_public_key') expect(import_data.auth_method).to eq('ssh_public_key')
......
...@@ -17,6 +17,8 @@ module QA ...@@ -17,6 +17,8 @@ module QA
element :mirror_repository_url_cell element :mirror_repository_url_cell
element :mirrored_repository_row element :mirrored_repository_row
element :update_now_button element :update_now_button
element :updating_button
element :copy_public_key_button
end end
end end
end end
......
...@@ -15,6 +15,10 @@ module QA ...@@ -15,6 +15,10 @@ module QA
def_delegators :evaluator, :view, :views def_delegators :evaluator, :view, :views
def assert_no_element(name)
assert_no_selector(element_selector_css(name))
end
def refresh def refresh
page.refresh page.refresh
end end
...@@ -102,9 +106,9 @@ module QA ...@@ -102,9 +106,9 @@ module QA
def select_element(name, value) def select_element(name, value)
element = find_element(name) element = find_element(name)
return if element.text.downcase.to_s == value.to_s return if element.text == value
element.select value.to_s.capitalize element.select value
end end
def has_element?(name, text: nil, wait: Capybara.default_max_wait_time) def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
......
...@@ -15,7 +15,9 @@ module QA ...@@ -15,7 +15,9 @@ module QA
element :mirror_repository_button element :mirror_repository_button
element :mirror_repository_url_cell element :mirror_repository_url_cell
element :mirror_last_update_at_cell element :mirror_last_update_at_cell
element :mirror_error_badge
element :mirrored_repository_row element :mirrored_repository_row
element :copy_public_key_button
end end
view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do
...@@ -24,6 +26,17 @@ module QA ...@@ -24,6 +26,17 @@ module QA
view 'app/views/shared/_remote_mirror_update_button.html.haml' do view 'app/views/shared/_remote_mirror_update_button.html.haml' do
element :update_now_button element :update_now_button
element :updating_button
end
view 'app/views/projects/mirrors/_ssh_host_keys.html.haml' do
element :detect_host_keys
element :fingerprints_list
end
view 'app/views/projects/mirrors/_authentication_method.html.haml' do
element :authentication_method
element :password
end end
def repository_url=(value) def repository_url=(value)
...@@ -35,17 +48,40 @@ module QA ...@@ -35,17 +48,40 @@ module QA
end end
def mirror_direction=(value) def mirror_direction=(value)
raise ArgumentError, "Mirror direction must be :push or :pull" unless [:push, :pull].include? value raise ArgumentError, "Mirror direction must be 'Push' or 'Pull'" unless %w(Push Pull).include? value
select_element(:mirror_direction, value) select_element(:mirror_direction, value)
# Changing the mirror direction causes the fields below to change,
# and that change is animated, so we need to wait for the animation
# to complete otherwise changes to those fields could fail
wait_for_animated_element :authentication_method
end end
def authentication_method=(value) def authentication_method=(value)
raise ArgumentError, "Authentication method must be :password or :none" unless [:password, :none].include? value raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'" unless %w(Password None SSH\ public\ key).include? value
select_element(:authentication_method, value) select_element(:authentication_method, value)
end end
def public_key(url)
row_index = find_repository_row_index url
within_element_by_index(:mirrored_repository_row, row_index) do
find_element(:copy_public_key_button)['data-clipboard-text']
end
end
def detect_host_keys
click_element :detect_host_keys
# The host key detection process is interrupted if we navigate away
# from the page before the fingerprint appears.
wait(max: 5) do
find_element(:fingerprints_list).has_text? /.*/
end
end
def mirror_repository def mirror_repository
click_element :mirror_repository_button click_element :mirror_repository_button
end end
...@@ -54,7 +90,9 @@ module QA ...@@ -54,7 +90,9 @@ module QA
row_index = find_repository_row_index url row_index = find_repository_row_index url
within_element_by_index(:mirrored_repository_row, row_index) do within_element_by_index(:mirrored_repository_row, row_index) do
click_element :update_now_button # When a repository is first mirrored, the update process might
# already be started, so the button is already "clicked"
click_element :update_now_button unless has_element? :updating_button
end end
# Wait a few seconds for the sync to occur and then refresh the page # Wait a few seconds for the sync to occur and then refresh the page
...@@ -72,16 +110,19 @@ module QA ...@@ -72,16 +110,19 @@ module QA
# Fail early if the page still shows that there has been no update # Fail early if the page still shows that there has been no update
within_element_by_index(:mirrored_repository_row, row_index) do within_element_by_index(:mirrored_repository_row, row_index) do
find_element(:mirror_last_update_at_cell, wait: 0).assert_no_text('Never') find_element(:mirror_last_update_at_cell, wait: 0).assert_no_text('Never')
assert_no_element(:mirror_error_badge)
end end
end end
private private
def find_repository_row_index(target_url) def find_repository_row_index(target_url)
all_elements(:mirror_repository_url_cell).index do |url| wait(max: 5, reload: false) do
# The url might be a sanitized url but the target_url won't be so all_elements(:mirror_repository_url_cell).index do |url|
# we compare just the paths instead of the full url # The url might be a sanitized url but the target_url won't be so
URI.parse(url.text).path == target_url.path # 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
......
...@@ -16,7 +16,7 @@ module QA ...@@ -16,7 +16,7 @@ module QA
end end
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
element :mirroring_repositories_settings element :mirroring_repositories_settings_section
end end
def expand_deploy_keys(&block) def expand_deploy_keys(&block)
...@@ -38,7 +38,7 @@ module QA ...@@ -38,7 +38,7 @@ module QA
end end
def expand_mirroring_repositories(&block) def expand_mirroring_repositories(&block)
expand_section(:mirroring_repositories_settings) do expand_section(:mirroring_repositories_settings_section) do
MirroringRepositories.perform(&block) MirroringRepositories.perform(&block)
end end
end end
......
...@@ -25,8 +25,8 @@ module QA ...@@ -25,8 +25,8 @@ module QA
settings.expand_mirroring_repositories do |mirror_settings| settings.expand_mirroring_repositories do |mirror_settings|
# Configure the source project to push to the target project # Configure the source project to push to the target project
mirror_settings.repository_url = target_project_uri mirror_settings.repository_url = target_project_uri
mirror_settings.mirror_direction = :push mirror_settings.mirror_direction = 'Push'
mirror_settings.authentication_method = :password mirror_settings.authentication_method = 'Password'
mirror_settings.password = Runtime::User.password mirror_settings.password = Runtime::User.password
mirror_settings.mirror_repository mirror_settings.mirror_repository
mirror_settings.update target_project_uri mirror_settings.update target_project_uri
......
...@@ -70,7 +70,7 @@ module QA ...@@ -70,7 +70,7 @@ module QA
Page::Project::Settings::Main.perform do |settings| Page::Project::Settings::Main.perform do |settings|
# Change visibility from public to internal # Change visibility from public to internal
settings.expand_visibility_project_features_permissions do |page| settings.expand_visibility_project_features_permissions do |page|
page.set_project_visibility "internal" page.set_project_visibility "Internal"
end end
end end
end end
......
...@@ -26,8 +26,8 @@ module QA ...@@ -26,8 +26,8 @@ module QA
settings.expand_mirroring_repositories do |mirror_settings| settings.expand_mirroring_repositories do |mirror_settings|
# Configure the target project to pull from the source project # Configure the target project to pull from the source project
mirror_settings.repository_url = source_project_uri mirror_settings.repository_url = source_project_uri
mirror_settings.mirror_direction = :pull mirror_settings.mirror_direction = 'Pull'
mirror_settings.authentication_method = :password mirror_settings.authentication_method = 'Password'
mirror_settings.password = Runtime::User.password mirror_settings.password = Runtime::User.password
mirror_settings.mirror_repository mirror_settings.mirror_repository
mirror_settings.update source_project_uri mirror_settings.update source_project_uri
......
# frozen_string_literal: true
module QA
context 'Create' do
describe 'Pull mirror a repository over SSH with a private key' do
let(:source) do
Resource::Repository::ProjectPush.fabricate! do |project_push|
project_push.project_name = 'pull-mirror-source-project'
project_push.file_name = 'README.md'
project_push.file_content = '# This is a pull mirroring test project'
project_push.commit_message = 'Add README.md'
end
end
let(:source_project_uri) { source.project.repository_ssh_location.uri }
let(:target_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'pull-mirror-target-project'
end
end
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
target_project.visit!
end
it 'configures and syncs a (pull) mirrored repository' do
# Configure the target project to pull from the source project
# And get the public key to be used as a deploy key
Page::Project::Menu.perform(&:go_to_repository_settings)
public_key = Page::Project::Settings::Repository.perform do |settings|
settings.expand_mirroring_repositories do |mirror_settings|
mirror_settings.repository_url = source_project_uri
mirror_settings.mirror_direction = 'Pull'
mirror_settings.authentication_method = 'SSH public key'
mirror_settings.detect_host_keys
mirror_settings.mirror_repository
mirror_settings.public_key source_project_uri
end
end
# Add the public key to the source project as a deploy key
Resource::DeployKey.fabricate! do |deploy_key|
deploy_key.project = source.project
deploy_key.title = "pull mirror key #{Time.now.to_f}"
deploy_key.key = public_key
end
# Sync the repositories
target_project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings)
Page::Project::Settings::Repository.perform do |settings|
settings.expand_mirroring_repositories do |mirror_settings|
mirror_settings.update source_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 pull mirroring test project')
expect(page).to have_content("Mirrored from #{masked_url(source_project_uri)}")
end
def masked_url(url)
url.user = '*****'
url
end
end
end
end
...@@ -4,6 +4,12 @@ module QA ...@@ -4,6 +4,12 @@ module QA
module Support module Support
module Page module Page
module Logging module Logging
def assert_no_element(name)
log("asserting no element :#{name}")
super
end
def refresh def refresh
log("refreshing #{current_url}") log("refreshing #{current_url}")
......
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