xcmonkey 0.3.0 → 1.0.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/.github/issue_template/bug_report.md +26 -0
- data/.github/issue_template/feature_request.md +26 -0
- data/.github/pull_request_template.md +5 -0
- data/.gitignore +3 -0
- data/README.md +28 -1
- data/bin/xcmonkey +21 -4
- data/lib/xcmonkey/describer.rb +1 -2
- data/lib/xcmonkey/driver.rb +55 -4
- data/lib/xcmonkey/repeater.rb +39 -0
- data/lib/xcmonkey/version.rb +1 -1
- data/lib/xcmonkey.rb +6 -9
- data/spec/driver_spec.rb +41 -4
- data/spec/repeater_spec.rb +52 -0
- data/spec/xcmonkey_spec.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be5b38b4ec7977038ba4f739a4516a376bc79652447a0cc52c10465ea85657d2
|
4
|
+
data.tar.gz: 97d3eefd65f8a438bfb2ed2919bdaa6bf42e0324235d10a7f7df5b0ede316d88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94b8daa5da4df0d18b1644bf9d110978c070ba032e661d4bf1079f7fad90a2ed6df553bc8685a7267d7f819159ac615474c88c4ebdd6e00627b74339cc2f9e0e
|
7
|
+
data.tar.gz: 66fe245043c8f4887a3849c7e60bc6b8ee81cecfb56781f409e09a6651511d009acd2a2284e3534d67ef59abffb5432b3d85ff9495a951a4fc26dbd44bff38fb
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## What did you do?
|
11
|
+
|
12
|
+
|
13
|
+
## What did you expect to happen?
|
14
|
+
|
15
|
+
|
16
|
+
## What happened instead?
|
17
|
+
|
18
|
+
|
19
|
+
## Environment
|
20
|
+
|
21
|
+
- `xcmonkey` version:
|
22
|
+
- `idb` version:
|
23
|
+
- `xcode` version:
|
24
|
+
- `macOS` version:
|
25
|
+
|
26
|
+
## Additional context
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
name: Feature Request
|
3
|
+
about: Got any ideas about new features? Let us know!
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## What are you trying to achieve?
|
11
|
+
|
12
|
+
|
13
|
+
## If possible, how can you achieve this currently?
|
14
|
+
|
15
|
+
|
16
|
+
## What would be the better way?
|
17
|
+
|
18
|
+
|
19
|
+
## Environment
|
20
|
+
|
21
|
+
- `xcmonkey` version:
|
22
|
+
- `idb` version:
|
23
|
+
- `xcode` version:
|
24
|
+
- `macOS` version:
|
25
|
+
|
26
|
+
## Additional context
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
## Description
|
13
13
|
|
14
|
-
*xcmonkey* is a tool for doing
|
14
|
+
*xcmonkey* is a tool for doing stress testing of iOS apps. It's inspired by and has similar goals to [*monkey*](https://developer.android.com/studio/test/monkey) on Android.
|
15
15
|
|
16
16
|
Under the hood, *xcmonkey* uses [iOS Development Bridge](https://fbidb.io/) as a driver, that's why it's pretty smart and can do a lot of things, such as taps, swipes and presses. All that comes «pseudo-random» because it has access to the screen hierarchy, and so can either do actions blindly (like tapping on random points) or precisely (like tapping on the existing elements).
|
17
17
|
|
@@ -63,6 +63,33 @@ $ xcmonkey test --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA" --bundle-id "com.a
|
|
63
63
|
}
|
64
64
|
```
|
65
65
|
|
66
|
+
### To repeat the stress test from generated session
|
67
|
+
|
68
|
+
```bash
|
69
|
+
$ xcmonkey repeat --session-path "./xcmonkey-session.json"
|
70
|
+
12:48:13.333: Device info: iPhone 14 Pro | 413EA256-CFFB-4312-94A6-12592BEE4CBA | Booted | simulator | iOS 16.2 | x86_64 | /tmp/idb/413EA256-CFFB-4312-94A6-12592BEE4CBA_companion.sock
|
71
|
+
|
72
|
+
12:48:16.542: App info: com.apple.Maps | Maps | system | arm64, x86_64 | Running | Not Debuggable | pid=73416
|
73
|
+
|
74
|
+
12:48:20.195: Tap: {
|
75
|
+
"x": 53,
|
76
|
+
"y": 749
|
77
|
+
}
|
78
|
+
|
79
|
+
12:48:20.404: Swipe (0.5s): {
|
80
|
+
"x": 196,
|
81
|
+
"y": 426
|
82
|
+
} => {
|
83
|
+
"x": 143,
|
84
|
+
"y": 447
|
85
|
+
}
|
86
|
+
|
87
|
+
12:48:21.155: Press (1.2s): {
|
88
|
+
"x": 143,
|
89
|
+
"y": 323
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
66
93
|
### To describe the required point
|
67
94
|
|
68
95
|
```bash
|
data/bin/xcmonkey
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'commander/import'
|
4
4
|
require_relative '../lib/xcmonkey'
|
5
5
|
require_relative '../lib/xcmonkey/describer'
|
6
|
+
require_relative '../lib/xcmonkey/repeater'
|
6
7
|
require_relative '../lib/xcmonkey/logger'
|
7
8
|
require_relative '../lib/xcmonkey/driver'
|
8
9
|
require_relative '../lib/xcmonkey/version'
|
@@ -18,25 +19,41 @@ module Xcmonkey
|
|
18
19
|
c.option('-b', '--bundle-id STRING', String, 'Set target bundle identifier')
|
19
20
|
c.option('-d', '--duration SECONDS', Integer, 'Test duration in seconds. Defaults to `60`')
|
20
21
|
c.option('-k', '--enable-simulator-keyboard', 'Should simulator keyboard be enabled? Defaults to `true`')
|
21
|
-
c.
|
22
|
-
|
22
|
+
c.option('-s', '--session-path STRING', String, 'Path where monkey testing session should be saved. Defaults to current directory')
|
23
|
+
c.action do |_, options|
|
24
|
+
options.default(
|
25
|
+
duration: 60,
|
26
|
+
session_path: Dir.pwd,
|
27
|
+
enable_simulator_keyboard: true
|
28
|
+
)
|
23
29
|
params = {
|
24
30
|
udid: options.udid,
|
25
31
|
bundle_id: options.bundle_id,
|
26
32
|
duration: options.duration,
|
27
|
-
|
33
|
+
session_path: options.session_path,
|
34
|
+
enable_simulator_keyboard: options.enable_simulator_keyboard
|
28
35
|
}
|
29
36
|
Xcmonkey.new(params).run
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
40
|
+
command :repeat do |c|
|
41
|
+
c.syntax = 'xcmonkey repeat [options]'
|
42
|
+
c.description = 'Repeats given session'
|
43
|
+
c.option('-s', '--session-path STRING', String, 'Path to monkey testing session')
|
44
|
+
c.action do |_, options|
|
45
|
+
params = { session_path: options.session_path }
|
46
|
+
Repeater.new(params).run
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
33
50
|
command :describe do |c|
|
34
51
|
c.syntax = 'xcmonkey describe [options]'
|
35
52
|
c.description = 'Describes given point'
|
36
53
|
c.option('-u', '--udid STRING', String, 'Set device UDID')
|
37
54
|
c.option('-x', '--x STRING', 'Point `x` coordinate')
|
38
55
|
c.option('-y', '--y STRING', 'Point `y` coordinate')
|
39
|
-
c.action do |
|
56
|
+
c.action do |_, options|
|
40
57
|
params = {
|
41
58
|
udid: options.udid,
|
42
59
|
x: options.x,
|
data/lib/xcmonkey/describer.rb
CHANGED
data/lib/xcmonkey/driver.rb
CHANGED
@@ -1,18 +1,30 @@
|
|
1
1
|
class Driver
|
2
|
-
attr_accessor :udid, :bundle_id, :
|
2
|
+
attr_accessor :udid, :bundle_id, :enable_simulator_keyboard, :session_duration, :session_path, :session_actions
|
3
3
|
|
4
4
|
def initialize(params)
|
5
5
|
self.udid = params[:udid]
|
6
6
|
self.bundle_id = params[:bundle_id]
|
7
|
-
self.
|
7
|
+
self.session_duration = params[:duration]
|
8
|
+
self.session_path = params[:session_path]
|
8
9
|
self.enable_simulator_keyboard = params[:enable_simulator_keyboard]
|
10
|
+
self.session_actions = params[:session_actions]
|
11
|
+
@session = { params: params, actions: [] }
|
9
12
|
ensure_driver_installed
|
10
13
|
end
|
11
14
|
|
15
|
+
def monkey_test_precondition
|
16
|
+
ensure_device_exists
|
17
|
+
ensure_app_installed
|
18
|
+
terminate_app
|
19
|
+
open_home_screen(with_tracker: true)
|
20
|
+
launch_app
|
21
|
+
end
|
22
|
+
|
12
23
|
def monkey_test(gestures)
|
24
|
+
monkey_test_precondition
|
13
25
|
app_elements = describe_ui.shuffle
|
14
26
|
current_time = Time.now
|
15
|
-
while Time.now < current_time +
|
27
|
+
while Time.now < current_time + session_duration
|
16
28
|
el1_coordinates = central_coordinates(app_elements.first)
|
17
29
|
el2_coordinates = central_coordinates(app_elements.last)
|
18
30
|
case gestures.sample
|
@@ -40,7 +52,30 @@ class Driver
|
|
40
52
|
next
|
41
53
|
end
|
42
54
|
app_elements = describe_ui.shuffle
|
43
|
-
|
55
|
+
next unless app_elements.include?(@home_tracker)
|
56
|
+
|
57
|
+
save_session
|
58
|
+
Logger.error('App lost')
|
59
|
+
end
|
60
|
+
save_session
|
61
|
+
end
|
62
|
+
|
63
|
+
def repeat_monkey_test
|
64
|
+
monkey_test_precondition
|
65
|
+
session_actions.each do |action|
|
66
|
+
case action['type']
|
67
|
+
when 'tap'
|
68
|
+
tap(coordinates: { x: action['x'], y: action['y'] })
|
69
|
+
when 'press'
|
70
|
+
press(coordinates: { x: action['x'], y: action['y'] }, duration: action['duration'])
|
71
|
+
when 'swipe'
|
72
|
+
swipe(
|
73
|
+
start_coordinates: { x: action['x'], y: action['y'] },
|
74
|
+
end_coordinates: { x: action['endX'], y: action['endY'] },
|
75
|
+
duration: action['duration']
|
76
|
+
)
|
77
|
+
end
|
78
|
+
Logger.error('App lost') if describe_ui.shuffle.include?(@home_tracker)
|
44
79
|
end
|
45
80
|
end
|
46
81
|
|
@@ -113,11 +148,13 @@ class Driver
|
|
113
148
|
|
114
149
|
def tap(coordinates:)
|
115
150
|
Logger.info('Tap:', payload: JSON.pretty_generate(coordinates))
|
151
|
+
@session[:actions] << { type: :tap, x: coordinates[:x], y: coordinates[:y] } unless session_actions
|
116
152
|
`idb ui tap --udid #{udid} #{coordinates[:x]} #{coordinates[:y]}`
|
117
153
|
end
|
118
154
|
|
119
155
|
def press(coordinates:, duration:)
|
120
156
|
Logger.info("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
157
|
+
@session[:actions] << { type: :press, x: coordinates[:x], y: coordinates[:y], duration: duration } unless session_actions
|
121
158
|
`idb ui tap --udid #{udid} --duration #{duration} #{coordinates[:x]} #{coordinates[:y]}`
|
122
159
|
end
|
123
160
|
|
@@ -126,6 +163,16 @@ class Driver
|
|
126
163
|
"Swipe (#{duration}s):",
|
127
164
|
payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
|
128
165
|
)
|
166
|
+
unless session_actions
|
167
|
+
@session[:actions] << {
|
168
|
+
type: :swipe,
|
169
|
+
x: start_coordinates[:x],
|
170
|
+
y: start_coordinates[:y],
|
171
|
+
endX: end_coordinates[:x],
|
172
|
+
endY: end_coordinates[:y],
|
173
|
+
duration: duration
|
174
|
+
}
|
175
|
+
end
|
129
176
|
coordinates = "#{start_coordinates[:x]} #{start_coordinates[:y]} #{end_coordinates[:x]} #{end_coordinates[:y]}"
|
130
177
|
`idb ui swipe --udid #{udid} --duration #{duration} #{coordinates}`
|
131
178
|
end
|
@@ -168,6 +215,10 @@ class Driver
|
|
168
215
|
rand(0.5..1.5).ceil(1)
|
169
216
|
end
|
170
217
|
|
218
|
+
def save_session
|
219
|
+
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
|
220
|
+
end
|
221
|
+
|
171
222
|
private
|
172
223
|
|
173
224
|
def ensure_driver_installed
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Repeater
|
2
|
+
attr_accessor :udid, :bundle_id, :enable_simulator_keyboard, :actions
|
3
|
+
|
4
|
+
def initialize(params)
|
5
|
+
validate_session(params[:session_path])
|
6
|
+
end
|
7
|
+
|
8
|
+
def run
|
9
|
+
params = {
|
10
|
+
udid: udid,
|
11
|
+
bundle_id: bundle_id,
|
12
|
+
enable_simulator_keyboard: enable_simulator_keyboard,
|
13
|
+
session_actions: actions
|
14
|
+
}
|
15
|
+
Driver.new(params).repeat_monkey_test
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_session(session_path)
|
19
|
+
Logger.error("Provided session can't be found: #{session_path}") unless File.exist?(session_path)
|
20
|
+
|
21
|
+
session = JSON.parse(File.read(session_path))
|
22
|
+
|
23
|
+
if session['params'].nil?
|
24
|
+
Logger.error('Provided session is not valid: `params` should not be `nil`')
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
self.actions = session['actions']
|
29
|
+
Logger.error('Provided session is not valid: `actions` should not be `nil` or `empty`') if actions.nil? || actions.empty?
|
30
|
+
|
31
|
+
self.udid = session['params']['udid']
|
32
|
+
Logger.error('Provided session is not valid: `udid` should not be `nil`') if udid.nil?
|
33
|
+
|
34
|
+
self.bundle_id = session['params']['bundle_id']
|
35
|
+
Logger.error('Provided session is not valid: `bundle_id` should not be `nil`') if bundle_id.nil?
|
36
|
+
|
37
|
+
self.enable_simulator_keyboard = session['params']['enable_simulator_keyboard']
|
38
|
+
end
|
39
|
+
end
|
data/lib/xcmonkey/version.rb
CHANGED
data/lib/xcmonkey.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'colorize'
|
3
3
|
require_relative 'xcmonkey/describer'
|
4
|
+
require_relative 'xcmonkey/repeater'
|
4
5
|
require_relative 'xcmonkey/version'
|
5
6
|
require_relative 'xcmonkey/logger'
|
6
7
|
require_relative 'xcmonkey/driver'
|
7
8
|
|
8
9
|
module Xcmonkey
|
9
10
|
class Xcmonkey
|
10
|
-
attr_accessor :
|
11
|
+
attr_accessor :driver
|
11
12
|
|
12
13
|
def initialize(params)
|
13
14
|
ensure_required_params(params)
|
14
|
-
self.udid = params[:udid]
|
15
|
-
self.bundle_id = params[:bundle_id]
|
16
|
-
self.duration = params[:duration]
|
17
15
|
self.driver = Driver.new(params)
|
18
16
|
end
|
19
17
|
|
20
18
|
def run
|
21
|
-
driver.ensure_device_exists
|
22
|
-
driver.ensure_app_installed
|
23
|
-
driver.terminate_app
|
24
|
-
driver.open_home_screen(with_tracker: true)
|
25
|
-
driver.launch_app
|
26
19
|
driver.monkey_test(gestures)
|
27
20
|
end
|
28
21
|
|
@@ -35,7 +28,11 @@ module Xcmonkey
|
|
35
28
|
|
36
29
|
def ensure_required_params(params)
|
37
30
|
Logger.error('UDID should be provided') if params[:udid].nil?
|
31
|
+
|
38
32
|
Logger.error('Bundle identifier should be provided') if params[:bundle_id].nil?
|
33
|
+
|
34
|
+
Logger.error('Session path should be a directory') if params[:session_path].nil? || !File.directory?(params[:session_path])
|
35
|
+
|
39
36
|
if params[:duration].nil? || !params[:duration].kind_of?(Integer) || !params[:duration].positive?
|
40
37
|
Logger.error('Duration must be Integer and not less than 1 second')
|
41
38
|
end
|
data/spec/driver_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
describe Driver do
|
2
2
|
let(:udid) { `xcrun simctl list | grep " iPhone 14 Pro Max"`.split("\n")[0].split('(')[1].split(')')[0] }
|
3
3
|
let(:bundle_id) { 'com.apple.Maps' }
|
4
|
-
let(:driver) { described_class.new(udid: udid, bundle_id: bundle_id) }
|
4
|
+
let(:driver) { described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd) }
|
5
|
+
let(:driver_with_session) { described_class.new(udid: udid, bundle_id: bundle_id, session_actions: [{ type: 'tap', x: 0, y: 0 }]) }
|
5
6
|
|
6
7
|
it 'verifies that sumulator was booted' do
|
7
8
|
error_message = "Failed to boot #{udid}"
|
@@ -150,28 +151,64 @@ describe Driver do
|
|
150
151
|
expect { driver.launch_app }.not_to raise_error
|
151
152
|
end
|
152
153
|
|
153
|
-
it 'verifies tap' do
|
154
|
+
it 'verifies tap in new session' do
|
154
155
|
driver.boot_simulator
|
155
156
|
coordinates = { x: 1, y: 1 }
|
156
157
|
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
157
158
|
driver.tap(coordinates: coordinates)
|
159
|
+
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
158
160
|
end
|
159
161
|
|
160
|
-
it 'verifies
|
162
|
+
it 'verifies tap in old session' do
|
163
|
+
driver_with_session.boot_simulator
|
164
|
+
coordinates = { x: 1, y: 1 }
|
165
|
+
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
166
|
+
driver_with_session.tap(coordinates: coordinates)
|
167
|
+
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'verifies press in new session' do
|
161
171
|
driver.boot_simulator
|
162
172
|
duration = 0.5
|
163
173
|
coordinates = { x: 1, y: 1 }
|
164
174
|
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
165
175
|
driver.press(coordinates: coordinates, duration: duration)
|
176
|
+
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'verifies press in old session' do
|
180
|
+
driver_with_session.boot_simulator
|
181
|
+
duration = 0.5
|
182
|
+
coordinates = { x: 1, y: 1 }
|
183
|
+
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
184
|
+
driver_with_session.press(coordinates: coordinates, duration: duration)
|
185
|
+
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
166
186
|
end
|
167
187
|
|
168
|
-
it 'verifies swipe' do
|
188
|
+
it 'verifies swipe in new session' do
|
169
189
|
driver.boot_simulator
|
170
190
|
duration = 0.5
|
171
191
|
start_coordinates = { x: 1, y: 1 }
|
172
192
|
end_coordinates = { x: 2, y: 2 }
|
173
193
|
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
174
194
|
driver.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
195
|
+
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'verifies swipe in old session' do
|
199
|
+
driver_with_session.boot_simulator
|
200
|
+
duration = 0.5
|
201
|
+
start_coordinates = { x: 1, y: 1 }
|
202
|
+
end_coordinates = { x: 2, y: 2 }
|
203
|
+
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
204
|
+
driver_with_session.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
205
|
+
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'verifies that session can be saved' do
|
209
|
+
expect(File).to receive(:write)
|
210
|
+
driver.instance_variable_set(:@session, { params: {}, actions: [] })
|
211
|
+
driver.save_session
|
175
212
|
end
|
176
213
|
|
177
214
|
it 'verifies that simulator was not booted' do
|
@@ -0,0 +1,52 @@
|
|
1
|
+
describe Repeater do
|
2
|
+
let(:session_path) { 'test/path/session.json' }
|
3
|
+
let(:session_file_content_full) { '{ "params": {"udid": "0", "bundle_id": "0", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
4
|
+
let(:session_file_content_without_params) { '{ "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
5
|
+
let(:session_file_content_with_empty_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "enable_simulator_keyboard": true}, "actions": [] }' }
|
6
|
+
let(:session_file_content_without_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "enable_simulator_keyboard": true} }' }
|
7
|
+
let(:session_file_content_without_bundle_id) { '{ "params": {"udid": "0", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
8
|
+
let(:session_file_content_without_udid) { '{ "params": {"bundle_id": "0", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
9
|
+
|
10
|
+
# TESTME
|
11
|
+
it 'verifies that session cannot be validated without params' do
|
12
|
+
allow(File).to receive(:exist?).and_return(true)
|
13
|
+
allow(File).to receive(:read).and_return(session_file_content_without_params)
|
14
|
+
expect(Logger).to receive(:error).with('Provided session is not valid: `params` should not be `nil`')
|
15
|
+
described_class.new(session_path: session_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'verifies that session cannot be validated without actions' do
|
19
|
+
allow(File).to receive(:exist?).and_return(true)
|
20
|
+
allow(File).to receive(:read).and_return(session_file_content_without_actions)
|
21
|
+
expect(Logger).to receive(:error).with('Provided session is not valid: `actions` should not be `nil` or `empty`')
|
22
|
+
described_class.new(session_path: session_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'verifies that session cannot be validated with empty actions' do
|
26
|
+
allow(File).to receive(:exist?).and_return(true)
|
27
|
+
allow(File).to receive(:read).and_return(session_file_content_with_empty_actions)
|
28
|
+
expect(Logger).to receive(:error).with('Provided session is not valid: `actions` should not be `nil` or `empty`')
|
29
|
+
described_class.new(session_path: session_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'verifies that session cannot be validated without bundle id' do
|
33
|
+
allow(File).to receive(:exist?).and_return(true)
|
34
|
+
allow(File).to receive(:read).and_return(session_file_content_without_bundle_id)
|
35
|
+
expect(Logger).to receive(:error).with('Provided session is not valid: `bundle_id` should not be `nil`')
|
36
|
+
described_class.new(session_path: session_path)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'verifies that session cannot be validated without udid' do
|
40
|
+
allow(File).to receive(:exist?).and_return(true)
|
41
|
+
allow(File).to receive(:read).and_return(session_file_content_without_udid)
|
42
|
+
expect(Logger).to receive(:error).with('Provided session is not valid: `udid` should not be `nil`')
|
43
|
+
described_class.new(session_path: session_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'verifies that session validation can pass' do
|
47
|
+
allow(File).to receive(:exist?).and_return(true)
|
48
|
+
allow(File).to receive(:read).and_return(session_file_content_full)
|
49
|
+
expect(Logger).not_to receive(:error)
|
50
|
+
described_class.new(session_path: session_path)
|
51
|
+
end
|
52
|
+
end
|
data/spec/xcmonkey_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
describe Xcmonkey do
|
2
2
|
describe Xcmonkey::Xcmonkey do
|
3
|
-
let(:params) { { udid: '123', bundle_id: 'example.com.app', duration: 10 } }
|
3
|
+
let(:params) { { udid: '123', bundle_id: 'example.com.app', duration: 10, session_path: Dir.pwd } }
|
4
4
|
let(:duration_error_msg) { 'Duration must be Integer and not less than 1 second' }
|
5
5
|
|
6
6
|
it 'verifies gestures' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcmonkey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alteral
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -217,6 +217,8 @@ files:
|
|
217
217
|
- ".fasterer.yml"
|
218
218
|
- ".github/FUNDING.yml"
|
219
219
|
- ".github/dependabot.yml"
|
220
|
+
- ".github/issue_template/bug_report.md"
|
221
|
+
- ".github/issue_template/feature_request.md"
|
220
222
|
- ".github/pull_request_template.md"
|
221
223
|
- ".github/workflows/test.yml"
|
222
224
|
- ".gitignore"
|
@@ -236,12 +238,14 @@ files:
|
|
236
238
|
- lib/xcmonkey/describer.rb
|
237
239
|
- lib/xcmonkey/driver.rb
|
238
240
|
- lib/xcmonkey/logger.rb
|
241
|
+
- lib/xcmonkey/repeater.rb
|
239
242
|
- lib/xcmonkey/version.rb
|
240
243
|
- requirements.txt
|
241
244
|
- sonar-project.properties
|
242
245
|
- spec/describer_spec.rb
|
243
246
|
- spec/driver_spec.rb
|
244
247
|
- spec/logger_spec.rb
|
248
|
+
- spec/repeater_spec.rb
|
245
249
|
- spec/spec_helper.rb
|
246
250
|
- spec/xcmonkey_spec.rb
|
247
251
|
- xcmonkey.gemspec
|