fastlane-plugin-saucectl 0.1.3.pre → 0.1.4.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,312 @@
1
+ require 'fastlane/action'
2
+ require 'json'
3
+ require 'yaml'
4
+ require_relative '../helper/config'
5
+ require_relative '../helper/runner'
6
+
7
+ module Fastlane
8
+ module Actions
9
+ class SauceConfigAction < Action
10
+ @messages = YAML.load_file("#{__dir__}/../strings/messages.yml")
11
+
12
+ def self.run(params)
13
+ if params[:platform].eql?('android')
14
+ UI.user_error!("❌ For android platform you must specify devices or emulators under test in order to execute tests") if params[:emulators].nil? && params[:devices].nil?
15
+ else
16
+ if params[:emulators]
17
+ UI.user_error!("❌ Sauce Labs platform does not currently support virtual device execution for ios apps")
18
+ end
19
+ UI.user_error!("❌ For ios platform you must specify devices under test in order to execute tests") if params[:devices].nil?
20
+ end
21
+
22
+ Fastlane::Saucectl::ConfigGenerator.new(params).create
23
+ end
24
+
25
+ def self.description
26
+ "Create SauceLabs configuration file for test execution based on given parameters"
27
+ end
28
+
29
+ def self.details
30
+ "Create SauceLabs configuration file for test execution based on given parameters"
31
+ end
32
+
33
+ def self.available_options
34
+ [
35
+ FastlaneCore::ConfigItem.new(key: :platform,
36
+ description: "application under test platform (ios or android)",
37
+ optional: false,
38
+ type: String,
39
+ verify_block: proc do |value|
40
+ UI.user_error!(@messages['platform_error']) if value.to_s.empty?
41
+ end),
42
+ FastlaneCore::ConfigItem.new(key: :kind,
43
+ description: "Specifies which framework is associated with the automation tests configured in this specification (xcuitest & espresso)",
44
+ optional: false,
45
+ type: String,
46
+ verify_block: proc do |value|
47
+ UI.user_error!(@messages['platform_error']) if value.to_s.empty?
48
+ end),
49
+ FastlaneCore::ConfigItem.new(key: :app,
50
+ description: "The path to the app",
51
+ optional: false,
52
+ type: String,
53
+ verify_block: proc do |value|
54
+ UI.user_error!(@messages['app_name_error']) unless value && !value.empty?
55
+ end),
56
+ FastlaneCore::ConfigItem.new(key: :test_app,
57
+ description: "The path to the testing app",
58
+ optional: false,
59
+ is_string: true,
60
+ verify_block: proc do |value|
61
+ UI.user_error!(@messages['test_runner_app_error']) unless value && !value.empty?
62
+ end),
63
+ FastlaneCore::ConfigItem.new(key: :region,
64
+ description: "Data Center region (us or eu), set using: region: 'eu'",
65
+ optional: false,
66
+ type: String,
67
+ verify_block: proc do |value|
68
+ UI.user_error!(@messages['region_error'].gsub!('$region', value)) unless @messages['supported_regions'].include?(value)
69
+ end),
70
+ FastlaneCore::ConfigItem.new(key: :retries,
71
+ description: "Sets the number of times to retry a failed suite",
72
+ optional: true,
73
+ type: Integer,
74
+ default_value: 0),
75
+ FastlaneCore::ConfigItem.new(key: :test_distribution,
76
+ description: "Test distribution method",
77
+ optional: true,
78
+ type: String,
79
+ default_value: 'class'),
80
+ FastlaneCore::ConfigItem.new(key: :test_class,
81
+ description: "Array of tests to execute",
82
+ optional: true,
83
+ type: Array),
84
+ FastlaneCore::ConfigItem.new(key: :emulators,
85
+ description: "The parent property that defines details for running this suite on virtual devices using an emulator",
86
+ optional: true,
87
+ type: Array,
88
+ verify_block: proc do |emulators|
89
+ emulators.each do |emulator|
90
+ if emulator.class != Hash
91
+ UI.user_error!("Each emulator must be represented by a Hash object, #{emulator.class} found")
92
+ end
93
+ verify_device_property(emulator, :name)
94
+ set_default_property(emulator, :orientation, 'portrait')
95
+ verify_device_property(emulator, :platform_versions)
96
+ end
97
+ end),
98
+ FastlaneCore::ConfigItem.new(key: :devices,
99
+ description: "The parent property that defines details for running this suite on real devices",
100
+ optional: true,
101
+ type: Array,
102
+ verify_block: proc do |devices|
103
+ devices.each do |device|
104
+ if device.class != Hash
105
+ UI.user_error!("Each device must be represented by a Hash object, #{device.class} found")
106
+ end
107
+ verify_optional_device_props(device)
108
+ set_default_property(device, :orientation, 'portrait')
109
+ set_default_property(device, :device_type, 'phone')
110
+ set_default_property(device, :private, true)
111
+ set_default_property(device, :carrier_connectivity, false)
112
+ end
113
+ end),
114
+ FastlaneCore::ConfigItem.new(key: :name,
115
+ description: "The name of the device or emulator",
116
+ optional: true,
117
+ type: String),
118
+ FastlaneCore::ConfigItem.new(key: :id,
119
+ description: "The id of the device",
120
+ optional: true,
121
+ type: String),
122
+ FastlaneCore::ConfigItem.new(key: :platform_versions,
123
+ description: "Platform versions of the virtual device you wish to test your application on: Virtual Device only",
124
+ optional: true,
125
+ type: Array),
126
+ FastlaneCore::ConfigItem.new(key: :platform_version,
127
+ description: "Platform version of the real device you wish to test your application on: Real device only",
128
+ optional: true,
129
+ type: String),
130
+ FastlaneCore::ConfigItem.new(key: :orientation,
131
+ description: "The orientation of the device. Default: portrait",
132
+ optional: true,
133
+ type: String,
134
+ default_value: 'portrait'),
135
+ FastlaneCore::ConfigItem.new(key: :device_type,
136
+ description: "Request that the matching device is a specific type of device. Valid values are: ANY TABLET PHONE any tablet phone",
137
+ optional: true,
138
+ type: String,
139
+ default_value: 'phone'),
140
+ FastlaneCore::ConfigItem.new(key: :private,
141
+ description: "Request that the matching device is from your organization's private pool",
142
+ optional: true,
143
+ is_string: false),
144
+ FastlaneCore::ConfigItem.new(key: :carrier_connectivity,
145
+ description: "Request that the matching device is also connected to a cellular network",
146
+ optional: true,
147
+ is_string: false),
148
+ FastlaneCore::ConfigItem.new(key: :test_target,
149
+ description: "Name of the Xcode test target name",
150
+ optional: true,
151
+ type: String),
152
+ FastlaneCore::ConfigItem.new(key: :test_plan,
153
+ description: "Name of the Xcode test plan",
154
+ optional: true,
155
+ type: String),
156
+ FastlaneCore::ConfigItem.new(key: :path_to_tests,
157
+ description: "Path to your espresso tests",
158
+ optional: true,
159
+ type: String,
160
+ default_value: "#{Dir.pwd}/app/src/androidTest"),
161
+ FastlaneCore::ConfigItem.new(key: :clear_data,
162
+ description: "Clear package data from device (android only)",
163
+ optional: true,
164
+ is_string: false,
165
+ default_value: true),
166
+ FastlaneCore::ConfigItem.new(key: :use_test_orchestrator,
167
+ description: "User Android test orchestrator (android only)",
168
+ optional: true,
169
+ is_string: false,
170
+ default_value: true),
171
+ FastlaneCore::ConfigItem.new(key: :max_concurrency_size,
172
+ description: "Sets the maximum number of suites to execute at the same time. If the test defines more suites than the max, excess suites are queued and run in order as each suite completes",
173
+ optional: true,
174
+ type: Integer,
175
+ default_value: 1)
176
+ ]
177
+ end
178
+
179
+ def self.verify_device_property(device, property)
180
+ UI.user_error!("Each device must have #{property} property") unless device.key?(property)
181
+ end
182
+
183
+ def self.verify_optional_device_props(device)
184
+ unless device.key?(:name) || device.key?(:id)
185
+ UI.user_error!("Real devices must have a device name or device id")
186
+ end
187
+ end
188
+
189
+ private_class_method :verify_device_property
190
+
191
+ def self.set_default_property(device, property, default)
192
+ unless device.key?(property)
193
+ device[property] = default
194
+ end
195
+ end
196
+
197
+ private_class_method :set_default_property
198
+
199
+ def self.authors
200
+ ["Ian Hamilton"]
201
+ end
202
+
203
+ def self.category
204
+ :testing
205
+ end
206
+
207
+ def self.is_supported?(platform)
208
+ [:ios, :android].include?(platform)
209
+ end
210
+
211
+ def self.example_code
212
+ [
213
+ "sauce_config({platform: 'android',
214
+ kind: 'espresso',
215
+ app: 'path/to/myTestApp.apk',
216
+ test_app: 'path/to/myTestRunner.apk',
217
+ path_to_tests: 'path/to/app/src/androidTest',
218
+ region: 'eu',
219
+ emulators: [ {name: 'Android GoogleApi Emulator', platform_versions: %w[10.0 11.0], orientation: 'portrait'}]
220
+ })",
221
+ "sauce_config({platform: 'android',
222
+ kind: 'espresso',
223
+ app: 'path/to/myTestApp.apk',
224
+ test_app: 'path/to/myTestRunner.apk',
225
+ path_to_tests: 'path/to/app/src/androidTest',
226
+ region: 'eu',
227
+ test_distribution: 'testCase',
228
+ devices: [ {name: 'RDC One', orientation: 'portrait', platform_version: '11.0'}]
229
+ })",
230
+ "sauce_config({platform: 'android',
231
+ kind: 'espresso',
232
+ app: 'path/to/myTestApp.apk',
233
+ test_app: 'path/to/myTestRunner.apk',
234
+ path_to_tests: 'path/to/app/src/androidTest',
235
+ region: 'eu',
236
+ test_distribution: 'shard',
237
+ devices: [ {name: 'RDC One', orientation: 'portrait', platform_version: '11.0'}]
238
+ })",
239
+ "sauce_config({platform: 'ios',
240
+ kind: 'xcuitest',
241
+ app: 'path/to/MyTestApp.ipa',
242
+ test_app: 'path/to/MyTestAppRunner.ipa',
243
+ region: 'eu',
244
+ devices: [ {name: 'iPhone RDC One'}, {id: 'iphone_rdc_two'} ],
245
+ test_target: 'MyDemoAppUITests'
246
+ })",
247
+ "sauce_config({platform: 'ios',
248
+ kind: 'xcuitest',
249
+ app: 'path/to/MyTestApp.ipa',
250
+ test_app: 'path/to/MyTestAppRunner.ipa',
251
+ region: 'eu',
252
+ devices: [ {name: 'iPhone RDC One'}, {id: 'iphone_rdc_two'} ],
253
+ test_target: 'MyDemoAppUITests',
254
+ test_distribution: 'shard',
255
+ })",
256
+ " sauce_config({platform: 'ios',
257
+ kind: 'xcuitest',
258
+ app: 'path/to/MyTestApp.ipa',
259
+ test_app: 'path/to/MyTestAppRunner.ipa',
260
+ region: 'eu',
261
+ devices: [ {name: 'iPhone RDC One'}, {id: 'iphone_rdc_two'} ],
262
+ test_target: 'MyDemoAppUITests',
263
+ test_distribution: 'testCase'
264
+ })",
265
+ "sauce_config({platform: 'ios',
266
+ kind: 'xcuitest',
267
+ app: 'path/to/MyTestApp.ipa',
268
+ test_app: 'path/to/MyTestAppRunner.ipa',
269
+ region: 'eu',
270
+ devices: [ {name: 'iPhone RDC One'}, {id: 'iphone_rdc_two'} ],
271
+ test_plan: 'EnabledUITests',
272
+ test_distribution: 'shard'
273
+ })",
274
+ "sauce_config({platform: 'ios',
275
+ kind: 'xcuitest',
276
+ app: 'path/to/MyTestApp.ipa',
277
+ test_app: 'path/to/MyTestAppRunner.ipa',
278
+ region: 'eu',
279
+ devices: [ {name: 'iPhone RDC One'}, {id: 'iphone_rdc_two'} ],
280
+ test_plan: 'UITests'
281
+ })",
282
+ "sauce_config({platform: 'android',
283
+ kind: 'espresso',
284
+ app: 'path/to/myTestApp.apk',
285
+ test_app: 'path/to/myTestRunner.apk',
286
+ path_to_tests: 'path/to/app/src/androidTest',
287
+ region: 'eu',
288
+ devices: [ {name: 'iPhone RDC One'}],
289
+ test_class: ['com.some.package.testing.SomeClassOne', 'com.some.package.testing.SomeClassTwo', 'com.some.package.testing.SomeClassThree', 'com.some.package.testing.SomeClassFour']
290
+ })",
291
+ "sauce_config({platform: 'android',
292
+ kind: 'espresso',
293
+ app: 'path/to/myTestApp.apk',
294
+ test_app: 'path/to/myTestRunner.apk',
295
+ path_to_tests: 'path/to/app/src/androidTest',
296
+ region: 'eu',
297
+ emulators: [ {name: 'iPhone RDC One', platform_versions: ['11.0']}, {name: 'iPhone RDC Two', platform_versions: ['11.0']}],
298
+ test_class: ['com.some.package.testing.SomeClassOne', 'com.some.package.testing.SomeClassTwo', 'com.some.package.testing.SomeClassThree', 'com.some.package.testing.SomeClassFour']
299
+ })",
300
+ "sauce_config({platform: 'ios',
301
+ kind: 'xcuitest',
302
+ app: 'path/to/myTestApp.app',
303
+ test_app: 'path/to/myTestRunner.app',
304
+ region: 'eu',
305
+ devices: [ {name: 'iPhone RDC One'}],
306
+ test_class: ['MyDemoAppUITests.SomeClassOne', 'MyDemoAppUITests.SomeClassTwo', 'MyDemoAppUITests.SomeClassThree', 'MyDemoAppUITests.SomeClassFour']
307
+ })"
308
+ ]
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,95 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/api'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class SauceDevicesAction < Action
7
+ @messages = YAML.load_file("#{__dir__}/../strings/messages.yml")
8
+
9
+ def self.run(params)
10
+ platform = params[:platform]
11
+ case platform
12
+ when 'android'
13
+ Fastlane::Saucectl::Api.new(params).fetch_android_devices
14
+ when 'ios'
15
+ Fastlane::Saucectl::Api.new(params).fetch_ios_devices
16
+ else
17
+ Fastlane::Saucectl::Api.new(params).available_devices
18
+ end
19
+ end
20
+
21
+ def self.description
22
+ "Returns a list of Device IDs for all devices in the data center that are currently free for testing."
23
+ end
24
+
25
+ def self.details
26
+ "Returns a list of Device IDs for all devices in the data center that are currently free for testing."
27
+ end
28
+
29
+ def self.available_options
30
+ [
31
+ FastlaneCore::ConfigItem.new(key: :platform,
32
+ description: "Device platform that you wish to query",
33
+ optional: true,
34
+ is_string: true,
35
+ default_value: ''),
36
+ FastlaneCore::ConfigItem.new(key: :region,
37
+ description: "Data Center region (us or eu), set using: region: 'eu'",
38
+ optional: false,
39
+ type: String,
40
+ verify_block: proc do |value|
41
+ UI.user_error!(@messages['region_error'].gsub!('$region', value)) unless @messages['supported_regions'].include?(value)
42
+ end),
43
+ FastlaneCore::ConfigItem.new(key: :sauce_username,
44
+ env_name: "SAUCE_USERNAME",
45
+ description: "Your sauce labs username in order to authenticate upload requests",
46
+ default_value: Actions.lane_context[SharedValues::SAUCE_USERNAME],
47
+ optional: false,
48
+ type: String,
49
+ verify_block: proc do |value|
50
+ UI.user_error!(@messages['sauce_username_error']) unless value && !value.empty?
51
+ end),
52
+ FastlaneCore::ConfigItem.new(key: :sauce_access_key,
53
+ env_name: "SAUCE_ACCESS_KEY",
54
+ description: "Your sauce labs access key in order to authenticate upload requests",
55
+ default_value: Actions.lane_context[SharedValues::SAUCE_ACCESS_KEY],
56
+ optional: false,
57
+ type: String,
58
+ verify_block: proc do |value|
59
+ UI.user_error!(@messages['sauce_api_key_error']) unless value && !value.empty?
60
+ end)
61
+ ]
62
+ end
63
+
64
+ def self.authors
65
+ ["Ian Hamilton"]
66
+ end
67
+
68
+ def self.category
69
+ :testing
70
+ end
71
+
72
+ def self.is_supported?(platform)
73
+ [:ios, :android].include?(platform)
74
+ end
75
+
76
+ def self.example_code
77
+ [
78
+ "sauce_devices({platform: 'android',
79
+ region: 'eu',
80
+ sauce_username: 'foo',
81
+ sauce_access_key: 'bar123',
82
+ })",
83
+ "sauce_devices({region: 'eu',
84
+ sauce_username: 'foo',
85
+ sauce_access_key: 'bar123',
86
+ })",
87
+ "sauce_devices({region: 'us',
88
+ sauce_username: 'foo',
89
+ sauce_access_key: 'bar123',
90
+ })"
91
+ ]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../helper/runner'
2
+
3
+ module Fastlane
4
+ module Actions
5
+ class SauceRunnerAction < Action
6
+ @messages = YAML.load_file("#{__dir__}/../strings/messages.yml")
7
+
8
+ def self.run(run = '')
9
+ Saucectl::Runner.new.execute
10
+ end
11
+
12
+ def self.description
13
+ "Execute automated tests on sauce labs platform via saucectl binary for specified configuration"
14
+ end
15
+
16
+ def self.details
17
+ "Execute automated tests on sauce labs platform via saucectl binary for specified configuration"
18
+ end
19
+
20
+ def self.available_options
21
+ [
22
+ FastlaneCore::ConfigItem.new(key: :sauce_username,
23
+ env_name: "SAUCE_USERNAME",
24
+ default_value: Actions.lane_context[SharedValues::SAUCE_USERNAME],
25
+ description: "Your sauce labs username",
26
+ optional: false,
27
+ is_string: true,
28
+ verify_block: proc do |value|
29
+ UI.user_error!(@messages['sauce_username_error']) if value.empty?
30
+ end),
31
+ FastlaneCore::ConfigItem.new(key: :sauce_access_key,
32
+ env_name: "SAUCE_ACCESS_KEY",
33
+ default_value: Actions.lane_context[SharedValues::SAUCE_ACCESS_KEY],
34
+ description: "Your sauce labs access key",
35
+ optional: false,
36
+ is_string: true,
37
+ verify_block: proc do |value|
38
+ UI.user_error!(@messages['sauce_api_key_error']) if value.empty?
39
+ end)
40
+ ]
41
+ end
42
+
43
+ def self.authors
44
+ ["Ian Hamilton"]
45
+ end
46
+
47
+ def self.category
48
+ :testing
49
+ end
50
+
51
+ def self.is_supported?(platform)
52
+ [:ios, :android].include?(platform)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,133 @@
1
+ require 'fastlane/action'
2
+ require 'json'
3
+ require 'yaml'
4
+ require_relative '../helper/api'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ module SharedValues
9
+ SAUCE_USERNAME = :SAUCE_USERNAME
10
+ SAUCE_ACCESS_KEY = :SAUCE_ACCESS_KEY
11
+ end
12
+
13
+ class SauceUploadAction < Action
14
+ @messages = YAML.load_file("#{__dir__}/../strings/messages.yml")
15
+
16
+ def self.run(params)
17
+ response = Fastlane::Saucectl::Api.new(params).upload
18
+ body = JSON.parse(response.body)
19
+ body['item']['id']
20
+ end
21
+
22
+ def self.description
23
+ "Upload test artifacts to sauce labs storage"
24
+ end
25
+
26
+ def self.details
27
+ "Upload test artifacts to sauce labs storage"
28
+ end
29
+
30
+ def self.available_options
31
+ [
32
+ FastlaneCore::ConfigItem.new(key: :platform,
33
+ description: "application under test platform (ios or android)",
34
+ optional: false,
35
+ is_string: true,
36
+ verify_block: proc do |value|
37
+ UI.user_error!(@messages['platform_error']) if value.to_s.empty?
38
+ end),
39
+ FastlaneCore::ConfigItem.new(key: :file,
40
+ description: "File to upload to sauce storage",
41
+ optional: false,
42
+ is_string: true,
43
+ verify_block: proc do |value|
44
+ UI.user_error!(@messages['file_error']) unless value && !value.empty?
45
+ if value
46
+ UI.user_error!("Could not find file to upload \"#{value}\" ") unless File.exist?(value)
47
+ extname = File.extname(value)
48
+ UI.user_error!("Extension not supported for \"#{value}\" ") unless @messages['accepted_file_types'].include?(extname)
49
+ end
50
+ end),
51
+ FastlaneCore::ConfigItem.new(key: :app,
52
+ description: "Name of the application to be uploaded",
53
+ optional: false,
54
+ is_string: true,
55
+ verify_block: proc do |value|
56
+ UI.user_error!(@messages['app_name_error']) unless value && !value.empty?
57
+ end),
58
+ FastlaneCore::ConfigItem.new(key: :region,
59
+ description: "Data Center region (us or eu), set using: region: 'eu'",
60
+ optional: false,
61
+ is_string: true,
62
+ verify_block: proc do |value|
63
+ UI.user_error!(@messages['region_error'].gsub!('$region', value)) unless @messages['supported_regions'].include?(value)
64
+ end),
65
+ FastlaneCore::ConfigItem.new(key: :sauce_username,
66
+ env_name: "SAUCE_USERNAME",
67
+ description: "Your sauce labs username in order to authenticate upload requests",
68
+ default_value: Actions.lane_context[SharedValues::SAUCE_USERNAME],
69
+ optional: false,
70
+ type: String,
71
+ verify_block: proc do |value|
72
+ UI.user_error!(@messages['sauce_username_error']) unless value && !value.empty?
73
+ end),
74
+ FastlaneCore::ConfigItem.new(key: :sauce_access_key,
75
+ env_name: "SAUCE_ACCESS_KEY",
76
+ description: "Your sauce labs access key in order to authenticate upload requests",
77
+ default_value: Actions.lane_context[SharedValues::SAUCE_ACCESS_KEY],
78
+ optional: false,
79
+ type: String,
80
+ verify_block: proc do |value|
81
+ UI.user_error!(@messages['sauce_api_key_error']) unless value && !value.empty?
82
+ end)
83
+ ]
84
+ end
85
+
86
+ def self.authors
87
+ ["Ian Hamilton"]
88
+ end
89
+
90
+ def self.category
91
+ :testing
92
+ end
93
+
94
+ def self.is_supported?(platform)
95
+ [:ios, :android].include?(platform)
96
+ end
97
+
98
+ def self.example_code
99
+ [
100
+ "sauce_upload({
101
+ platform: 'android',
102
+ sauce_username: 'username',
103
+ sauce_access_key: 'accessKey',
104
+ app: 'Android.MyCustomApp.apk',
105
+ file: 'app/build/outputs/apk/debug/app-debug.apk',
106
+ region: 'eu'
107
+ })",
108
+ "sauce_upload({
109
+ platform: 'android',
110
+ sauce_username: 'username',
111
+ sauce_access_key: 'accessKey',
112
+ app: 'Android.MyCustomApp.apk',
113
+ file: 'app/build/outputs/apk/debug/app-debug.apk',
114
+ region: 'eu',
115
+ app_description: 'this is a test description'
116
+ })",
117
+ "sauce_upload({
118
+ platform: 'ios',
119
+ sauce_username: 'username',
120
+ sauce_access_key: 'accessKey',
121
+ app: 'MyTestApp.ipa',
122
+ file: 'path/to/my/app/MyTestApp.ipa',
123
+ region: 'eu'
124
+ })"
125
+ ]
126
+ end
127
+
128
+ def self.return_value
129
+ "Returns the application id of the app uploaded"
130
+ end
131
+ end
132
+ end
133
+ end