Commit e13631b8 authored by Mark Lapierre's avatar Mark Lapierre

Adds an E2E test of multi-project pipelines

Uses a group runner rather than 2 project runners, so it also adds the
ability to override the token used when registering a runner (the
default is to use the token for the project attribute of the runner).

Adds the ability to remove a runner via the API, which also removes
the runner service (docker container).

Extracts the CI badge element and methods to a component that is
shared with the pipeline page object and the job page object.

Fixes a bug in `click_element` that meant text input was ignored.
parent f46b3f01
......@@ -115,7 +115,10 @@ export default {
data-qa-selector="merge_request_pipeline_info_content"
>
{{ pipeline.details.name }}
<gl-link :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
<gl-link
:href="pipeline.path"
class="pipeline-id font-weight-normal pipeline-number"
data-qa-selector="pipeline_link"
>#{{ pipeline.id }}</gl-link
>
{{ pipeline.details.status.label }}
......
......@@ -48,6 +48,7 @@ export default {
v-gl-tooltip
:title="tooltipText"
class="js-linked-pipeline-content linked-pipeline-content"
data-qa-selector="linked_pipeline_button"
:class="`js-pipeline-expand-${pipeline.id}`"
@click="onClickLinkedPipeline"
>
......
......@@ -357,6 +357,7 @@ module QA
# Classes describing components that are used by several pages.
#
module Component
autoload :CiBadgeLink, 'qa/page/component/ci_badge_link'
autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :LazyLoader, 'qa/page/component/lazy_loader'
autoload :LegacyClonePanel, 'qa/page/component/legacy_clone_panel'
......
......@@ -5,15 +5,19 @@ module QA::EE::Page
module Show
def self.prepended(page)
page.module_eval do
view 'ee/app/views/projects/pipelines/_tabs_holder.html.haml' do
element :security_tab
element :security_counter
view 'ee/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue' do
element :linked_pipeline_button
end
view 'ee/app/assets/javascripts/security_dashboard/components/filter.vue' do
element :filter_dropdown, ':data-qa-selector="qaSelector"' # rubocop:disable QA/ElementWithPattern
element :filter_dropdown_content
end
view 'ee/app/views/projects/pipelines/_tabs_holder.html.haml' do
element :security_tab
element :security_counter
end
end
end
......
......@@ -95,7 +95,7 @@ module QA
# replace with (..., page = self.class)
def click_element(name, page = nil, text: nil)
find_element(name, text: nil).click
find_element(name, text: text).click
page.validate_elements_present! if page
end
......
# frozen_string_literal: true
module QA
module Page
module Component
module CiBadgeLink
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze
def self.included(base)
base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
element :status_badge
end
end
def status_badge
find_element(:status_badge).text
end
def successful?(timeout: 60)
raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
status_badge == PASSED_STATUS
end
private
def completed?(timeout: 60)
wait(reload: false, max: timeout) do
COMPLETED_STATUSES.include?(status_badge)
end
end
end
end
end
end
......@@ -14,6 +14,7 @@ module QA
view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue' do
element :merge_request_pipeline_info_content
element :pipeline_link
end
view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do
......@@ -59,6 +60,18 @@ module QA
element :edit_button
end
def click_discussions_tab
click_element :notes_tab
end
def click_diffs_tab
click_element :diffs_tab
end
def click_pipeline_link
click_element :pipeline_link
end
def fast_forward_possible?
has_no_text?('Fast-forward merge is not possible')
end
......@@ -156,14 +169,6 @@ module QA
click_element :squash_checkbox
end
def click_discussions_tab
click_element :notes_tab
end
def click_diffs_tab
click_element :diffs_tab
end
def add_comment_to_diff(text)
wait(interval: 5) do
has_text?("No newline at end of file")
......
......@@ -3,17 +3,12 @@
module QA::Page
module Project::Job
class Show < QA::Page::Base
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze
include Component::CiBadgeLink
view 'app/assets/javascripts/jobs/components/job_log.vue' do
element :build_trace
end
view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
element :status_badge
end
view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do
element :pipeline_path
end
......@@ -45,16 +40,6 @@ module QA::Page
has_element?(:build_trace, wait: 1)
end
end
def completed?(timeout: 60)
wait(reload: false, max: timeout) do
COMPLETED_STATUSES.include?(status_badge)
end
end
def status_badge
find_element(:status_badge).text
end
end
end
end
......@@ -3,6 +3,8 @@
module QA::Page
module Project::Pipeline
class Show < QA::Page::Base
include Component::CiBadgeLink
view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do
element :pipeline_header, /header class.*ci-header-container.*/ # rubocop:disable QA/ElementWithPattern
end
......@@ -38,6 +40,14 @@ module QA::Page
end
end
def has_job?(job_name)
has_element?(:job_link, text: job_name)
end
def has_no_job?(job_name)
has_no_element?(:job_link, text: job_name)
end
def has_tag?(tag_name)
within_element(:pipeline_badges) do
has_selector?('.badge', text: tag_name)
......@@ -45,7 +55,11 @@ module QA::Page
end
def click_job(job_name)
find_element(:job_link, text: job_name).click
click_element(:job_link, text: job_name)
end
def click_linked_job(project_name)
click_element(:linked_pipeline_button, text: /#{project_name}/)
end
def click_on_first_job
......
......@@ -88,6 +88,10 @@ module QA
"#{api_get_path}/members"
end
def api_runners_path
"#{api_get_path}/runners"
end
def api_post_path
'/projects'
end
......@@ -108,6 +112,11 @@ module QA
post_body
end
def runners
response = get Runtime::API::Request.new(api_client, api_runners_path).url
parse_body(response)
end
def share_with_group(invitee, access_level = Resource::Members::AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, "/projects/#{id}/share").url, { group_id: invitee.id, group_access: access_level }
end
......
......@@ -6,8 +6,9 @@ module QA
module Resource
class Runner < Base
attr_writer :name, :tags, :image
attr_accessor :config
attr_accessor :config, :token
attribute :id
attribute :project do
Project.fabricate_via_api! do |resource|
resource.name = 'project-with-ci-cd'
......@@ -30,7 +31,7 @@ module QA
def fabricate_via_api!
Service::Runner.new(name).tap do |runner|
runner.pull
runner.token = project.runners_token
runner.token = @token ||= project.runners_token
runner.address = Runtime::Scenario.gitlab_address
runner.tags = tags
runner.image = image
......@@ -40,6 +41,18 @@ module QA
end
end
def remove_via_api!
@id = project.runners.find { |runner| runner[:description] == name }[:id]
super
Service::Runner.new(name).remove!
end
def api_delete_path
"/runners/#{id}"
end
def api_get_path
end
......
......@@ -10,6 +10,7 @@ module QA
attr_accessor :path
attribute :id
attribute :runners_token
def initialize
@path = Runtime::Namespace.sandbox_name
......
# frozen_string_literal: true
require 'securerandom'
module QA
context 'Release', :docker do
describe 'Multi-project pipelines' do
let(:upstream_project_name) { "upstream-project-#{SecureRandom.hex(8)}" }
let(:downstream_project_name) { "downstream-project-#{SecureRandom.hex(8)}" }
let(:upstream_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = upstream_project_name
end
end
let(:downstream_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = downstream_project_name
end
end
let!(:runner) do
Resource::Runner.fabricate_via_api! do |runner|
runner.project = upstream_project
runner.token = upstream_project.group.sandbox.runners_token
runner.name = upstream_project_name
runner.tags = %w[qa test]
end
end
before do
Resource::Repository::ProjectPush.fabricate! do |project_push|
project_push.project = upstream_project
project_push.file_name = '.gitlab-ci.yml'
project_push.commit_message = 'Add .gitlab-ci.yml'
project_push.file_content = <<~CI
stages:
- test
- deploy
job1:
stage: test
script: echo "done"
staging:
stage: deploy
trigger:
project: #{downstream_project.path_with_namespace}
strategy: depend
CI
end
Resource::Repository::ProjectPush.fabricate! do |project_push|
project_push.project = downstream_project
project_push.file_name = '.gitlab-ci.yml'
project_push.commit_message = 'Add .gitlab-ci.yml'
project_push.file_content = <<~CI
downstream_job:
stage: test
script: echo "done"
CI
end
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
Resource::MergeRequest.fabricate_via_api! do |merge_request|
merge_request.project = upstream_project
merge_request.target_new_branch = false
end.visit!
end
after do
runner.remove_via_api!
end
it 'creates a multi-project pipeline' do
Page::MergeRequest::Show.perform do |show|
pipeline_passed = Support::Retrier.retry_until do
show.has_content?(/Pipeline #\d+ passed/)
end
expect(pipeline_passed).to be_truthy, "The pipeline did not pass."
show.click_pipeline_link
end
Page::Project::Pipeline::Show.perform do |show|
expect(show).to be_successful
expect(show).to have_no_job("downstream_job")
show.click_linked_job(downstream_project_name)
expect(show).to have_job("downstream_job")
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