fastlane 2.226.0 → 2.227.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +96 -96
  3. data/cert/lib/cert/options.rb +7 -2
  4. data/cert/lib/cert/runner.rb +23 -11
  5. data/deliver/lib/deliver/options.rb +1 -1
  6. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +1 -1
  7. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +1 -1
  8. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +1 -1
  9. data/fastlane/lib/fastlane/actions/notarize.rb +4 -0
  10. data/fastlane/lib/fastlane/actions/onesignal.rb +1 -1
  11. data/fastlane/lib/fastlane/actions/setup_ci.rb +14 -4
  12. data/fastlane/lib/fastlane/actions/testfairy.rb +5 -2
  13. data/fastlane/lib/fastlane/actions/unlock_keychain.rb +6 -1
  14. data/fastlane/lib/fastlane/version.rb +1 -1
  15. data/fastlane/swift/Actions.swift +1 -1
  16. data/fastlane/swift/Appfile.swift +1 -1
  17. data/fastlane/swift/ArgumentProcessor.swift +1 -1
  18. data/fastlane/swift/Atomic.swift +1 -1
  19. data/fastlane/swift/ControlCommand.swift +1 -1
  20. data/fastlane/swift/Deliverfile.swift +2 -2
  21. data/fastlane/swift/DeliverfileProtocol.swift +2 -2
  22. data/fastlane/swift/Fastlane.swift +20 -10
  23. data/fastlane/swift/Gymfile.swift +2 -2
  24. data/fastlane/swift/GymfileProtocol.swift +2 -2
  25. data/fastlane/swift/LaneFileProtocol.swift +1 -1
  26. data/fastlane/swift/MainProcess.swift +1 -1
  27. data/fastlane/swift/Matchfile.swift +2 -2
  28. data/fastlane/swift/MatchfileProtocol.swift +2 -2
  29. data/fastlane/swift/OptionalConfigValue.swift +1 -1
  30. data/fastlane/swift/Plugins.swift +1 -1
  31. data/fastlane/swift/Precheckfile.swift +2 -2
  32. data/fastlane/swift/PrecheckfileProtocol.swift +2 -2
  33. data/fastlane/swift/RubyCommand.swift +1 -1
  34. data/fastlane/swift/RubyCommandable.swift +1 -1
  35. data/fastlane/swift/Runner.swift +1 -1
  36. data/fastlane/swift/RunnerArgument.swift +1 -1
  37. data/fastlane/swift/Scanfile.swift +2 -2
  38. data/fastlane/swift/ScanfileProtocol.swift +2 -2
  39. data/fastlane/swift/Screengrabfile.swift +2 -2
  40. data/fastlane/swift/ScreengrabfileProtocol.swift +2 -2
  41. data/fastlane/swift/Snapshotfile.swift +2 -2
  42. data/fastlane/swift/SnapshotfileProtocol.swift +2 -2
  43. data/fastlane/swift/SocketClient.swift +1 -1
  44. data/fastlane/swift/SocketClientDelegateProtocol.swift +1 -1
  45. data/fastlane/swift/SocketResponse.swift +1 -1
  46. data/fastlane/swift/main.swift +1 -1
  47. data/fastlane_core/lib/fastlane_core/helper.rb +6 -1
  48. data/match/lib/assets/READMETemplate.md +2 -2
  49. data/match/lib/match/generator.rb +2 -2
  50. data/match/lib/match/runner.rb +1 -1
  51. data/precheck/lib/precheck/options.rb +1 -1
  52. data/produce/lib/produce/options.rb +1 -1
  53. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +1 -0
  54. data/supply/lib/supply/uploader.rb +19 -12
  55. data/trainer/lib/trainer/legacy_xcresult.rb +586 -0
  56. data/trainer/lib/trainer/options.rb +5 -0
  57. data/trainer/lib/trainer/plist_test_summary_parser.rb +84 -0
  58. data/trainer/lib/trainer/test_parser.rb +12 -293
  59. data/trainer/lib/trainer/xcresult/helper.rb +53 -0
  60. data/trainer/lib/trainer/xcresult/repetition.rb +39 -0
  61. data/trainer/lib/trainer/xcresult/test_case.rb +221 -0
  62. data/trainer/lib/trainer/xcresult/test_case_attributes.rb +49 -0
  63. data/trainer/lib/trainer/xcresult/test_plan.rb +91 -0
  64. data/trainer/lib/trainer/xcresult/test_suite.rb +134 -0
  65. data/trainer/lib/trainer/xcresult.rb +31 -388
  66. data/trainer/lib/trainer.rb +3 -1
  67. metadata +29 -21
@@ -0,0 +1,49 @@
1
+ module Trainer
2
+ module XCResult
3
+ # Mixin for shared attributes between TestCase and Repetition
4
+ module TestCaseAttributes
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def passed?
10
+ @result == 'Passed'
11
+ end
12
+
13
+ def failed?
14
+ @result == 'Failed'
15
+ end
16
+
17
+ def skipped?
18
+ @result == 'Skipped'
19
+ end
20
+
21
+ module ClassMethods
22
+ def parse_duration(duration_str)
23
+ return 0.0 if duration_str.nil?
24
+
25
+ # Handle comma-separated duration, and remove 's' suffix
26
+ duration_str.gsub(',', '.').chomp('s').to_f
27
+ end
28
+
29
+ def extract_failure_messages(node)
30
+ node['children']
31
+ &.select { |child| child['nodeType'] == 'Failure Message' }
32
+ &.map { |msg| msg['name'] } || []
33
+ end
34
+
35
+ def extract_source_references(node)
36
+ node['children']
37
+ &.select { |child| child['nodeType'] == 'Source Code Reference' }
38
+ &.map { |ref| ref['name'] } || []
39
+ end
40
+
41
+ def extract_attachments(node)
42
+ node['children']
43
+ &.select { |child| child['nodeType'] == 'Attachment' }
44
+ &.map { |attachment| attachment['name'] } || []
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,91 @@
1
+ require_relative 'test_suite'
2
+
3
+ module Trainer
4
+ module XCResult
5
+ # Represents a collection of test suites + the configuration, and device used to run them
6
+ class TestPlan
7
+ attr_reader :test_suites, :name, :configurations, :devices
8
+ attr_accessor :output_remove_retry_attempts
9
+
10
+ def initialize(test_suites:, configurations: [], devices: [], output_remove_retry_attempts: false)
11
+ @test_suites = test_suites
12
+ @configurations = configurations
13
+ @devices = devices
14
+ @output_remove_retry_attempts = output_remove_retry_attempts
15
+ end
16
+
17
+ def self.from_json(json:)
18
+ # Extract configurations and devices
19
+ configurations = json['testPlanConfigurations'] || []
20
+ devices = json['devices'] || []
21
+
22
+ # Find the test plan node (root of test results)
23
+ test_plan_node = json['testNodes']&.find { |node| node['nodeType'] == 'Test Plan' }
24
+ return new(test_suites: []) if test_plan_node.nil?
25
+
26
+ # Convert test plan node's children (test bundles) to TestSuite objects
27
+ test_suites = test_plan_node['children']&.map do |test_bundle|
28
+ TestSuite.from_json(
29
+ node: test_bundle
30
+ )
31
+ end || []
32
+
33
+ new(
34
+ test_suites: test_suites,
35
+ configurations: configurations,
36
+ devices: devices
37
+ )
38
+ end
39
+
40
+ # Allows iteration over test suites. Used by TestParser to collect test results
41
+ include Enumerable
42
+ def each(&block)
43
+ test_suites.map(&:to_hash).each(&block)
44
+ end
45
+
46
+ # Generates a JUnit-compatible XML representation of the test plan
47
+ # See https://github.com/testmoapp/junitxml/
48
+ def to_xml
49
+ # Create the root testsuites element with calculated summary attributes
50
+ testsuites = Helper.create_xml_element('testsuites',
51
+ tests: test_suites.sum(&:test_cases_count).to_s,
52
+ failures: test_suites.sum(&:failures_count).to_s,
53
+ skipped: test_suites.sum(&:skipped_count).to_s,
54
+ time: test_suites.sum(&:duration).to_s)
55
+
56
+ # Create <properties> node for configuration and device, to be applied to each suite node
57
+ properties = Helper.create_xml_element('properties').tap do |node|
58
+ @configurations.each do |config|
59
+ config_prop = Helper.create_xml_element('property', name: 'Configuration', value: config['configurationName'])
60
+ node.add_element(config_prop)
61
+ end
62
+
63
+ @devices.each do |device|
64
+ device_prop = Helper.create_xml_element('property', name: 'device', value: "#{device.fetch('modelName', 'Unknown Device')} (#{device.fetch('osVersion', 'Unknown OS Version')})")
65
+ node.add_element(device_prop)
66
+ end
67
+ end
68
+
69
+ # Add each test suite to the root
70
+ test_suites.each do |suite|
71
+ suite_node = suite.to_xml(output_remove_retry_attempts: output_remove_retry_attempts)
72
+ # In JUnit conventions, the <testsuites> root element can't have properties
73
+ # So we add the <properties> node to each child <testsuite> node instead
74
+ suite_node.add_element(properties.dup) if properties.elements.any?
75
+ testsuites.add_element(suite_node)
76
+ end
77
+
78
+ # Convert to XML string with prologue
79
+ doc = REXML::Document.new
80
+ doc << REXML::XMLDecl.new('1.0', 'UTF-8')
81
+
82
+ doc.add(testsuites)
83
+
84
+ formatter = REXML::Formatters::Pretty.new
85
+ output = String.new
86
+ formatter.write(doc, output)
87
+ output
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,134 @@
1
+ require_relative 'test_case'
2
+
3
+ module Trainer
4
+ module XCResult
5
+ # Represents a test suite, including its test cases and sub-suites
6
+ class TestSuite
7
+ attr_reader :name
8
+ attr_reader :identifier
9
+ attr_reader :type
10
+ attr_reader :result
11
+ attr_reader :test_cases
12
+ attr_reader :sub_suites
13
+ attr_reader :tags
14
+
15
+ def initialize(name:, identifier:, type:, result:, tags: [], test_cases: [], sub_suites: [])
16
+ @name = name
17
+ @identifier = identifier
18
+ @type = type
19
+ @result = result
20
+ @tags = tags
21
+ @test_cases = test_cases
22
+ @sub_suites = sub_suites
23
+ end
24
+
25
+ def self.from_json(node:)
26
+ # Create initial TestSuite with basic attributes
27
+ test_suite = new(
28
+ name: node['name'],
29
+ identifier: node['nodeIdentifier'],
30
+ type: node['nodeType'],
31
+ result: node['result'],
32
+ tags: node['tags'] || []
33
+ )
34
+
35
+ # Process children to populate test_cases and sub_suites
36
+ test_suite.process_children(node['children'] || [])
37
+
38
+ test_suite
39
+ end
40
+
41
+ def passed?
42
+ @result == 'Passed'
43
+ end
44
+
45
+ def failed?
46
+ @result == 'Failed'
47
+ end
48
+
49
+ def skipped?
50
+ @result == 'Skipped'
51
+ end
52
+
53
+ def duration
54
+ @duration ||= @test_cases.sum(&:duration) + @sub_suites.sum(&:duration)
55
+ end
56
+
57
+ def test_cases_count
58
+ @test_cases_count ||= @test_cases.count + @sub_suites.sum(&:test_cases_count)
59
+ end
60
+
61
+ def failures_count
62
+ @failures_count ||= @test_cases.count(&:failed?) + @sub_suites.sum(&:failures_count)
63
+ end
64
+
65
+ def skipped_count
66
+ @skipped_count ||= @test_cases.count(&:skipped?) + @sub_suites.sum(&:skipped_count)
67
+ end
68
+
69
+ def total_tests_count
70
+ @test_cases.sum(&:total_tests_count) +
71
+ @sub_suites.sum(&:total_tests_count)
72
+ end
73
+
74
+ def total_failures_count
75
+ @test_cases.sum(&:total_failures_count) +
76
+ @sub_suites.sum(&:total_failures_count)
77
+ end
78
+
79
+ def total_retries_count
80
+ @test_cases.sum(&:retries_count) +
81
+ @sub_suites.sum(&:total_retries_count)
82
+ end
83
+
84
+ # Hash representation used by TestParser to collect test results
85
+ def to_hash
86
+ {
87
+ number_of_tests: total_tests_count,
88
+ number_of_failures: total_failures_count,
89
+ number_of_tests_excluding_retries: test_cases_count,
90
+ number_of_failures_excluding_retries: failures_count,
91
+ number_of_retries: total_retries_count,
92
+ number_of_skipped: skipped_count
93
+ }
94
+ end
95
+
96
+ # Generates a JUnit-compatible XML representation of the test suite
97
+ # See https://github.com/testmoapp/junitxml/
98
+ def to_xml(output_remove_retry_attempts: false)
99
+ testsuite = Helper.create_xml_element('testsuite',
100
+ name: @name,
101
+ time: duration.to_s,
102
+ tests: test_cases_count.to_s,
103
+ failures: failures_count.to_s,
104
+ skipped: skipped_count.to_s)
105
+
106
+ # Add test cases
107
+ @test_cases.each do |test_case|
108
+ runs = test_case.to_xml_nodes
109
+ runs = runs.last(1) if output_remove_retry_attempts
110
+ runs.each { |node| testsuite.add_element(node) }
111
+ end
112
+
113
+ # Add sub-suites
114
+ @sub_suites.each do |sub_suite|
115
+ testsuite.add_element(sub_suite.to_xml(output_remove_retry_attempts: output_remove_retry_attempts))
116
+ end
117
+
118
+ testsuite
119
+ end
120
+
121
+ def process_children(children)
122
+ children.each do |child|
123
+ case child['nodeType']
124
+ when 'Test Case'
125
+ # Use from_json to generate multiple test cases if needed
126
+ @test_cases.concat(TestCase.from_json(node: child))
127
+ when 'Test Suite', 'Unit test bundle', 'UI test bundle'
128
+ @sub_suites << TestSuite.from_json(node: child)
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end