Commit bc8581ab authored by Sanad Liaquat's avatar Sanad Liaquat

Merge branch 'shl-jenkins-integration-pipeline-result' into 'master'

Add e2e test for MR status for builds on Jenkins CI

Closes gitlab-org/quality/testcases#120

See merge request gitlab-org/gitlab!18578
parents bd0f9d01 790c01d0
......@@ -32,7 +32,7 @@ module ServicesHelper
end
def service_save_button(service)
button_tag(class: 'btn btn-success', type: 'submit', disabled: service.deprecated?) do
button_tag(class: 'btn btn-success', type: 'submit', disabled: service.deprecated?, data: { qa_selector: 'save_changes_button' }) do
icon('spinner spin', class: 'hidden js-btn-spinner') +
content_tag(:span, 'Save changes', class: 'js-btn-label')
end
......
......@@ -4,7 +4,7 @@
%fieldset
.form-group
.form-check
= f.check_box :allow_local_requests_from_web_hooks_and_services, class: 'form-check-input'
= f.check_box :allow_local_requests_from_web_hooks_and_services, class: 'form-check-input', data: { qa_selector: 'allow_requests_from_services_checkbox' }
= f.label :allow_local_requests_from_web_hooks_and_services, class: 'form-check-label' do
= _('Allow requests to the local network from web hooks and services')
.form-check
......@@ -27,4 +27,4 @@
%span.form-text.text-muted
= _('Resolves IP addresses once and uses them to submit requests')
= f.submit 'Save changes', class: "btn btn-success"
= f.submit 'Save changes', class: "btn btn-success", data: { qa_selector: 'save_changes_button' }
......@@ -24,7 +24,7 @@
.settings-content
= render 'ip_limits'
%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?) }
%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'outbound_requests_section' } }
.settings-header
%h4
= _('Outbound requests')
......
......@@ -163,7 +163,7 @@
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines' do
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines', data: { qa_selector: 'ci_cd_link' } do
.nav-icon-container
= sprite_icon('rocket')
%span.nav-item-name#js-onboarding-pipelines-link
......@@ -347,7 +347,7 @@
= _('Members')
- if can_edit
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
= link_to project_settings_integrations_path(@project), title: _('Integrations') do
= link_to project_settings_integrations_path(@project), title: _('Integrations'), data: { qa_selector: 'integrations_settings_link' } do
%span
= _('Integrations')
= nav_link(controller: :repository) do
......
......@@ -21,7 +21,7 @@
%td{ "aria-label" => (service.activated? ? s_("ProjectService|%{service_title}: status on") : s_("ProjectService|%{service_title}: status off")) % { service_title: service.title } }
= boolean_to_icon service.activated?
%td
= link_to edit_project_service_path(@project, service.to_param) do
= link_to edit_project_service_path(@project, service.to_param), { data: { qa_selector: "#{service.title.downcase.gsub(/[\s\(\)]/,'_')}_link" } } do
%strong= service.title
%td.d-none.d-sm-block
= service.description
......
......@@ -16,7 +16,7 @@
= form.label name, title, class: "col-form-label col-sm-2"
.col-sm-10
- if type == 'text'
= form.text_field name, class: "form-control", placeholder: placeholder, required: required, disabled: disabled
= form.text_field name, class: "form-control", placeholder: placeholder, required: required, disabled: disabled, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
- elsif type == 'textarea'
= form.text_area name, rows: 5, class: "form-control", placeholder: placeholder, required: required, disabled: disabled
- elsif type == 'checkbox'
......@@ -24,6 +24,6 @@
- elsif type == 'select'
= form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control", disabled: disabled}
- elsif type == 'password'
= form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, disabled: disabled
= form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, disabled: disabled, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
- if help
%span.form-text.text-muted= help
......@@ -12,7 +12,7 @@
.form-group.row
= form.label :active, "Active", class: "col-form-label col-sm-2"
.col-sm-10
= form.check_box :active, disabled: disable_fields_service?(@service)
= form.check_box :active, disabled: disable_fields_service?(@service), data: { qa_selector: 'active_checkbox' }
- if @service.configurable_events.present?
.form-group.row
......
......@@ -331,6 +331,7 @@ module QA
module Component
autoload :IpLimits, 'qa/page/admin/settings/component/ip_limits'
autoload :OutboundRequests, 'qa/page/admin/settings/component/outbound_requests'
autoload :RepositoryStorage, 'qa/page/admin/settings/component/repository_storage'
autoload :AccountAndLimit, 'qa/page/admin/settings/component/account_and_limit'
autoload :PerformanceBar, 'qa/page/admin/settings/component/performance_bar'
......@@ -406,6 +407,7 @@ module QA
module DockerRun
autoload :Base, 'qa/service/docker_run/base'
autoload :Jenkins, 'qa/service/docker_run/jenkins'
autoload :LDAP, 'qa/service/docker_run/ldap'
autoload :Maven, 'qa/service/docker_run/maven'
autoload :NodeJs, 'qa/service/docker_run/node_js'
......@@ -438,6 +440,17 @@ module QA
end
end
module Jenkins
module Page
autoload :Base, 'qa/vendor/jenkins/page/base'
autoload :Login, 'qa/vendor/jenkins/page/login'
autoload :Configure, 'qa/vendor/jenkins/page/configure'
autoload :NewCredentials, 'qa/vendor/jenkins/page/new_credentials'
autoload :NewJob, 'qa/vendor/jenkins/page/new_job'
autoload :ConfigureJob, 'qa/vendor/jenkins/page/configure_job'
end
end
module Github
module Page
autoload :Base, 'qa/vendor/github/page/base'
......
......@@ -109,8 +109,13 @@ module QA
autoload :ProtectedBranches, 'qa/ee/page/project/settings/protected_branches'
autoload :MirroringRepositories, 'qa/ee/page/project/settings/mirroring_repositories'
autoload :MergeRequest, 'qa/ee/page/project/settings/merge_request'
autoload :Integrations, 'qa/ee/page/project/settings/integrations'
autoload :Repository, 'qa/ee/page/project/settings/repository'
autoload :PushRules, 'qa/ee/page/project/settings/push_rules'
module Services
autoload :Jenkins, 'qa/ee/page/project/settings/services/jenkins'
end
end
module Operations
......
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Settings
class Integrations < QA::Page::Base
view 'app/views/projects/services/_index.html.haml' do
element :jenkins_ci_link, '{ data: { qa_selector: "#{service.title.downcase.gsub' # rubocop:disable QA/ElementWithPattern
end
def click_jenkins_ci_link
click_element :jenkins_ci_link
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Settings
module Services
class Jenkins < QA::Page::Base
view 'app/views/shared/_service_settings.html.haml' do
element :active_checkbox
end
view 'app/views/shared/_field.html.haml' do
element :jenkins_url_field, 'data: { qa_selector: "#{name.downcase.gsub' # rubocop:disable QA/ElementWithPattern
element :project_name_field, 'data: { qa_selector: "#{name.downcase.gsub' # rubocop:disable QA/ElementWithPattern
element :username_field, 'data: { qa_selector: "#{name.downcase.gsub' # rubocop:disable QA/ElementWithPattern
element :password_field, 'data: { qa_selector: "#{name.downcase.gsub' # rubocop:disable QA/ElementWithPattern
end
view 'app/helpers/services_helper.rb' do
element :save_changes_button
end
def setup_service_with(jenkins_url:, project_name:)
click_active_checkbox
set_jenkins_url(jenkins_url)
set_project_name(project_name)
set_username('admin')
set_password('password')
click_save_changes_button
end
private
def click_active_checkbox
click_element :active_checkbox
end
def set_jenkins_url(jenkins_url)
fill_element(:jenkins_url_field, jenkins_url)
end
def set_project_name(project_name)
fill_element(:project_name_field, project_name)
end
def set_username(username)
fill_element(:username_field, username)
end
def set_password(password)
fill_element(:password_field, password)
end
def click_save_changes_button
click_element :save_changes_button
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Admin
module Settings
module Component
class OutboundRequests < Page::Base
view 'app/views/admin/application_settings/_outbound.html.haml' do
element :allow_requests_from_services_checkbox
element :save_changes_button
end
def allow_requests_to_local_network_from_services
check_allow_requests_to_local_network_from_services_checkbox
click_save_changes_button
end
private
def check_allow_requests_to_local_network_from_services_checkbox
check_element :allow_requests_from_services_checkbox
end
def click_save_changes_button
click_element :save_changes_button
end
end
end
end
end
end
end
......@@ -9,6 +9,7 @@ module QA
view 'app/views/admin/application_settings/network.html.haml' do
element :ip_limits_section
element :outbound_requests_section
end
def expand_ip_limits(&block)
......@@ -16,6 +17,12 @@ module QA
Component::IpLimits.perform(&block)
end
end
def expand_outbound_requests(&block)
expand_section(:outbound_requests_section) do
Component::OutboundRequests.perform(&block)
end
end
end
end
end
......
......@@ -20,7 +20,7 @@ module QA
element :admin_area_link
element :projects_dropdown, required: true
element :groups_dropdown, required: true
element :more_dropdown, required: true
element :more_dropdown
element :snippets_link
end
......
......@@ -13,6 +13,7 @@ module QA
element :settings_item
element :link_members_settings
element :general_settings_link
element :integrations_settings_link
end
end
end
......@@ -55,6 +56,14 @@ module QA
end
end
def go_to_integrations_settings
hover_settings do
within_submenu do
click_element :integrations_settings_link
end
end
end
private
def hover_settings
......
......@@ -9,6 +9,7 @@ module QA
include Members
attr_writer :initialize_with_readme
attr_writer :auto_devops_enabled
attr_writer :visibility
attribute :id
......@@ -47,6 +48,7 @@ module QA
@standalone = false
@description = 'My awesome project'
@initialize_with_readme = false
@auto_devops_enabled = true
@visibility = 'public'
end
......@@ -101,7 +103,8 @@ module QA
name: name,
description: description,
visibility: @visibility,
initialize_with_readme: @initialize_with_readme
initialize_with_readme: @initialize_with_readme,
auto_devops_enabled: @auto_devops_enabled
}
unless @standalone
......
# frozen_string_literal: true
module QA
module Service
module DockerRun
class Jenkins < Base
def initialize
@image = 'registry.gitlab.com/gitlab-org/gitlab-qa/jenkins-gitlab:version1'
@name = 'jenkins-server'
@port = '8080'
super()
end
def host_address
"http://#{host_name}:#{@port}"
end
def host_name
return 'localhost' unless QA::Runtime::Env.running_in_ci?
super
end
def register!
command = <<~CMD.tr("\n", ' ')
docker run -d --rm
--network #{network}
--hostname #{host_name}
--name #{@name}
--env JENKINS_HOME=jenkins_home
--publish #{@port}:8080
--publish 50000:50000
#{@image}
CMD
command.gsub!("--network #{network} ", '') unless QA::Runtime::Env.running_in_ci?
shell command
end
end
end
end
end
# frozen_string_literal: true
require 'securerandom'
module QA
context 'Create', :docker do
describe 'Jenkins integration' do
let(:project_name) { "project_with_jenkins_#{SecureRandom.hex(4)}" }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = project_name
project.initialize_with_readme = true
project.auto_devops_enabled = false
end
end
before do
jenkins_server = run_jenkins_server
Vendor::Jenkins::Page::Base.host = jenkins_server.host_address
Runtime::Env.personal_access_token ||= fabricate_personal_access_token
allow_requests_to_local_networks
setup_jenkins
end
it 'integrates and displays build status for MR pipeline in GitLab' do
login_to_gitlab
setup_project_integration_with_jenkins
expect(page).to have_text("Jenkins CI activated.")
QA::Support::Retrier.retry_on_exception do
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.branch_name = 'master'
push.new_branch = false
push.file_name = "file_#{SecureRandom.hex(4)}.txt"
end
project.visit!
Page::Project::Menu.perform(&:click_ci_cd_pipelines)
Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
Page::Project::Pipeline::Show.perform do |show|
expect(show).to have_build('jenkins', status: :success, wait: 15)
end
end
end
after do
remove_jenkins_server
end
def setup_jenkins
Vendor::Jenkins::Page::Login.perform do |login_page|
login_page.visit!
login_page.login
end
token_description = "token-#{SecureRandom.hex(8)}"
Vendor::Jenkins::Page::NewCredentials.perform do |new_credentials|
new_credentials.visit_and_set_gitlab_api_token(Runtime::Env.personal_access_token, token_description)
end
Vendor::Jenkins::Page::Configure.perform do |configure|
configure.visit_and_setup_gitlab_connection(patch_host_name(Runtime::Scenario.gitlab_address, 'gitlab'), token_description) do
configure.click_test_connection
expect(configure).to have_success
end
end
Vendor::Jenkins::Page::NewJob.perform do |new_job|
new_job.visit_and_create_new_job_with_name(project_name)
end
Vendor::Jenkins::Page::ConfigureJob.perform do |configure_job|
configure_job.configure(scm_url: patch_host_name(project.repository_http_location.git_uri, 'gitlab'))
end
end
def run_jenkins_server
Service::DockerRun::Jenkins.new.tap do |runner|
runner.pull
runner.register!
end
end
def remove_jenkins_server
Service::DockerRun::Jenkins.new.remove!
end
def fabricate_personal_access_token
login_to_gitlab
token = Resource::PersonalAccessToken.fabricate!.access_token
Page::Main::Menu.perform(&:sign_out)
token
end
def login_to_gitlab
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
end
def patch_host_name(host_name, container_name)
return host_name unless host_name.include?('localhost')
ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}`.strip
host_name.gsub('localhost', ip_address)
end
def setup_project_integration_with_jenkins
project.visit!
Page::Project::Menu.perform(&:click_project)
Page::Project::Menu.perform(&:go_to_integrations_settings)
QA::EE::Page::Project::Settings::Integrations.perform(&:click_jenkins_ci_link)
QA::EE::Page::Project::Settings::Services::Jenkins.perform do |jenkins|
jenkins.setup_service_with(jenkins_url: patch_host_name(Vendor::Jenkins::Page::Base.host, 'jenkins-server'),
project_name: project_name)
end
end
def allow_requests_to_local_networks
Page::Main::Menu.perform(&:sign_out_if_signed_in)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
Page::Main::Menu.perform(&:click_admin_area)
Page::Admin::Menu.perform(&:go_to_network_settings)
Page::Admin::Settings::Network.perform do |network|
network.expand_outbound_requests do |outbound_requests|
outbound_requests.allow_requests_to_local_network_from_services
end
end
Page::Main::Menu.perform(&:sign_out)
end
end
end
end
# frozen_string_literal: true
module QA
module Vendor
module Jenkins
module Page
class Base
include Capybara::DSL
include Scenario::Actable
attr_reader :path
class << self
attr_accessor :host
end
def visit!
page.visit URI.join(Base.host, path).to_s
end
end
end
end
end
end
# frozen_string_literal: true
require 'capybara/dsl'
module QA
module Vendor
module Jenkins
module Page
class Configure < Page::Base
def initialize
@path = 'configure'
end
def visit_and_setup_gitlab_connection(gitlab_host, token_description)
visit!
fill_in '_.name', with: 'GitLab'
find('.setting-name', text: "Gitlab host URL").find(:xpath, "..").find('input').set gitlab_host
dropdown_element = find('.setting-name', text: "Credentials").find(:xpath, "..").find('select')
QA::Support::Retrier.retry_until(exit_on_failure: true) do
dropdown_element.select "GitLab API token (#{token_description})"
dropdown_element.value != ''
end
yield if block_given?
click_save
end
def click_test_connection
click_on 'Test Connection'
end
def has_success?
has_css?('div.ok', text: "Success")
end
private
def click_save
click_on 'Save'
end
end
end
end
end
end
# frozen_string_literal: true
require 'capybara/dsl'
module QA
module Vendor
module Jenkins
module Page
class ConfigureJob < Page::Base
attr_accessor :job_name
def initialize
@path = "/job/#{@job_name}/configure"
end
def configure(scm_url:)
set_git_source_code_management_url(scm_url)
click_build_when_change_is_pushed_to_gitlab
set_publish_status_to_gitlab
click_save
end
private
def set_git_source_code_management_url(repository_url)
select_git_source_code_management
set_repository_url(repository_url)
end
def click_build_when_change_is_pushed_to_gitlab
find('label', text: 'Build when a change is pushed to GitLab').find(:xpath, "..").find('input').click
end
def set_publish_status_to_gitlab
click_add_post_build_action
select_publish_build_status_to_gitlab
end
def click_save
click_on 'Save'
end
def select_git_source_code_management
find('#radio-block-1').click
end
def set_repository_url(repository_url)
find('.setting-name', text: "Repository URL").find(:xpath, "..").find('input').set repository_url
end
def click_add_post_build_action
click_on "Add post-build action"
end
def select_publish_build_status_to_gitlab
click_link "Publish build status to GitLab"
end
end
end
end
end
end
# frozen_string_literal: true
require 'capybara/dsl'
module QA
module Vendor
module Jenkins
module Page
class Login < Page::Base
def initialize
@path = 'login'
end
def visit!
super
QA::Support::Retrier.retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, exit_on_failure: true) do
page.has_text? 'Welcome to Jenkins!'
end
end
def login
fill_in 'j_username', with: 'admin'
fill_in 'j_password', with: 'password'
click_on 'Sign in'
end
end
end
end
end
end
# frozen_string_literal: true
require 'capybara/dsl'
module QA
module Vendor
module Jenkins
module Page
class NewCredentials < Page::Base
def initialize
@path = 'credentials/store/system/domain/_/newCredentials'
end
def visit_and_set_gitlab_api_token(api_token, description)
visit!
wait_for_page_to_load
select_gitlab_api_token
set_api_token(api_token)
set_description(description)
click_ok
end
private
def select_gitlab_api_token
find('.setting-name', text: "Kind").find(:xpath, "..").find('select').select "GitLab API token"
end
def set_api_token(api_token)
fill_in '_.apiToken', with: api_token
end
def set_description(description)
fill_in '_.description', with: description
end
def click_ok
click_on 'OK'
end
def wait_for_page_to_load
QA::Support::Waiter.wait(interval: 1.0) do
page.has_css?('.setting-name', text: "Description")
end
end
end
end
end
end
end
# frozen_string_literal: true
require 'capybara/dsl'
module QA
module Vendor
module Jenkins
module Page
class NewJob < Page::Base
def initialize
@path = 'newJob'
end
def visit_and_create_new_job_with_name(new_job_name)
visit!
set_new_job_name(new_job_name)
click_free_style_project
click_ok
end
private
def set_new_job_name(new_job_name)
fill_in 'name', with: new_job_name
end
def click_free_style_project
find('.hudson_model_FreeStyleProject').click
end
def click_ok
click_on 'OK'
end
end
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