fastlane-plugin-saucectl 0.1.0.pre
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 +7 -0
- data/lib/fastlane/plugin/saucectl/actions/delete_from_storage_action.rb +96 -0
- data/lib/fastlane/plugin/saucectl/actions/disabled_tests_action.rb +86 -0
- data/lib/fastlane/plugin/saucectl/actions/install_toolkit_action.rb +30 -0
- data/lib/fastlane/plugin/saucectl/actions/sauce_apps_action.rb +96 -0
- data/lib/fastlane/plugin/saucectl/actions/sauce_config_action.rb +312 -0
- data/lib/fastlane/plugin/saucectl/actions/sauce_devices_action.rb +95 -0
- data/lib/fastlane/plugin/saucectl/actions/sauce_runner_action.rb +52 -0
- data/lib/fastlane/plugin/saucectl/actions/sauce_upload_action.rb +133 -0
- data/lib/fastlane/plugin/saucectl/helper/api.rb +115 -0
- data/lib/fastlane/plugin/saucectl/helper/config.rb +97 -0
- data/lib/fastlane/plugin/saucectl/helper/espresso.rb +93 -0
- data/lib/fastlane/plugin/saucectl/helper/file_utils.rb +27 -0
- data/lib/fastlane/plugin/saucectl/helper/installer.rb +51 -0
- data/lib/fastlane/plugin/saucectl/helper/runner.rb +36 -0
- data/lib/fastlane/plugin/saucectl/helper/storage.rb +46 -0
- data/lib/fastlane/plugin/saucectl/helper/suites.rb +201 -0
- data/lib/fastlane/plugin/saucectl/helper/xctest.rb +168 -0
- data/lib/fastlane/plugin/saucectl/version.rb +5 -0
- data/lib/fastlane/plugin/saucectl.rb +17 -0
- metadata +216 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'fastlane'
|
2
|
+
require 'open3'
|
3
|
+
require 'fastlane_core/ui/ui'
|
4
|
+
require_relative "file_utils"
|
5
|
+
|
6
|
+
module Fastlane
|
7
|
+
module Saucectl
|
8
|
+
#
|
9
|
+
# This class provides the ability to execute tests via configured specifications and capture the output of the sauce executable.
|
10
|
+
#
|
11
|
+
class Runner
|
12
|
+
include FileUtils
|
13
|
+
EXECUTABLE = 'saucectl'
|
14
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
15
|
+
|
16
|
+
def execute
|
17
|
+
unless File.exist?(EXECUTABLE)
|
18
|
+
UI.user_error!("❌ sauce labs executable file does not exist! Expected sauce executable file to be located at:'#{Dir.pwd}/#{EXECUTABLE}'")
|
19
|
+
end
|
20
|
+
|
21
|
+
system("chmod +x #{EXECUTABLE}")
|
22
|
+
system("./#{EXECUTABLE} run")
|
23
|
+
end
|
24
|
+
|
25
|
+
def system(*cmd)
|
26
|
+
Open3.popen2e(*cmd) do |stdin, stdout_stderr, wait_thread|
|
27
|
+
Thread.new do
|
28
|
+
stdout_stderr.each { |out| UI.message(out) }
|
29
|
+
end
|
30
|
+
stdin.close
|
31
|
+
wait_thread.value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
require_relative 'api'
|
5
|
+
|
6
|
+
module Fastlane
|
7
|
+
module Saucectl
|
8
|
+
# This class provides the ability to store, delete, and retrieve data from the Sauce Labs Storage API
|
9
|
+
class Storage
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get App Storage Files
|
15
|
+
# @return the set of files that have been uploaded to Sauce Storage by the requester.
|
16
|
+
def retrieve_all_apps
|
17
|
+
api = Fastlane::Saucectl::Api.new(@config)
|
18
|
+
api.retrieve_all_apps
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delete app by the Sauce Labs identifier of the stored file. You can look up file IDs using the Get App Storage Files endpoint.
|
22
|
+
# https://docs.saucelabs.com/dev/api/storage/#get-app-storage-files
|
23
|
+
# @return json response containing the file id and the number of files deleted.
|
24
|
+
def delete_app_with_file_id
|
25
|
+
api = Fastlane::Saucectl::Api.new(@config)
|
26
|
+
api.delete_app("v1/storage/files/#{@config[:app_id]}")
|
27
|
+
end
|
28
|
+
|
29
|
+
# Deletes the specified group of files from Sauce Storage.
|
30
|
+
# The Sauce Labs identifier of the group of files. You can look up file IDs using the Get App Storage Groups endpoint.
|
31
|
+
# https://docs.saucelabs.com/dev/api/storage/#get-app-storage-groups
|
32
|
+
# @return json response containing the group ID and the number of files deleted.
|
33
|
+
def delete_all_apps_for_group_id
|
34
|
+
path = "v1/storage/files/#{@config[:group_id]}"
|
35
|
+
api = Fastlane::Saucectl::Api.new(@config)
|
36
|
+
api.delete_app(path)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Uploads an application file to Sauce Storage for the purpose of mobile application testing
|
40
|
+
# @return a unique file ID assigned to the app.
|
41
|
+
def upload_app
|
42
|
+
Fastlane::Saucectl::Api.new(@config).upload
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'fastlane'
|
3
|
+
require 'fastlane_core/ui/ui'
|
4
|
+
require_relative 'espresso'
|
5
|
+
require_relative 'xctest'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Saucectl
|
9
|
+
#
|
10
|
+
# This class will create test suites based on user specified configuration properties
|
11
|
+
#
|
12
|
+
class Suites
|
13
|
+
include FileUtils
|
14
|
+
|
15
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_test_plan
|
22
|
+
check_kind
|
23
|
+
if @config[:platform].casecmp('ios').zero?
|
24
|
+
is_ios_reqs_satisfied?
|
25
|
+
Fastlane::Saucectl::XCTest.new(@config)
|
26
|
+
else
|
27
|
+
Fastlane::Saucectl::Espresso.new(@config)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_kind
|
32
|
+
if @config[:platform].eql?('android')
|
33
|
+
UI.user_error!("❌ #{@config[:kind]} is not a supported test framework for android. Use espresso") unless @config[:kind].eql?('espresso')
|
34
|
+
else
|
35
|
+
UI.user_error!("❌ #{@config[:kind]} is not a supported test framework for iOS. Use xcuitest") unless @config[:kind].eql?('xcuitest')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_ios_reqs_satisfied?
|
40
|
+
if @config[:test_target].nil? && @config[:test_plan].nil?
|
41
|
+
UI.user_error!("❌ For ios you must specify test_target or test_plan")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_distribution_array
|
46
|
+
@config[:test_class] || create_test_plan.test_distribution
|
47
|
+
end
|
48
|
+
|
49
|
+
def suite_name(test_type)
|
50
|
+
if ENV['JOB_NAME'].nil? && ENV['BUILD_NUMBER'].nil?
|
51
|
+
"#{@config[:kind]}-#{test_type.split('.')[-1]}"
|
52
|
+
else
|
53
|
+
"#{ENV['JOB_NAME']}-#{ENV['BUILD_NUMBER']}-#{test_type.split('.')[-1]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_virtual_device_suites
|
58
|
+
if @config[:test_distribution] == 'shard'
|
59
|
+
shard_virtual_device_suites
|
60
|
+
elsif @config[:test_class]
|
61
|
+
custom_test_classes
|
62
|
+
else
|
63
|
+
test_suites = []
|
64
|
+
@config[:emulators].each do |emulator|
|
65
|
+
test_distribution_array.each do |test_type|
|
66
|
+
test_suites << {
|
67
|
+
'name' => suite_name(test_type).downcase,
|
68
|
+
'testOptions' => default_test_options(test_type)
|
69
|
+
}.merge(virtual_device_options(emulator))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
test_suites
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def shard_virtual_device_suites
|
77
|
+
UI.user_error!("❌ Cannot split #{@config[:test_distribution]}'s across virtual devices with a single emulator. \nPlease specify a minimum of two devices!") if @config[:emulators].size.eql?(1)
|
78
|
+
test_suites = []
|
79
|
+
arr = test_distribution_array
|
80
|
+
shards = arr.each_slice((arr.size / @config[:emulators].size.to_f).round).to_a
|
81
|
+
shards.each_with_index do |suite, i|
|
82
|
+
test_suites << {
|
83
|
+
'name' => suite_name("shard #{i + 1}").downcase,
|
84
|
+
'testOptions' => default_test_options(suite)
|
85
|
+
}.merge(virtual_device_options(@config[:emulators][i]))
|
86
|
+
end
|
87
|
+
test_suites
|
88
|
+
end
|
89
|
+
|
90
|
+
def shard_real_device_suites
|
91
|
+
test_suites = []
|
92
|
+
arr = test_distribution_array
|
93
|
+
shards = arr.each_slice((arr.size / @config[:devices].size.to_f).round).to_a
|
94
|
+
shards.each_with_index do |suite, i|
|
95
|
+
test_suites << {
|
96
|
+
'name' => suite_name("shard #{i + 1}").downcase,
|
97
|
+
'testOptions' => default_test_options(suite)
|
98
|
+
}.merge(real_device_options(@config[:devices][i]))
|
99
|
+
end
|
100
|
+
test_suites
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_plan_suites
|
104
|
+
test_suites = []
|
105
|
+
@config[:devices].each do |device|
|
106
|
+
test_suites << {
|
107
|
+
'name' => suite_name(@config[:test_plan].to_s).downcase,
|
108
|
+
'testOptions' => default_test_options(test_distribution_array)
|
109
|
+
}.merge(real_device_options(device))
|
110
|
+
end
|
111
|
+
test_suites
|
112
|
+
end
|
113
|
+
|
114
|
+
def custom_test_classes
|
115
|
+
test_suites = []
|
116
|
+
devices = @config[:devices].nil? ? @config[:emulators] : @config[:devices]
|
117
|
+
devices.each do |device|
|
118
|
+
device_options = @config[:devices].nil? ? virtual_device_options(device) : real_device_options(device)
|
119
|
+
test_classes = @config[:test_class].reject(&:empty?).join(',')
|
120
|
+
test_suites << {
|
121
|
+
'name' => suite_name(device[:name]).downcase,
|
122
|
+
'testOptions' => default_test_options(test_classes.split(','))
|
123
|
+
}.merge(device_options)
|
124
|
+
end
|
125
|
+
test_suites
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_real_device_suites
|
129
|
+
if !@config[:test_plan].nil? && @config[:test_distribution].eql?('class')
|
130
|
+
test_plan_suites
|
131
|
+
elsif @config[:test_distribution] == 'shard'
|
132
|
+
shard_real_device_suites
|
133
|
+
elsif @config[:test_class].kind_of?(Array)
|
134
|
+
custom_test_classes
|
135
|
+
else
|
136
|
+
test_suites = []
|
137
|
+
@config[:devices].each do |device|
|
138
|
+
test_distribution_array.each do |test_type|
|
139
|
+
test_suites << {
|
140
|
+
'name' => suite_name(test_type).downcase,
|
141
|
+
'testOptions' => default_test_options(test_type)
|
142
|
+
}.merge(real_device_options(device))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
test_suites
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def virtual_device_options(device)
|
150
|
+
platform_versions = device[:platform_versions].reject(&:empty?).join(',')
|
151
|
+
{ 'emulators' => [{ 'name' => device[:name],
|
152
|
+
'orientation' => device[:orientation],
|
153
|
+
'platformVersions' => platform_versions.split(',') }] }
|
154
|
+
end
|
155
|
+
|
156
|
+
def real_device_options(device)
|
157
|
+
{ 'devices' => [rdc_options(device)] }
|
158
|
+
end
|
159
|
+
|
160
|
+
def rdc_options(device)
|
161
|
+
device_type_key = device.key?(:id) ? 'id' : 'name'
|
162
|
+
name = device.key?(:id) ? device[:id] : device[:name]
|
163
|
+
|
164
|
+
base_device_hash = {
|
165
|
+
device_type_key => name,
|
166
|
+
'orientation' => device[:orientation]
|
167
|
+
}.merge('options' => device_options(device))
|
168
|
+
|
169
|
+
unless device[:platform_version].nil?
|
170
|
+
base_device_hash = base_device_hash.merge({ 'platformVersion' => device[:platform_version] })
|
171
|
+
end
|
172
|
+
|
173
|
+
base_device_hash
|
174
|
+
end
|
175
|
+
|
176
|
+
def device_options(device)
|
177
|
+
{
|
178
|
+
'carrierConnectivity' => device[:carrier_connectivity],
|
179
|
+
'deviceType' => device[:device_type].upcase!,
|
180
|
+
'private' => device[:private]
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
def default_test_options(test_type)
|
185
|
+
test_option_type = @config[:test_distribution].eql?('package') ? 'package' : 'class'
|
186
|
+
if @config[:platform] == 'android'
|
187
|
+
{ test_option_type => test_type }.merge(android_test_options)
|
188
|
+
else
|
189
|
+
{ 'class' => test_type }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def android_test_options
|
194
|
+
{
|
195
|
+
'clearPackageData' => @config[:clear_data],
|
196
|
+
'useTestOrchestrator' => @config[:use_test_orchestrator]
|
197
|
+
}
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "find"
|
2
|
+
require "open3"
|
3
|
+
require "json"
|
4
|
+
require_relative "file_utils"
|
5
|
+
|
6
|
+
module Fastlane
|
7
|
+
module Saucectl
|
8
|
+
# This class is responsible for creating test execution plans for ios applications and will distribute tests
|
9
|
+
# that will be be executed via the cloud provider.
|
10
|
+
#
|
11
|
+
class XCTest
|
12
|
+
include FileUtils
|
13
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
14
|
+
TEST_FUNCTION_REGEX = /(test+[A-Z][a-zA-Z]+)[(][)]/.freeze
|
15
|
+
|
16
|
+
def initialize(config)
|
17
|
+
@config = config
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_plan_exists?
|
21
|
+
File.exist?(Dir["**/#{@config[:test_plan]}.xctestplan"][0])
|
22
|
+
rescue StandardError
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_test_plan?
|
27
|
+
File.exist?(Dir["**/#{@config[:test_plan]}.xctestplan"][0])
|
28
|
+
rescue StandardError
|
29
|
+
UI.user_error!("#{@config[:test_plan]} was not found in workspace")
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_test_plan
|
33
|
+
plan_path = Dir["**/#{@config[:test_plan]}.xctestplan"][0]
|
34
|
+
selected = File.read(plan_path)
|
35
|
+
JSON.parse(selected)["testTargets"][0]
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_target_from_test_plan
|
39
|
+
fetch_test_plan["target"]["name"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_data
|
43
|
+
if test_plan_exists?
|
44
|
+
valid_test_plan?
|
45
|
+
if fetch_test_plan.include?("skippedTests")
|
46
|
+
strip_skipped(all_tests)
|
47
|
+
else
|
48
|
+
fetch_selected_tests
|
49
|
+
end
|
50
|
+
else
|
51
|
+
all_tests
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_target
|
56
|
+
@config[:test_target].nil? ? fetch_target_from_test_plan : @config[:test_target]
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_distribution
|
60
|
+
test_distribution_check
|
61
|
+
tests_arr = []
|
62
|
+
|
63
|
+
test_distribution = @config[:test_plan].nil? ? @config[:test_distribution] : 'testPlan'
|
64
|
+
case test_distribution
|
65
|
+
when 'testCase', 'testPlan'
|
66
|
+
test_data.each do |type|
|
67
|
+
type[:tests].each { |test| tests_arr << "#{test_target}.#{type[:class]}/#{test}" }
|
68
|
+
end
|
69
|
+
else
|
70
|
+
test_data.each do |type|
|
71
|
+
tests_arr << "#{test_target}.#{type[:class]}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
tests_arr.uniq
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_distribution_check
|
78
|
+
return @config[:test_distribution] if @config[:test_distribution].kind_of?(Array)
|
79
|
+
|
80
|
+
distribution_types = %w[class testCase shard]
|
81
|
+
unless distribution_types.include?(@config[:test_distribution]) || @config[:test_distribution].nil?
|
82
|
+
UI.user_error!("#{@config[:test_distribution]} is not a valid method of test distribution. \n Supported types for iOS: \n #{distribution_types}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def fetch_selected_tests
|
87
|
+
ui_tests = []
|
88
|
+
fetch_test_plan["selectedTests"].each do |test|
|
89
|
+
test_case = test.gsub('/', ' ').split
|
90
|
+
ui_tests << { class: test_case[0], tests: [test_case[1].gsub(/[()]/, "").to_s] }
|
91
|
+
end
|
92
|
+
ui_tests
|
93
|
+
end
|
94
|
+
|
95
|
+
def strip_skipped(all_tests)
|
96
|
+
enabled_ui_tests = []
|
97
|
+
skipped_tests = fetch_disabled_tests
|
98
|
+
all_tests.each do |tests|
|
99
|
+
tests[:tests].each do |test|
|
100
|
+
unless skipped_tests.to_s.include?(test)
|
101
|
+
enabled_ui_tests << { class: tests[:class].to_s, tests: [test] }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
enabled_ui_tests
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_disabled_tests
|
109
|
+
skipped = fetch_test_plan["skippedTests"]
|
110
|
+
ui_tests = []
|
111
|
+
skipped.each do |item|
|
112
|
+
if item.include?('/')
|
113
|
+
test_case = item.gsub('/', ' ').split
|
114
|
+
ui_tests << sort_ui_tests(test_case[0], test_case[1])
|
115
|
+
else
|
116
|
+
ui_tests << scan_test_class(item)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
ui_tests
|
120
|
+
end
|
121
|
+
|
122
|
+
def sort_ui_tests(cls, test_case)
|
123
|
+
{ class: cls, tests: test_case.gsub(/[()]/, "").to_s }
|
124
|
+
end
|
125
|
+
|
126
|
+
def all_tests
|
127
|
+
test_details = []
|
128
|
+
test_dir = Dir["**/#{test_target}"][0]
|
129
|
+
search_retrieve_test_classes(test_dir).each do |f|
|
130
|
+
next unless File.basename(f) =~ CLASS_NAME_REGEX
|
131
|
+
|
132
|
+
test_details << { class: File.basename(f).gsub(FILE_TYPE_REGEX, ""), tests: tests_from(f) }
|
133
|
+
end
|
134
|
+
|
135
|
+
strip_empty(test_details)
|
136
|
+
end
|
137
|
+
|
138
|
+
def scan_test_class(cls)
|
139
|
+
test_details = []
|
140
|
+
test_dir = Dir["**/#{test_target}"][0]
|
141
|
+
search_retrieve_test_classes(test_dir).each do |f|
|
142
|
+
next unless File.basename(f) =~ /#{cls}/
|
143
|
+
|
144
|
+
test_details << { class: File.basename(f).gsub(FILE_TYPE_REGEX, ""), tests: tests_from(f) }
|
145
|
+
end
|
146
|
+
|
147
|
+
strip_empty(test_details)
|
148
|
+
end
|
149
|
+
|
150
|
+
def tests_from(path)
|
151
|
+
stdout, = find(path, "func")
|
152
|
+
test_cases = []
|
153
|
+
stdout.split.each do |line|
|
154
|
+
test_cases << line.match(TEST_FUNCTION_REGEX).to_s.gsub(/[()]/, "") if line =~ TEST_FUNCTION_REGEX
|
155
|
+
end
|
156
|
+
test_cases
|
157
|
+
end
|
158
|
+
|
159
|
+
def strip_empty(test_details)
|
160
|
+
tests = []
|
161
|
+
test_details.each do |test|
|
162
|
+
tests << test unless test[:tests].size.zero?
|
163
|
+
end
|
164
|
+
tests
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fastlane'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Saucectl
|
5
|
+
UI = FastlaneCore::UI
|
6
|
+
# Return all .rb files inside the "actions" and "helper" directory
|
7
|
+
def self.all_classes
|
8
|
+
Dir[File.expand_path('**/actions/*_action.rb', File.dirname(__FILE__))]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# By default we want to import all available actions and helpers
|
14
|
+
# A plugin can contain any number of actions and plugins
|
15
|
+
Fastlane::Saucectl.all_classes.each do |current|
|
16
|
+
require current
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fastlane-plugin-saucectl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Hamilton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-03-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fastlane
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ox
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.5
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.14.5
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec_junit_formatter
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.12.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.12.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-performance
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop-require_tools
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description:
|
168
|
+
email: ian.ross.hamilton@gmail.com
|
169
|
+
executables: []
|
170
|
+
extensions: []
|
171
|
+
extra_rdoc_files: []
|
172
|
+
files:
|
173
|
+
- lib/fastlane/plugin/saucectl.rb
|
174
|
+
- lib/fastlane/plugin/saucectl/actions/delete_from_storage_action.rb
|
175
|
+
- lib/fastlane/plugin/saucectl/actions/disabled_tests_action.rb
|
176
|
+
- lib/fastlane/plugin/saucectl/actions/install_toolkit_action.rb
|
177
|
+
- lib/fastlane/plugin/saucectl/actions/sauce_apps_action.rb
|
178
|
+
- lib/fastlane/plugin/saucectl/actions/sauce_config_action.rb
|
179
|
+
- lib/fastlane/plugin/saucectl/actions/sauce_devices_action.rb
|
180
|
+
- lib/fastlane/plugin/saucectl/actions/sauce_runner_action.rb
|
181
|
+
- lib/fastlane/plugin/saucectl/actions/sauce_upload_action.rb
|
182
|
+
- lib/fastlane/plugin/saucectl/helper/api.rb
|
183
|
+
- lib/fastlane/plugin/saucectl/helper/config.rb
|
184
|
+
- lib/fastlane/plugin/saucectl/helper/espresso.rb
|
185
|
+
- lib/fastlane/plugin/saucectl/helper/file_utils.rb
|
186
|
+
- lib/fastlane/plugin/saucectl/helper/installer.rb
|
187
|
+
- lib/fastlane/plugin/saucectl/helper/runner.rb
|
188
|
+
- lib/fastlane/plugin/saucectl/helper/storage.rb
|
189
|
+
- lib/fastlane/plugin/saucectl/helper/suites.rb
|
190
|
+
- lib/fastlane/plugin/saucectl/helper/xctest.rb
|
191
|
+
- lib/fastlane/plugin/saucectl/version.rb
|
192
|
+
homepage: https://github.com/ianrhamilton/fastlane-plugin-saucectl
|
193
|
+
licenses:
|
194
|
+
- MIT
|
195
|
+
metadata:
|
196
|
+
rubygems_mfa_required: 'true'
|
197
|
+
post_install_message:
|
198
|
+
rdoc_options: []
|
199
|
+
require_paths:
|
200
|
+
- lib
|
201
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
202
|
+
requirements:
|
203
|
+
- - ">="
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '2.6'
|
206
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
|
+
requirements:
|
208
|
+
- - ">"
|
209
|
+
- !ruby/object:Gem::Version
|
210
|
+
version: 1.3.1
|
211
|
+
requirements: []
|
212
|
+
rubygems_version: 3.3.7
|
213
|
+
signing_key:
|
214
|
+
specification_version: 4
|
215
|
+
summary: Test your iOS and and Android apps at scale using Sauce Labs toolkit.
|
216
|
+
test_files: []
|