fastlane-plugin-test_center 3.8.4 → 3.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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