Skip to content
Commits on Source (2)
......@@ -46,7 +46,8 @@ variables:
QA_CAN_TEST_GIT_PROTOCOL_V2: "true"
QA_CAN_TEST_PRAEFECT: "false"
QA_GENERATE_ALLURE_REPORT: "true"
QA_TESTCASES_REPORTING_PROJECT: "gitlab-org/quality/testcases"
QA_TESTCASES_REPORTING_PROJECT: "gitlab-org/gitlab"
QA_TEST_RESULTS_ISSUES_PROJECT: "gitlab-org/quality/testcases"
QA_TESTCASE_SESSIONS_PROJECT: "gitlab-org/quality/testcase-sessions"
# QA_DEFAULT_BRANCH is the default branch name of the instance under test.
QA_DEFAULT_BRANCH: "master"
......@@ -84,7 +85,7 @@ rspec:
- bundle exec exe/gitlab-qa ${QA_SCENARIO:=Test::Instance::Image} ${RELEASE:=$DEFAULT_RELEASE} $GITLAB_QA_OPTIONS -- $QA_TESTS $QA_RSPEC_TAGS $RSPEC_REPORT_OPTS || test_run_exit_code=$?
- bundle exec exe/gitlab-qa-report --update-screenshot-path "gitlab-qa-run-*/**/rspec-*.xml"
- export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-in-issues "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_TESTCASES_REPORTING_PROJECT" || true; fi
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-results "gitlab-qa-run-*/**/rspec-*.json" --test-case-project "$QA_TESTCASES_REPORTING_PROJECT" --results-issue-project "$QA_TEST_RESULTS_ISSUES_PROJECT" || true; fi
- exit $test_run_exit_code
# For jobs that shouldn't report results in issues, e.g., manually run custom jobs
......@@ -107,7 +108,7 @@ rspec:
- bundle exec exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=$DEFAULT_RELEASE} ${RELEASE:=$DEFAULT_RELEASE} $GITLAB_QA_OPTIONS -- $QA_TESTS $QA_RSPEC_TAGS $RSPEC_REPORT_OPTS || test_run_exit_code=$?
- bundle exec exe/gitlab-qa-report --update-screenshot-path "gitlab-qa-run-*/**/rspec-*.xml"
- export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-in-issues "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_TESTCASES_REPORTING_PROJECT" || true; fi
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-results "gitlab-qa-run-*/**/rspec-*.json" --test-case-project "$QA_TESTCASES_REPORTING_PROJECT" --results-issue-project "$QA_TEST_RESULTS_ISSUES_PROJECT" || true; fi
- exit $test_run_exit_code
.ce-variables:
......
......@@ -123,7 +123,10 @@ module Gitlab
autoload :JUnitTestResults, 'gitlab/qa/report/junit_test_results'
autoload :PrepareStageReports, 'gitlab/qa/report/prepare_stage_reports'
autoload :ReportAsIssue, 'gitlab/qa/report/report_as_issue'
autoload :ReportResults, 'gitlab/qa/report/report_results'
autoload :ResultsInIssues, 'gitlab/qa/report/results_in_issues'
autoload :ResultsInTestCases, 'gitlab/qa/report/results_in_testcases'
autoload :ResultsReporterShared, 'gitlab/qa/report/results_reporter_shared'
autoload :GenerateTestSession, 'gitlab/qa/report/generate_test_session'
autoload :SummaryTable, 'gitlab/qa/report/summary_table'
autoload :TestResult, 'gitlab/qa/report/test_result'
......
......@@ -6,6 +6,11 @@ module Gitlab
module QA
module Report
class GenerateTestSession < ReportAsIssue
def initialize(**kwargs)
super
@issue_type = 'issue'
end
private
# rubocop:disable Metrics/AbcSize
......
......@@ -19,6 +19,7 @@ module Gitlab
def initialize(max_diff_ratio: DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION, **kwargs)
super
@max_diff_ratio = max_diff_ratio.to_f
@issue_type = 'issue'
end
private
......@@ -63,7 +64,7 @@ module Gitlab
puts " => Found issue #{issue.web_url} for test '#{test.name}' with a diff ratio of #{(diff_ratio * 100).round(2)}%."
else
issue = create_issue(test)
puts " => Created new issue: #{issue.web_url} for test '#{test.name}'." if issue
puts "for test '#{test.name}'."
end
issue
......
......@@ -22,7 +22,7 @@ module Gitlab
private
attr_reader :gitlab, :files, :project
attr_reader :gitlab, :files, :project, :issue_type
def run!
raise NotImplementedError
......@@ -77,11 +77,18 @@ module Gitlab
end
def create_issue(test)
gitlab.create_issue(
issue = gitlab.create_issue(
title: title_from_test(test),
description: new_issue_description(test),
labels: new_issue_labels(test).to_a
labels: new_issue_labels(test).to_a,
issue_type: issue_type
)
new_link = issue_type == 'test_case' ? issue.web_url.sub('/issues/', '/quality/test_cases/') : issue.web_url
puts "Created new #{issue_type}: #{new_link}"
issue
end
def issue_labels(issue)
......
# frozen_string_literal: true
module Gitlab
module QA
module Report
# Uses the API to create or update GitLab test cases and issues with the results of tests from RSpec report files.
class ReportResults < ReportAsIssue
# TODO: Remove old_testcase_project_reporter once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
attr_accessor :testcase_project_reporter, :results_issue_project_reporter, :files, :test_case_project, :results_issue_project, :gitlab, :old_testcase_project_reporter
def initialize(token:, input_files:, test_case_project:, results_issue_project:, dry_run: false, **kwargs)
@testcase_project_reporter = Gitlab::QA::Report::ResultsInTestCases.new(token: token, input_files: input_files, project: test_case_project, dry_run: dry_run, **kwargs)
# TODO: Remove the line below once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
@old_testcase_project_reporter = Gitlab::QA::Report::ResultsInTestCases.new(token: token, input_files: input_files, project: results_issue_project, dry_run: dry_run, **kwargs)
@results_issue_project_reporter = Gitlab::QA::Report::ResultsInIssues.new(token: token, input_files: input_files, project: results_issue_project, dry_run: dry_run, **kwargs)
@test_case_project = test_case_project
@results_issue_project = results_issue_project
@files = Array(input_files)
@gitlab = testcase_project_reporter.gitlab
end
def assert_project!
return if test_case_project && results_issue_project
abort "Please provide valid project IDs or paths with the `--results-issue-project` and `--test-case-project` options!"
end
def run!
puts "Reporting test results in `#{files.join(',')}` as test cases in project `#{test_case_project}`"\
" and issues in project `#{results_issue_project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
test_results_per_file do |test_results|
puts "Reporting tests in #{test_results.path}"
test_results.each do |test|
puts "Reporting test: #{test.file} | #{test.name}\n"
report_test(test) unless test.skipped
end
test_results.write
end
end
private
def report_test(test)
# TODO: Remove the line below and replace correct_testcase_project_reporter with testcase_project_reporter
# once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
correct_testcase_project_reporter = using_old_testcase_link(test) ? old_testcase_project_reporter : testcase_project_reporter
testcase = correct_testcase_project_reporter.find_or_create_testcase(test)
# The API returns the test case with an issue URL since it is technically a type of issue.
# This updates the URL to a valid test case link.
test.testcase = testcase.web_url.sub('/issues/', '/quality/test_cases/')
issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
correct_testcase_project_reporter.add_issue_link_to_testcase(testcase, issue, test) if is_new
correct_testcase_project_reporter.update_testcase(testcase, test)
results_issue_project_reporter.update_issue(issue, test)
end
def using_old_testcase_link(test)
test.testcase.include?(results_issue_project)
end
end
end
end
end
# frozen_string_literal: true
require 'nokogiri'
require 'active_support/core_ext/enumerable'
module Gitlab
module QA
module Report
# Uses the API to create or update GitLab issues with the results of tests from RSpec report files.
# Uses the API to create or update GitLab test result issues with the results of tests from RSpec report files.
class ResultsInIssues < ReportAsIssue
private
RESULTS_SECTION_TEMPLATE = "\n\n### DO NOT EDIT BELOW THIS LINE\n\nActive and historical test results:"
def run!
puts "Reporting test results in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
test_results_per_file do |test_results|
puts "Reporting tests in #{test_results.path}"
include ResultsReporterShared
test_results.each do |test|
puts "Reporting test: #{test.file} | #{test.name}\n"
report_test(test) unless test.skipped
end
test_results.write
end
def initialize(**kwargs)
super
@issue_type = 'issue'
end
def report_test(test)
testcase = find_testcase(test) || create_testcase(test)
test.testcase ||= testcase.web_url.sub('/issues/', '/quality/test_cases/')
def get_related_issue(testcase, test)
issue = find_linked_results_issue_by_iid(testcase, test)
is_new = false
if issue
issue = update_issue_title(issue, test, 'issue') if issue_title_needs_updating?(issue, test)
issue = update_issue_title(issue, test) if issue_title_needs_updating?(issue, test)
else
puts "No valid issue link found"
issue = find_or_create_results_issue(test)
add_issue_to_testcase(testcase, issue)
is_new = true
end
update_labels(testcase, test)
update_issue(issue, test)
[issue, is_new]
end
def find_testcase(test)
testcase = find_testcase_by_iid(test)
def update_issue(issue, test)
new_labels = issue_labels(issue)
new_labels |= ['Testcase Linked']
labels_updated = update_labels(issue, test, new_labels)
note_posted = note_status(issue, test)
if testcase
testcase = update_issue_title(testcase, test, 'test_case') if issue_title_needs_updating?(testcase, test)
if labels_updated || note_posted
puts "Issue updated."
else
testcase = find_issue(test, 'test_case')
puts "Test passed, no results issue update needed."
end
testcase
end
def find_testcase_by_iid(test)
iid = testcase_iid_from_url(test.testcase)
return unless iid
find_issue_by_iid(iid, 'test_case')
end
def issue_title_needs_updating?(issue, test)
issue.title.strip != title_from_test(test) && !%w[canary production preprod release].include?(pipeline)
end
private
def find_linked_results_issue_by_iid(testcase, test)
iid = issue_iid_from_testcase(testcase)
return unless iid
find_issue_by_iid(iid, 'issue')
find_issue_by_iid(iid)
end
def find_issue_by_iid(iid, issue_type)
issues = gitlab.find_issues(iid: iid) do |issue|
issue.state == 'opened' && issue.issue_type == issue_type
end
warn(%(#{issue_type} iid "#{iid}" not valid)) if issues.empty?
issues.first
end
def update_issue_title(issue, test, issue_type)
warn(%(#{issue_type} title needs to be updated from '#{issue.title.strip}' to '#{title_from_test(test)}'))
gitlab.edit_issue(iid: issue.iid, options: { title: title_from_test(test) })
end
def create_testcase(test)
title = title_from_test(test)
puts "Creating test case '#{title}' ..."
gitlab.create_issue(
title: title,
description: new_testcase_description(test),
labels: new_issue_labels(test),
issue_type: 'test_case'
)
end
def testcase_iid_from_url(url)
return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
url && url.split('/').last.to_i
end
def new_testcase_description(test)
"#{new_issue_description(test)}#{RESULTS_SECTION_TEMPLATE}"
def find_or_create_results_issue(test)
find_issue(test) || create_issue(test)
end
def issue_iid_from_testcase(testcase)
results = testcase.description.partition(RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(RESULTS_SECTION_TEMPLATE)
results = testcase.description.partition(TEST_CASE_RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE)
return puts "No issue link found" unless results
......@@ -127,66 +65,6 @@ module Gitlab
issue_iid&.to_i
end
def find_or_create_results_issue(test)
issue = find_issue(test, 'issue')
if issue
puts "Found existing issue: #{issue.web_url}"
else
issue = create_issue(test)
puts "Created new issue: #{issue.web_url}"
end
issue
end
def find_issue(test, issue_type)
issues = gitlab.find_issues(options: { search: search_term(test) }) do |issue|
issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
end
warn(%(Too many #{issue_type}s found with the file path "#{test.file}" and name "#{test.name}")) if issues.many?
issues.first
end
def add_issue_to_testcase(testcase, issue)
results_section = testcase.description.include?(RESULTS_SECTION_TEMPLATE) ? '' : RESULTS_SECTION_TEMPLATE
gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
puts "Added issue #{issue.web_url} to testcase #{testcase.web_url}"
end
def update_issue(issue, test)
new_labels = issue_labels(issue)
new_labels |= ['Testcase Linked']
labels_updated = update_labels(issue, test, new_labels)
note_posted = note_status(issue, test)
if labels_updated || note_posted
puts "Issue updated."
else
puts "Test passed, no update needed."
end
end
def new_issue_labels(test)
['Quality', "devops::#{test.stage}", 'status::automated']
end
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
labels = super
labels |= new_issue_labels(test).to_set
labels.delete_if { |label| label.start_with?("#{pipeline}::") }
labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
end
def search_term(test)
%("#{partial_file_path(test.file)}" "#{search_safe(test.name)}")
end
def note_status(issue, test)
return false if test.skipped
return false if test.failures.empty?
......
# frozen_string_literal: true
module Gitlab
module QA
module Report
# Uses the API to create or update GitLab test cases with the results of tests from RSpec report files.
class ResultsInTestCases < ReportAsIssue
attr_reader :issue_type, :gitlab
include ResultsReporterShared
def initialize(**kwargs)
super
@issue_type = 'test_case'
end
def find_or_create_testcase(test)
find_testcase(test) || create_issue(test)
end
def add_issue_link_to_testcase(testcase, issue, test)
results_section = testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE) ? '' : TEST_CASE_RESULTS_SECTION_TEMPLATE
gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
# We are using test.testcase for the url here instead of testcase.web_url since it has the updated test case path
puts "Added results issue #{issue.web_url} link to test case #{test.testcase}"
end
def update_testcase(testcase, test)
puts "Test case labels updated." if update_labels(testcase, test)
end
private
def find_testcase(test)
testcase = find_testcase_by_iid(test)
if testcase
testcase = update_issue_title(testcase, test) if issue_title_needs_updating?(testcase, test)
else
testcase = find_issue(test)
end
testcase
end
def find_testcase_by_iid(test)
iid = testcase_iid_from_url(test.testcase)
return unless iid
find_issue_by_iid(iid)
end
def testcase_iid_from_url(url)
return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
url && url.split('/').last.to_i
end
def new_issue_description(test)
"#{super}#{TEST_CASE_RESULTS_SECTION_TEMPLATE}"
end
end
end
end
end
# frozen_string_literal: true
require 'active_support/core_ext/enumerable'
module Gitlab
module QA
module Report
module ResultsReporterShared
TEST_CASE_RESULTS_SECTION_TEMPLATE = "\n\n### DO NOT EDIT BELOW THIS LINE\n\nActive and historical test results:"
def find_issue(test)
issues = search_for_issues(test)
warn(%(Too many #{issue_type}s found with the file path "#{test.file}" and name "#{test.name}")) if issues.many?
puts "Found existing #{issue_type}: #{issues.first.web_url}" unless issues.empty?
issues.first
end
def find_issue_by_iid(iid)
issues = gitlab.find_issues(iid: iid) do |issue|
issue.state == 'opened' && issue.issue_type == issue_type
end
warn(%(#{issue_type} iid "#{iid}" not valid)) if issues.empty?
issues.first
end
def issue_title_needs_updating?(issue, test)
issue.title.strip != title_from_test(test) && !%w[canary production preprod release].include?(pipeline)
end
def new_issue_labels(test)
['Quality', "devops::#{test.stage}", 'status::automated']
end
def search_term(test)
%("#{partial_file_path(test.file)}" "#{search_safe(test.name)}")
end
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
labels = super
labels |= new_issue_labels(test).to_set
labels.delete_if { |label| label.start_with?("#{pipeline}::") }
labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
end
def update_issue_title(issue, test)
warn(%(#{issue_type} title needs to be updated from '#{issue.title.strip}' to '#{title_from_test(test)}'))
gitlab.edit_issue(iid: issue.iid, options: { title: title_from_test(test) })
end
private
def search_for_issues(test)
gitlab.find_issues(options: { search: search_term(test) }) do |issue|
issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
end
end
end
end
end
end
......@@ -17,8 +17,8 @@ module Gitlab
report_options[:input_files] = files if files
end
opts.on('--report-in-issues FILES', String, 'Report test results from JUnit XML files in GitLab issues') do |files|
report_options[:report_in_issues] = true
opts.on('--report-results FILES', String, 'Report test results from JUnit XML files in GitLab test cases and results issues') do |files|
report_options[:report_results] = true
report_options[:input_files] = files if files
end
......@@ -31,16 +31,24 @@ module Gitlab
report_options[:max_diff_ratio] = value
end
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-in-issues and --relate-failure-issue') do |value|
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --relate-failure-issue') do |value|
report_options[:project] = value
end
opts.on('--results-issue-project RESULTS_ISSUE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
report_options[:results_issue_project] = value
end
opts.on('--test-case-project TEST_CASE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
report_options[:test_case_project] = value
end
opts.on('--generate-test-session FILES', String, 'Generate test session report') do |files|
report_options[:generate_test_session] = true
report_options[:input_files] = files if files
end
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-in-issues and --relate-failure-issue') do |value|
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-results and --relate-failure-issue') do |value|
report_options[:token] = value
end
......@@ -60,7 +68,7 @@ module Gitlab
report_options[:files] = files
end
opts.on('--dry-run', "Perform a dry-run (don't create or update issues)") do |files|
opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do |files|
report_options[:dry_run] = true
end
......@@ -86,9 +94,9 @@ module Gitlab
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
Gitlab::QA::Report::RelateFailureIssue.new(**report_options).invoke!
elsif report_options.delete(:report_in_issues)
elsif report_options.delete(:report_results)
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
Gitlab::QA::Report::ResultsInIssues.new(**report_options).invoke!
Gitlab::QA::Report::ReportResults.new(**report_options).invoke!
elsif report_options.delete(:generate_test_session)
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
......
......@@ -134,12 +134,12 @@ Failure/Error: expect_status(404)
context 'when no issue is found' do
it 'creates a new issue' do
expect(subject).to receive(:create_issue).and_return(new_issue)
expect(::Gitlab).to receive(:create_issue).and_return(new_issue)
expect(subject).to receive(:update_labels)
expect(subject).to receive(:post_failed_job_note)
expect { subject.invoke! }
.to output(%r{Created new issue: http://new-issue.url for test '#{test_name}'.*}).to_stdout
.to output(%r{Created new issue: http://new-issue.url\nfor test '#{test_name}'.*}).to_stdout
end
context 'when creating a new issue' do
......@@ -148,8 +148,9 @@ Failure/Error: expect_status(404)
.with(
project,
"Failure in #{test_file_partial} | #{test_name}",
hash_including(description: expected_new_issue_description, labels: described_class::NEW_ISSUE_LABELS.to_a + ['found:staging.gitlab.com']))
hash_including(description: expected_new_issue_description, labels: described_class::NEW_ISSUE_LABELS.to_a + ['found:staging.gitlab.com'], issue_type: 'issue'))
.and_return(new_issue)
expect(::Gitlab).to receive(:edit_issue).with(anything, anything, labels: %w[found:staging.gitlab.com])
expect(::Gitlab).to receive(:create_issue_note).with(project, new_issue.iid, expected_posted_note)
......@@ -167,7 +168,11 @@ Failure/Error: expect_status(404)
full_title = "Failure in #{test_file_partial} | #{test_name}"
expect(::Gitlab).to receive(:create_issue)
.with(anything, "#{full_title[0...described_class::MAX_TITLE_LENGTH - 3]}...", anything)
.with(project, "#{full_title[0...described_class::MAX_TITLE_LENGTH - 3]}...", hash_including(issue_type: 'issue'))
.and_return(new_issue)
expect(::Gitlab).to receive(:edit_issue).with(anything, anything, labels: %w[found:staging.gitlab.com])
expect(::Gitlab).to receive(:create_issue_note).with(project, new_issue.iid, expected_posted_note)
expect { subject.invoke! }.to output.to_stdout
end
......
......@@ -18,21 +18,21 @@ describe Gitlab::QA::Reporter do
end
end
describe 'when reporting in issues' do
describe 'when reporting test results' do
it 'requires input files to be specified' do
expect { described_class.invoke('--report-in-issues') }
.to raise_error(OptionParser::MissingArgument, 'missing argument: --report-in-issues')
expect { described_class.invoke('--report-results') }
.to raise_error(OptionParser::MissingArgument, 'missing argument: --report-results')
end
it 'accepts provided files, token, and project' do
report_in_issues = double('Gitlab::QA::Report::ReportInIssues')
allow(report_in_issues).to receive(:invoke!)
it 'accepts provided files, token, and 2 projects' do
report_results = double('Gitlab::QA::Report::ReportResults')
allow(report_results).to receive(:invoke!)
expect(Gitlab::QA::Report::ResultsInIssues).to receive(:new)
.with(input_files: 'files', token: 'token', project: 'project')
.and_return(report_in_issues)
expect(Gitlab::QA::Report::ReportResults).to receive(:new)
.with(input_files: 'files', token: 'token', test_case_project: 'project1', results_issue_project: 'project2')
.and_return(report_results)
described_class.invoke(%w[--report-in-issues files -t token -p project])
described_class.invoke(%w[--report-results files -t token --test_case_project project1 --results-issue-project project2])
end
end
......