fastlane-plugin-test_center 3.8.4 → 3.8.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39540c7e8c08868f26fe01d1c612a636da7ce8bfc5d73b2f208fd5e20109c46f
4
- data.tar.gz: f86f81c817d44ffe631aad423a947e1746d07ac9aa4a3e461c0c92929c7767e1
3
+ metadata.gz: ea22b1e286af67f521152ef2ba5862103ff5f3c75602ff03ac7d9a2641008dc6
4
+ data.tar.gz: e74c7f19184c067e0640f7fd8c4cf05724e7419dd8814dab498931b5b42f8fa0
5
5
  SHA512:
6
- metadata.gz: a7d0040074f94705bb076784dfbc175b41e1540daf2e203feae942d2652fbead5a557866e474aad4a2bacd2b7f3c6278e306987318af11fa31b31c2fd01b3d69
7
- data.tar.gz: 50955867e5915082f0c818ef42137ce1cd30499359dbf34caa61aed1691c703de35945d4ab9e372e5a9cd0ddfee48390ef494a42579d2bccb04031f131e6d156
6
+ metadata.gz: 18c8938a1f216b4754f905c2402299d642855fb639b05d0e1c7dcfcea3bffb9452b7cd45c117daf639b079677dd51a82726d6cf2e016d4fe6ab119ccfaa298b6
7
+ data.tar.gz: ea8206538bf8c600ca725a1c2cceb3e1c697b16c2f895ea01c0b194422a9c4c835de47aa1240ada12f55aa2170b28c90684b638a233993548415f307bad08720
@@ -11,18 +11,11 @@ module Fastlane
11
11
  # copy any missing testsuites
12
12
  target_report = reports.shift
13
13
  reports.each do |report|
14
- report.elements.each("//section[contains(@class, 'test-suite')]") do |testsuite|
15
- collate_testsuite(testsuite_from_report(target_report, testsuite), testsuite)
16
- end
14
+ target_report.collate_report(report)
17
15
  end
18
- update_testsuites_status(target_report)
19
- update_test_counts(target_report)
20
16
 
21
17
  FileUtils.mkdir_p(File.dirname(params[:collated_report]))
22
-
23
- File.open(params[:collated_report], 'w') do |f|
24
- target_report.write(f, 2)
25
- end
18
+ target_report.save_report(params[:collated_report])
26
19
  end
27
20
  end
28
21
 
@@ -31,7 +24,7 @@ module Fastlane
31
24
  report = nil
32
25
  repair_attempted = false
33
26
  begin
34
- report = REXML::Document.new(File.new(report_filepath))
27
+ report = ::TestCenter::Helper::HtmlTestReport::Report.new(REXML::Document.new(File.new(report_filepath)))
35
28
  rescue REXML::ParseException => e
36
29
  if repair_attempted
37
30
  UI.important("'#{report_filepath}' is malformed and :collate_html_reports cannot repair it")
@@ -63,98 +56,6 @@ module Fastlane
63
56
  end
64
57
  end
65
58
 
66
- def self.testsuite_from_report(report, testsuite)
67
- testsuite_name = testsuite.attribute('id').value
68
- REXML::XPath.first(report, "//section[contains(@class, 'test-suite') and @id='#{testsuite_name}']")
69
- end
70
-
71
- def self.testcases_from_testsuite(testsuite)
72
- REXML::XPath.match(testsuite, ".//*[contains(@class, 'tests')]//*[contains(@class, 'test')]//*[contains(@class, 'title')]")
73
- end
74
-
75
- def self.testcase_from_testsuite(testsuite, testcase_name)
76
- REXML::XPath.first(testsuite, "*[contains(@class, 'test')]//*[text()='#{testcase_name}']/../..")
77
- end
78
-
79
- def self.collate_testsuite(target_testsuite, testsuite)
80
- if target_testsuite
81
- testcases = testcases_from_testsuite(testsuite)
82
- testcases.each do |testcase|
83
- testresult = testcase.parent.parent
84
- target_testresult = testcase_from_testsuite(target_testsuite, testcase.text)
85
- collate_testresults(target_testsuite, target_testresult, testresult)
86
- end
87
- else
88
- testable = testsuite.parent
89
- testable << testsuite
90
- end
91
- end
92
-
93
- def self.collate_testresults(target_testsuite, target_testresult, testresult)
94
- if target_testresult
95
- collate_testresult_details(target_testresult, testresult)
96
- target_testresult.parent.replace_child(target_testresult, testresult)
97
- else
98
- target_testsuite << testresult
99
- end
100
- end
101
-
102
- def self.collate_testresult_details(target_testresult, testresult)
103
- target_testdetails = details_for_testresult(target_testresult)
104
- testdetails = details_for_testresult(testresult)
105
-
106
- if target_testdetails
107
- if testdetails
108
- target_testresult.parent.replace_child(target_testdetails, testdetails)
109
- else
110
- target_testresult.parent.delete_element(target_testdetails)
111
- end
112
- end
113
- end
114
-
115
- def self.update_testsuites_status(report)
116
- report.elements.each("//section[contains(@class, 'test-suite')]") do |testsuite|
117
- failing_tests_xpath = "./*[contains(@class, 'tests')]//*[" \
118
- "contains(@class, 'failing')]"
119
-
120
- class_attributes = testsuite.attribute('class').value
121
- test_failures = REXML::XPath.match(testsuite, failing_tests_xpath)
122
- test_status = test_failures.size.zero? ? 'passing' : 'failing'
123
-
124
- testsuite.add_attribute('class', class_attributes.sub('failing', test_status))
125
- end
126
- end
127
-
128
- def self.update_test_counts(report)
129
- tests_xpath = "//*[contains(@class, 'tests')]//*[contains(@class, 'test')]//*[contains(@class, 'title')]"
130
- tests = REXML::XPath.match(report, tests_xpath)
131
-
132
- failing_tests_xpath = "//*[contains(@class, 'tests')]//*[" \
133
- "contains(@class, 'details') and " \
134
- "contains(@class, 'failing')]"
135
-
136
- test_failures = REXML::XPath.match(report, failing_tests_xpath)
137
- test_count = REXML::XPath.first(report, ".//*[@id='test-count']/span")
138
- if test_count
139
- test_count.text = tests.size
140
- end
141
- fail_count = REXML::XPath.first(report, ".//*[@id='fail-count']/span")
142
- if fail_count
143
- fail_count.text = test_failures.size
144
- end
145
- end
146
-
147
- def self.details_for_testresult(testresult)
148
- testcase = REXML::XPath.first(testresult, ".//*[contains(@class, 'title')]")
149
-
150
- xpath = "../*[" \
151
- "contains(@class, 'details') and " \
152
- "contains(@class, 'failing') and " \
153
- "contains(@class, '#{testcase.text}')]"
154
-
155
- REXML::XPath.first(testresult, xpath)
156
- end
157
-
158
59
  #####################################################
159
60
  # @!group Documentation
160
61
  #####################################################
@@ -0,0 +1,282 @@
1
+ module TestCenter
2
+ module Helper
3
+ module HtmlTestReport
4
+ class Report
5
+ require 'rexml/formatters/transitive'
6
+
7
+ attr_reader :root
8
+ def initialize(html_file)
9
+ @root = html_file.root
10
+ end
11
+
12
+ def testsuites
13
+ testsuite_elements = REXML::XPath.match(@root, "//section[contains(@class, 'test-suite')]")
14
+ testsuite_elements.map do |testsuite_element|
15
+ TestSuite.new(testsuite_element)
16
+ end
17
+ end
18
+
19
+ def add_testsuite(testsuite)
20
+ testsuites_element = REXML::XPath.first(@root, ".//*[@id='test-suites']")
21
+ testsuites_element.push(testsuite.root)
22
+ end
23
+
24
+ def collate_report(report)
25
+ testsuites.each(&:remove_duplicate_testcases)
26
+ report.testsuites.each(&:remove_duplicate_testcases)
27
+ FastlaneCore::UI.verbose("TestCenter::Helper::HtmlTestReport::Report.collate_report to report:\n\t#{@root}")
28
+ report.testsuites.each do |given_testsuite|
29
+ existing_testsuite = testsuite_with_title(given_testsuite.title)
30
+ if existing_testsuite.nil?
31
+ FastlaneCore::UI.verbose("\tadding testsuite\n\t\t#{given_testsuite}")
32
+ add_testsuite(given_testsuite)
33
+ else
34
+ FastlaneCore::UI.verbose("\tcollating testsuite\n\t\t#{given_testsuite.root}")
35
+ existing_testsuite.collate_testsuite(given_testsuite)
36
+ FastlaneCore::UI.verbose("\tafter collation exiting testsuite\n\t\t#{existing_testsuite.root}")
37
+ end
38
+ end
39
+ update_test_count
40
+ update_fail_count
41
+ end
42
+
43
+ def testsuite_with_title(title)
44
+ testsuite_element = REXML::XPath.first(@root, ".//*[contains(@id, 'test-suites')]//*[@id='#{title}' and contains(concat(' ', @class, ' '), ' test-suite ')]")
45
+ TestSuite.new(testsuite_element) unless testsuite_element.nil?
46
+ end
47
+
48
+ def test_count
49
+ REXML::XPath.first(@root, ".//*[@id = 'counters']//*[@id='test-count']/*[@class = 'number']/text()").to_s.to_i
50
+ end
51
+
52
+ def set_test_count(test_count)
53
+ test_count_element = REXML::XPath.first(@root, ".//*[@id = 'counters']//*[@id='test-count']/*[@class = 'number']/text()")
54
+ test_count_element.value = test_count.to_s
55
+ end
56
+
57
+ def update_test_count
58
+ testcase_elements = REXML::XPath.match(@root, ".//*[contains(@class, 'tests')]//*[contains(concat(' ', @class, ' '), ' test ')]")
59
+ set_test_count(testcase_elements.size)
60
+ end
61
+
62
+ def fail_count
63
+ fail_count_element = REXML::XPath.first(@root, ".//*[@id = 'counters']//*[@id='fail-count']/*[@class = 'number']/text()")
64
+ return fail_count_element.to_s.to_i if fail_count_element
65
+ return 0
66
+ end
67
+
68
+ def set_fail_count(fail_count)
69
+ counters_element = REXML::XPath.first(@root, ".//*[@id = 'counters']")
70
+ fail_count_number_element = REXML::XPath.first(counters_element, ".//*[@id='fail-count']/*[@class = 'number']/text()")
71
+ if fail_count_number_element
72
+ fail_count_number_element.value = fail_count.to_s
73
+ else
74
+ test_count_element = REXML::XPath.first(counters_element, ".//*[@id='test-count']")
75
+ fail_count_element = test_count_element.clone
76
+ fail_count_element.add_attribute('id', 'fail-count')
77
+
78
+ test_count_element.each_element do |element|
79
+ fail_count_element.add_element(element.clone)
80
+ end
81
+ REXML::XPath.first(fail_count_element, ".//*[@class = 'number']").text = fail_count
82
+ counters_element.add_element(fail_count_element)
83
+ end
84
+ end
85
+
86
+ def update_fail_count
87
+ xpath_class_attributes = [
88
+ "contains(concat(' ', @class, ' '), ' test ')",
89
+ "contains(concat(' ', @class, ' '), ' failing ')"
90
+ ].join(' and ')
91
+
92
+ failing_testcase_elements = REXML::XPath.match(@root, ".//[#{xpath_class_attributes}]")
93
+ set_fail_count(failing_testcase_elements.size)
94
+ end
95
+
96
+ def add_test_center_footer
97
+ test_center_footer = REXML::XPath.first(@root, ".//footer[@id = 'test-center-footer']")
98
+ return if test_center_footer
99
+
100
+ test_center_anchor = REXML::Element.new('a')
101
+ test_center_anchor.text = 'collate_html_reports'
102
+ test_center_anchor.add_attribute('href', 'https://github.com/lyndsey-ferguson/fastlane-plugin-test_center#collate_html_reports')
103
+
104
+ test_center_footer = REXML::Element.new('footer')
105
+ test_center_footer.add_attribute('id', 'test-center-footer')
106
+ test_center_footer.text = 'Collated by the '
107
+ test_center_footer.add_element(test_center_anchor)
108
+ test_center_footer.add_text(' action from the test_center fastlane plugin')
109
+
110
+ body_element = REXML::XPath.first(@root, "//body")
111
+ body_element.elements.add(test_center_footer)
112
+ end
113
+
114
+ def save_report(report_path)
115
+ add_test_center_footer
116
+
117
+ output = ''
118
+ formatter = REXML::Formatters::Transitive.new
119
+ formatter.write(@root, output)
120
+
121
+ File.open(report_path, 'w') do |f|
122
+ f.puts output
123
+ end
124
+ end
125
+ end
126
+
127
+ class TestSuite
128
+ attr_reader :root
129
+
130
+ def initialize(testsuite_element)
131
+ @root = testsuite_element
132
+ end
133
+
134
+ def title
135
+ @root.attribute('id').value
136
+ end
137
+
138
+ def testcases
139
+ testcase_elements = REXML::XPath.match(@root, ".//*[contains(@class, 'tests')]//*[contains(concat(' ', @class, ' '), ' test ')]")
140
+ testcase_elements.map do |testcase_element|
141
+ TestCase.new(testcase_element)
142
+ end
143
+ end
144
+
145
+ def testcase_with_title(title)
146
+ testcase_element = REXML::XPath.first(@root, ".//*[contains(@class, 'tests')]//*[contains(concat(' ', @class, ' '), ' test ')]//*[@class='title']/[normalize-space()='#{title}']/../../..")
147
+ TestCase.new(testcase_element) unless testcase_element.nil?
148
+ end
149
+
150
+ def passing?
151
+ @root.attribute('class').value.include?('passing')
152
+ end
153
+
154
+ def set_passing(status)
155
+ desired_status = status ? ' passing ' : ' failing '
156
+ to_replace = status ? /\bfailing\b/ : /\bpassing\b/
157
+
158
+ attribute = @root.attribute('class').value.sub(to_replace, desired_status)
159
+ attribute.gsub!(/\s{2,}/, ' ')
160
+ @root.add_attribute('class', attribute)
161
+ end
162
+
163
+ def add_testcase(testcase)
164
+ tests_table = REXML::XPath.first(@root, ".//*[contains(@class, 'tests')]/table")
165
+ details = testcase.failure_details
166
+ if details
167
+ tests_table.push(details)
168
+ tests_table.insert_before(details, testcase.root)
169
+ else
170
+ tests_table.push(testcase.root)
171
+ end
172
+ end
173
+
174
+ def duplicate_testcases?
175
+ nonuniq_testcases = testcases
176
+ uniq_testcases = nonuniq_testcases.uniq { |tc| tc.title }
177
+ nonuniq_testcases != uniq_testcases
178
+ end
179
+
180
+ def remove_duplicate_testcases
181
+ nonuniq_testcases = testcases
182
+ uniq_testcases = nonuniq_testcases.uniq { |tc| tc.title }
183
+ (nonuniq_testcases - uniq_testcases).each do |tc|
184
+ failure_details = tc.failure_details
185
+ tc.root.parent.delete_element(failure_details)
186
+ tc.root.parent.delete_element(tc.root)
187
+ end
188
+ end
189
+
190
+ def collate_testsuite(testsuite)
191
+ given_testcases = testsuite.testcases
192
+ given_testcases.each do |given_testcase|
193
+ existing_testcase = testcase_with_title(given_testcase.title)
194
+ if existing_testcase.nil?
195
+ FastlaneCore::UI.verbose("\t\tadding testcase\n\t\t\t#{given_testcase.root}")
196
+ unless given_testcase.passing?
197
+ FastlaneCore::UI.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
198
+ end
199
+ add_testcase(given_testcase)
200
+ else
201
+ FastlaneCore::UI.verbose("\t\tupdating testcase\n\t\t\t#{existing_testcase.root}")
202
+ unless given_testcase.passing?
203
+ FastlaneCore::UI.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
204
+ end
205
+ existing_testcase.update_testcase(given_testcase)
206
+ end
207
+ end
208
+ set_passing(testcases.all?(&:passing?))
209
+ end
210
+ end
211
+
212
+ class TestCase
213
+ attr_reader :root
214
+
215
+ def initialize(testcase_element)
216
+ @root = testcase_element
217
+ end
218
+
219
+ def title
220
+ REXML::XPath.first(@root, ".//h3[contains(@class, 'title')]/text()").to_s.strip
221
+ end
222
+
223
+ def passing?
224
+ @root.attribute('class').value.include?('passing')
225
+ end
226
+
227
+ def row_color
228
+ @root.attribute('class').value.include?('odd') ? 'odd' : ''
229
+ end
230
+
231
+ def set_row_color(row_color)
232
+ raise 'row_color must either be "odd" or ""' unless ['odd', ''].include?(row_color)
233
+
234
+ current_class_attribute = @root.attribute('class').value.sub(/\bodd\b/, '')
235
+ @root.add_attribute('class', current_class_attribute << ' ' << row_color)
236
+ end
237
+
238
+ def failure_details
239
+ return nil if @root.attribute('class').value.include?('passing')
240
+
241
+ xpath_class_attributes = [
242
+ "contains(concat(' ', @class, ' '), ' details ')",
243
+ "contains(concat(' ', @class, ' '), ' failing ')",
244
+ "contains(concat(' ', @class, ' '), ' #{title} ')"
245
+ ].join(' and ')
246
+
247
+ REXML::XPath.first(@root.parent, ".//[#{xpath_class_attributes}]")
248
+ end
249
+
250
+ def remove_failure_details
251
+ details = failure_details
252
+ return if details.nil?
253
+
254
+ details.parent.delete_element(details)
255
+ end
256
+
257
+ def update_testcase(testcase)
258
+ color = row_color
259
+ failure = failure_details
260
+ if failure.nil? && !passing?
261
+ FastlaneCore::UI.error("\t\t\t\tupdating failing test case that does not have failure_details")
262
+ end
263
+ parent = @root.parent
264
+
265
+ failure.parent.delete(failure) unless failure.nil?
266
+
267
+ new_failure = testcase.failure_details
268
+ if new_failure && testcase.passing?
269
+ FastlaneCore::UI.error("\t\t\t\tswapping passing failing test case that _does_have_ failure_details")
270
+ end
271
+
272
+ parent.replace_child(@root, testcase.root)
273
+ @root = testcase.root
274
+ unless new_failure.nil?
275
+ parent.insert_after(@root, new_failure)
276
+ end
277
+ set_row_color(color)
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
@@ -57,7 +57,7 @@ module TestCenter
57
57
  )
58
58
  CollateJunitReportsAction.run(config)
59
59
  FileUtils.rm_rf(report_files - [collated_file])
60
- elsif report_files.size == 1 && report_files.first != collated_file
60
+ elsif report_files.size == 1 && ! File.identical?(report_files.first, collated_file)
61
61
  FastlaneCore::UI.verbose("Copying junit report file #{report_files.first}")
62
62
  FileUtils.mkdir_p(File.dirname(collated_file))
63
63
  FileUtils.mv(report_files.first, collated_file)
@@ -80,7 +80,7 @@ module TestCenter
80
80
  )
81
81
  CollateHtmlReportsAction.run(config)
82
82
  FileUtils.rm_rf(report_files - [collated_file])
83
- elsif report_files.size == 1 && report_files.first != collated_file
83
+ elsif report_files.size == 1 && ! File.identical?(report_files.first, collated_file)
84
84
  FastlaneCore::UI.verbose("Copying html report file #{report_files.first}")
85
85
  FileUtils.mkdir_p(File.dirname(collated_file))
86
86
  FileUtils.mv(report_files.first, collated_file)
@@ -103,7 +103,7 @@ module TestCenter
103
103
  )
104
104
  CollateJsonReportsAction.run(config)
105
105
  FileUtils.rm_rf(report_files - [collated_file])
106
- elsif report_files.size == 1 && report_files.first != collated_file
106
+ elsif report_files.size == 1 && ! File.identical?(report_files.first, collated_file)
107
107
  FastlaneCore::UI.verbose("Copying json report file #{report_files.first}")
108
108
  FileUtils.mkdir_p(File.dirname(collated_file))
109
109
  FileUtils.mv(report_files.first, collated_file)
@@ -126,7 +126,7 @@ module TestCenter
126
126
  )
127
127
  CollateTestResultBundlesAction.run(config)
128
128
  FileUtils.rm_rf(test_result_bundlepaths - [collated_test_result_bundlepath])
129
- elsif test_result_bundlepaths.size == 1 && test_result_bundlepaths.first != collated_test_result_bundlepath
129
+ elsif test_result_bundlepaths.size == 1 && File.realdirpath(test_result_bundlepaths.first) != File.realdirpath(collated_test_result_bundlepath)
130
130
  FastlaneCore::UI.verbose("Copying test_result bundle #{test_result_bundlepaths.first}")
131
131
  FileUtils.mkdir_p(File.dirname(collated_test_result_bundlepath))
132
132
  FileUtils.mv(test_result_bundlepaths.first, collated_test_result_bundlepath)
@@ -30,8 +30,13 @@ module TestCenter
30
30
  .merge(@retrying_scan_helper.scan_options)
31
31
 
32
32
  prepare_scan_config_for_destination
33
+ scan_options[:build_for_testing] = false
34
+ FastlaneCore::UI.verbose("retrying_scan #update_scan_options")
33
35
  scan_options.each do |k,v|
36
+ next if v.nil?
37
+
34
38
  scan_config.set(k,v) unless v.nil?
39
+ FastlaneCore::UI.verbose("\tSetting #{k.to_s} to #{v}")
35
40
  end
36
41
  end
37
42
 
@@ -81,6 +81,10 @@ module TestCenter
81
81
  def scan_options
82
82
  valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
83
83
  xcargs = @options[:xcargs]
84
+ if xcargs&.include?('build-for-testing')
85
+ FastlaneCore::UI.important(":xcargs, #{xcargs}, contained 'build-for-testing', removing it")
86
+ xcargs.slice!('build-for-testing')
87
+ end
84
88
  retrying_scan_options = @reportnamer.scan_options.merge(
85
89
  {
86
90
  output_directory: output_directory,
@@ -153,8 +157,8 @@ module TestCenter
153
157
  junit_results, report_filepath = failure_details(additional_info)
154
158
 
155
159
  info = {
156
- failed: junit_results[:failed],
157
- passing: junit_results[:passing],
160
+ failed: junit_results.fetch(:failed, []),
161
+ passing: junit_results.fetch(:passing, []),
158
162
  batch: @options[:batch] || 1,
159
163
  try_count: @testrun_count,
160
164
  report_filepath: report_filepath
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "3.8.4"
3
+ VERSION = "3.8.5"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-test_center
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.4
4
+ version: 3.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lyndsey Ferguson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2019-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -255,6 +255,7 @@ files:
255
255
  - lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
256
256
  - lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
257
257
  - lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
258
+ - lib/fastlane/plugin/test_center/helper/html_test_report.rb
258
259
  - lib/fastlane/plugin/test_center/helper/junit_helper.rb
259
260
  - lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb
260
261
  - lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb