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?
- 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
%h4= _('Mirroring repositories')
%button.btn.js-settings-toggle
......@@ -59,10 +59,10 @@
- if mirror.disabled?
= render 'projects/mirrors/disabled_mirror_badge'
- 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
.btn-group.mirror-actions-group.pull-right{ role: 'group' }
- 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
%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 @@
- verified_at = mirror.ssh_known_hosts_verified_at
.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')
= _('Detect host keys')
.fingerprint-ssh-info.js-fingerprint-ssh-info.prepend-top-10.append-bottom-10{ class: ('collapse' unless mirror.ssh_mirror_url?) }
%label.label-bold
= _('Fingerprints')
.fingerprints-list.js-fingerprints-list
.fingerprints-list.js-fingerprints-list{ data: { qa_selector: 'fingerprints_list' } }
- mirror.ssh_known_hosts_fingerprints.each do |fp|
%code= fp.fingerprint
- if verified_at
......
- 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")
- 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
......
......@@ -19,9 +19,9 @@
.btn-group.mirror-actions-group.pull-right{ role: 'group' }
- ssh_public_key = @project.import_data.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?
%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
= 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")
......
......@@ -148,7 +148,7 @@ describe 'Project mirror', :js do
expect(page).to have_content('Mirroring settings were successfully updated')
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.username_only_import_url).to eq('ssh://user@example.com')
expect(import_data.auth_method).to eq('ssh_public_key')
......
......@@ -17,6 +17,8 @@ module QA
element :mirror_repository_url_cell
element :mirrored_repository_row
element :update_now_button
element :updating_button
element :copy_public_key_button
end
end
end
......
......@@ -15,6 +15,10 @@ module QA
def_delegators :evaluator, :view, :views
def assert_no_element(name)
assert_no_selector(element_selector_css(name))
end
def refresh
page.refresh
end
......@@ -102,9 +106,9 @@ module QA
def select_element(name, value)
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
def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
......
......@@ -15,7 +15,9 @@ module QA
element :mirror_repository_button
element :mirror_repository_url_cell
element :mirror_last_update_at_cell
element :mirror_error_badge
element :mirrored_repository_row
element :copy_public_key_button
end
view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do
......@@ -24,6 +26,17 @@ module QA
view 'app/views/shared/_remote_mirror_update_button.html.haml' do
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
def repository_url=(value)
......@@ -35,17 +48,40 @@ module QA
end
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)
# 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
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)
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
click_element :mirror_repository_button
end
......@@ -54,7 +90,9 @@ module QA
row_index = find_repository_row_index url
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
# Wait a few seconds for the sync to occur and then refresh the page
......@@ -72,16 +110,19 @@ module QA
# 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_cell, wait: 0).assert_no_text('Never')
assert_no_element(:mirror_error_badge)
end
end
private
def find_repository_row_index(target_url)
all_elements(:mirror_repository_url_cell).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
wait(max: 5, reload: false) do
all_elements(:mirror_repository_url_cell).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
......
......@@ -16,7 +16,7 @@ module QA
end
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
element :mirroring_repositories_settings
element :mirroring_repositories_settings_section
end
def expand_deploy_keys(&block)
......@@ -38,7 +38,7 @@ module QA
end
def expand_mirroring_repositories(&block)
expand_section(:mirroring_repositories_settings) do
expand_section(:mirroring_repositories_settings_section) do
MirroringRepositories.perform(&block)
end
end
......
......@@ -25,8 +25,8 @@ module QA
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.mirror_direction = 'Push'
mirror_settings.authentication_method = 'Password'
mirror_settings.password = Runtime::User.password
mirror_settings.mirror_repository
mirror_settings.update target_project_uri
......
......@@ -70,7 +70,7 @@ module QA
Page::Project::Settings::Main.perform do |settings|
# Change visibility from public to internal
settings.expand_visibility_project_features_permissions do |page|
page.set_project_visibility "internal"
page.set_project_visibility "Internal"
end
end
end
......
......@@ -26,8 +26,8 @@ module QA
settings.expand_mirroring_repositories do |mirror_settings|
# Configure the target project to pull from the source project
mirror_settings.repository_url = source_project_uri
mirror_settings.mirror_direction = :pull
mirror_settings.authentication_method = :password
mirror_settings.mirror_direction = 'Pull'
mirror_settings.authentication_method = 'Password'
mirror_settings.password = Runtime::User.password
mirror_settings.mirror_repository
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
module Support
module Page
module Logging
def assert_no_element(name)
log("asserting no element :#{name}")
super
end
def refresh
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