fastlane-plugin-xcresult_to_junit 0.3.2 → 0.4.0
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 +4 -4
- data/README.md +1 -1
- data/lib/fastlane/plugin/xcresult_to_junit/actions/xcresult_to_junit_action.rb +70 -109
- data/lib/fastlane/plugin/xcresult_to_junit/helper/xcresult_to_junit_helper.rb +21 -20
- data/lib/fastlane/plugin/xcresult_to_junit/version.rb +3 -1
- data/lib/fastlane/plugin/xcresult_to_junit.rb +2 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26d80a12ddd11ffb1a610f03fbe2b553e429e50318230aab292e05a7af7c562d
|
4
|
+
data.tar.gz: ead5297fa4ef5adcf6225ba13229202143a51ae2b0194f45d1452fcd1c878cef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 889460cd463ecfabf20d363fcb1cd8940eb2e9390950403fcd5409d3a9e566b0dee6aa3200cba0b0869776c87063da9af3a78a4418c0a435c04a8b7fe16fda97
|
7
|
+
data.tar.gz: 0f69ba041ff93232e524f11039b8e703ec06d0d1a26dc3a92132749f81613a4f036afaa8acd74249108d53af3608e9e7eee2ca8ff7139a30969a22a1b4ab78b5
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'fastlane/action'
|
3
5
|
require_relative '../helper/xcresult_to_junit_helper'
|
@@ -6,120 +8,79 @@ module Fastlane
|
|
6
8
|
module Actions
|
7
9
|
class XcresultToJunitAction < Action
|
8
10
|
def self.run(params)
|
9
|
-
UI.message(
|
10
|
-
|
11
|
-
all_results.each do |test_run|
|
12
|
-
next unless test_run['actionResult']['testsRef'] # Skip if section has no testRef data as this means its not a test run
|
13
|
-
map = {}
|
14
|
-
junit_folder = Helper::XcresultToJunitHelper.save_device_details_to_file(params[:output_path], test_run['runDestination'])
|
15
|
-
test_run_id = test_run['actionResult']['testsRef']['id']['_value']
|
16
|
-
all_tests = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], test_run_id)['summaries']['_values'][0]['testableSummaries']['_values']
|
17
|
-
test_suites = []
|
18
|
-
all_tests.each do |target|
|
19
|
-
target_name = target['targetName']['_value']
|
20
|
-
unless target['tests']
|
21
|
-
failure_summary = target['failureSummaries']['_values'][0]
|
22
|
-
test_suites << { name: target_name, error: failure_summary['message']['_value'] }
|
23
|
-
next
|
24
|
-
end
|
25
|
-
test_classes = []
|
26
|
-
if defined?(target['tests']['_values'][0]['subtests']['_values'][0]['subtests']['_values'])
|
27
|
-
test_classes = target['tests']['_values'][0]['subtests']['_values'][0]['subtests']['_values']
|
28
|
-
end
|
29
|
-
test_classes.each do |test_class|
|
30
|
-
suite_name = "#{target_name}.#{test_class['name']['_value']}"
|
31
|
-
suite = { name: suite_name, cases: [] }
|
32
|
-
if test_class['subtests']
|
33
|
-
test_class['subtests']['_values'].each do |test|
|
34
|
-
duration = 0
|
35
|
-
duration = test['duration']['_value'] if test['duration']
|
36
|
-
testcase_name = test['name']['_value'].tr('()', '')
|
37
|
-
tags = testcase_name.split('_')[1..-1]
|
38
|
-
testcase_name = testcase_name.split('_').first
|
39
|
-
testcase = { name: testcase_name, time: duration }
|
40
|
-
map["#{suite_name}.#{testcase_name}"] = { 'files' => [], 'tags' => tags }
|
11
|
+
UI.message('The xcresult_to_junit plugin has started!')
|
12
|
+
result = Helper::XcresultToJunitHelper.fetch_tests(params[:xcresult_path])
|
41
13
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
next unless summary['attachments']
|
48
|
-
summary['attachments']['_values'].each do |attachment|
|
49
|
-
name = attachment['name']['_value']
|
50
|
-
next unless name != 'kXCTAttachmentLegacyDiagnosticReportData'
|
51
|
-
timestamp = DateTime.parse(attachment['timestamp']['_value']).to_time.to_i
|
52
|
-
filename = attachment['filename']['_value']
|
53
|
-
folder_name = "#{suite_name}.#{testcase_name}"
|
54
|
-
id = attachment.dig('payloadRef', 'id', '_value')
|
55
|
-
next if id.nil?
|
56
|
-
Helper::XcresultToJunitHelper.fetch_screenshot(params[:xcresult_path], "#{junit_folder}/attachments/#{folder_name}", filename.to_s, id)
|
57
|
-
map[folder_name]['files'].push({ 'description' => name, 'mime-type' => 'image/png', 'path' => "#{folder_name}/#{filename}", 'timestamp' => timestamp })
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
14
|
+
devices = result['devices']
|
15
|
+
test_plans = result['testNodes']
|
16
|
+
map = {}
|
17
|
+
junit_folder = Helper::XcresultToJunitHelper.save_device_details_to_file(params[:output_path], devices)
|
18
|
+
test_suites = []
|
61
19
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
performancemetric = "\nMetric: #{metricname}\nResult: #{metricresult}%\nAverage: #{metricave}#{metricunit}\nBaseline: #{metricbaseline}#{metricunit}\nMax Deviation: #{metricmaxdev}%\n\n"
|
86
|
-
performancemetrics << performancemetric
|
87
|
-
end
|
88
|
-
testcase[:performance] = performancemetrics
|
89
|
-
end
|
20
|
+
test_plans.each do |test_plan|
|
21
|
+
test_plan['children'].each do |test_bundle|
|
22
|
+
test_bundle['children'].each do |test_suite|
|
23
|
+
suite_name = test_suite['name']
|
24
|
+
count = 0
|
25
|
+
passed = 0
|
26
|
+
failed = 0
|
27
|
+
test_cases = []
|
28
|
+
test_suite['children'].each do |test_case|
|
29
|
+
duration = 0.0
|
30
|
+
duration = test_case['duration'].sub('s', '').to_f if test_case['duration']
|
31
|
+
full_testcase_name = test_case['name'].sub('()', '')
|
32
|
+
tags = full_testcase_name.split('_')[1..]
|
33
|
+
testcase = { name: full_testcase_name, time: duration }
|
34
|
+
count += 1
|
35
|
+
if test_case['result'] == 'Passed'
|
36
|
+
passed += 1
|
37
|
+
elsif test_case['result'] == 'Failed'
|
38
|
+
failed += 1
|
39
|
+
test_case['children'].each do |failure|
|
40
|
+
testcase[:failure] = failure['name'].split(': ')[1]
|
41
|
+
testcase[:failure_location] = failure['name'].split(': ')[0]
|
90
42
|
end
|
91
|
-
if test['testStatus']['_value'] == 'Failure'
|
92
|
-
failure = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], test['summaryRef']['id']['_value'])['failureSummaries']['_values'][0]
|
93
|
-
filename = failure.dig('fileName', '_value')
|
94
|
-
message = failure['message']['_value']
|
95
|
-
if filename == '<unknown>' || filename.nil?
|
96
|
-
testcase[:error] = message
|
97
|
-
else
|
98
|
-
testcase[:failure] = message
|
99
|
-
testcase[:failure_location] = "#{filename}:#{failure['lineNumber']['_value']}"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
suite[:cases] << testcase
|
103
43
|
end
|
44
|
+
test_cases << testcase
|
45
|
+
map["#{suite_name}.#{full_testcase_name}"] = { 'files' => [], 'tags' => tags }
|
104
46
|
end
|
105
|
-
suite
|
106
|
-
suite[:failures] = suite[:cases].count { |testcase| testcase[:failure] }
|
107
|
-
suite[:errors] = suite[:cases].count { |testcase| testcase[:error] }
|
47
|
+
suite = { name: suite_name.to_s, count: count, failures: failed, errors: 0, cases: test_cases }
|
108
48
|
test_suites << suite
|
109
49
|
end
|
110
50
|
end
|
111
|
-
Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
|
112
|
-
Helper::XcresultToJunitHelper.save_screenshot_mapping(map, "#{junit_folder}/attachments/")
|
113
51
|
end
|
114
|
-
|
52
|
+
|
53
|
+
Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
|
54
|
+
attachments_folder = "#{junit_folder}/attachments"
|
55
|
+
Helper::XcresultToJunitHelper.save_attachments(params[:xcresult_path], attachments_folder)
|
56
|
+
|
57
|
+
test_attachments = Helper::XcresultToJunitHelper.fetch_attachment_manifest(attachments_folder)
|
58
|
+
|
59
|
+
test_attachments.each do |test_attachment|
|
60
|
+
test_identifier = test_attachment['testIdentifier']
|
61
|
+
folder_name = test_identifier.sub('()', '').sub('/', '.')
|
62
|
+
|
63
|
+
test_attachment['attachments'].each do |attachment|
|
64
|
+
name = attachment['suggestedHumanReadableName']
|
65
|
+
filename = attachment['exportedFileName']
|
66
|
+
mime_type = 'image/png'
|
67
|
+
mime_type = 'text/plain' if filename.end_with?('.txt')
|
68
|
+
timestamp = attachment['timestamp']
|
69
|
+
map[folder_name]['files'].push({ 'description' => name, 'mime-type' => mime_type, 'path' => filename,
|
70
|
+
'timestamp' => timestamp })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Helper::XcresultToJunitHelper.save_screenshot_mapping(map, attachments_folder)
|
75
|
+
UI.message('The xcresult_to_junit plugin has finished!')
|
115
76
|
end
|
116
77
|
|
117
78
|
def self.description
|
118
|
-
|
79
|
+
'Produces junit xml files from Xcode 11+ xcresult files'
|
119
80
|
end
|
120
81
|
|
121
82
|
def self.authors
|
122
|
-
[
|
83
|
+
['Shane Birdsall']
|
123
84
|
end
|
124
85
|
|
125
86
|
def self.return_value
|
@@ -127,26 +88,26 @@ module Fastlane
|
|
127
88
|
end
|
128
89
|
|
129
90
|
def self.details
|
130
|
-
|
91
|
+
'By using the xcresulttool this plugin parses xcresult files and generates junit reports to be used with other tools to display iOS test results'
|
131
92
|
end
|
132
93
|
|
133
94
|
def self.available_options
|
134
95
|
[
|
135
96
|
FastlaneCore::ConfigItem.new(key: :xcresult_path,
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
97
|
+
env_name: 'XCRESULT_TO_JUNIT_XCRESULT_PATH',
|
98
|
+
description: 'The path to the xcresult file',
|
99
|
+
optional: false,
|
100
|
+
type: String),
|
140
101
|
FastlaneCore::ConfigItem.new(key: :output_path,
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
102
|
+
env_name: 'XCRESULT_TO_JUNIT_OUTPUT_PATH',
|
103
|
+
description: 'The path where the output will be placed',
|
104
|
+
optional: false,
|
105
|
+
type: String)
|
145
106
|
]
|
146
107
|
end
|
147
108
|
|
148
109
|
def self.is_supported?(platform)
|
149
|
-
[
|
110
|
+
%i[ios mac].include?(platform)
|
150
111
|
end
|
151
112
|
end
|
152
113
|
end
|
@@ -1,25 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fastlane_core/ui/ui'
|
2
4
|
require 'json'
|
3
5
|
require 'fileutils'
|
4
6
|
|
5
7
|
module Fastlane
|
6
|
-
UI = FastlaneCore::UI unless Fastlane.const_defined?(
|
8
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
|
7
9
|
|
8
10
|
module Helper
|
9
11
|
class XcresultToJunitHelper
|
10
|
-
def self.
|
11
|
-
JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get
|
12
|
+
def self.fetch_tests(xcresult_path)
|
13
|
+
JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get test-results tests --path #{xcresult_path}"))
|
12
14
|
end
|
13
15
|
|
14
|
-
def self.
|
15
|
-
|
16
|
+
def self.save_attachments(xcresult_path, output_path)
|
17
|
+
FileUtils.mkdir(output_path) unless File.directory?(output_path)
|
18
|
+
FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool export attachments --path #{xcresult_path} --output-path #{output_path}")
|
16
19
|
end
|
17
20
|
|
18
|
-
def self.
|
19
|
-
|
20
|
-
FileUtils.mkdir(output_path)
|
21
|
-
end
|
22
|
-
FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool export --legacy --path #{xcresult_path} --output-path \"#{output_path}/#{file_name}\" --id #{id} --type file")
|
21
|
+
def self.fetch_attachment_manifest(attachments_folder)
|
22
|
+
JSON.parse(FastlaneCore::CommandExecutor.execute(command: "cat #{attachments_folder}/manifest.json"))
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.save_screenshot_mapping(map_hash, output_path)
|
@@ -28,21 +28,24 @@ module Fastlane
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def self.save_device_details_to_file(output_path,
|
32
|
-
|
31
|
+
def self.save_device_details_to_file(output_path, devices)
|
32
|
+
device = devices[0]
|
33
33
|
device_details = {
|
34
|
-
'
|
35
|
-
'
|
36
|
-
'
|
34
|
+
'architecture' => device['architecture'],
|
35
|
+
'udid' => device['deviceId'],
|
36
|
+
'name' => device['deviceName'],
|
37
|
+
'model' => device['modelName'],
|
38
|
+
'os' => device['osVersion'],
|
39
|
+
'platform' => device['platform']
|
37
40
|
}.to_json
|
38
41
|
|
39
|
-
junit_folder = "#{output_path}/ios-#{
|
42
|
+
junit_folder = "#{output_path}/ios-#{device['deviceId']}.junit"
|
40
43
|
FileUtils.rm_rf(junit_folder)
|
41
44
|
FileUtils.mkdir_p("#{junit_folder}/attachments")
|
42
45
|
File.open("#{junit_folder}/device.json", 'w') do |f|
|
43
46
|
f << device_details
|
44
47
|
end
|
45
|
-
|
48
|
+
junit_folder
|
46
49
|
end
|
47
50
|
|
48
51
|
def self.junit_file_start
|
@@ -104,9 +107,7 @@ module Fastlane
|
|
104
107
|
elsif testcase[:error]
|
105
108
|
Helper::XcresultToJunitHelper.junit_testcase_error(testcase)
|
106
109
|
end
|
107
|
-
if testcase[:performance]
|
108
|
-
Helper::XcresultToJunitHelper.junit_testcase_performance(testcase)
|
109
|
-
end
|
110
|
+
Helper::XcresultToJunitHelper.junit_testcase_performance(testcase) if testcase[:performance]
|
110
111
|
Helper::XcresultToJunitHelper.junit_testcase_end
|
111
112
|
end
|
112
113
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-xcresult_to_junit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Birdsall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pry
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rspec_junit_formatter
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|