xcmonkey 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/xcmonkey/driver.rb +63 -29
- data/lib/xcmonkey/version.rb +1 -1
- data/spec/describer_spec.rb +4 -2
- data/spec/driver_spec.rb +70 -28
- data/spec/xcmonkey_spec.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c93c94403332c085a393877d88d354b2736788f6e52d118eb8b9faa70c91b1e5
|
4
|
+
data.tar.gz: ee0f3bfcd014151b4821a6df50f601205581ebd480b5626e33588bda2928981a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ab88db343b0e83363130a9e9c8ed965acbf9d58f56d597b301ff12da4df9d4e79b08c3b2284b870a65c9bfc3d05a8ba9df0d19409631620a28836c9d5785814
|
7
|
+
data.tar.gz: fe46af5c215273be9210f3c880617457a12c6063e0bd1901d6abbd10750f10935b77a4797ff374926c3f1cac6b1ef79032f210fb705e05917146e4c64cede97c
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ gem 'xcmonkey'
|
|
39
39
|
### To run a stress test
|
40
40
|
|
41
41
|
```bash
|
42
|
-
xcmonkey test --
|
42
|
+
$ xcmonkey test --duration 100 --bundle-id "com.apple.Maps" --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
|
43
43
|
|
44
44
|
12:44:19.343: Device info: {
|
45
45
|
"name": "iPhone 14 Pro",
|
@@ -106,9 +106,9 @@ require 'xcmonkey'
|
|
106
106
|
|
107
107
|
lane :test do
|
108
108
|
Xcmonkey.new(
|
109
|
-
|
109
|
+
duration: 100,
|
110
110
|
bundle_id: 'com.apple.Maps',
|
111
|
-
|
111
|
+
udid: '413EA256-CFFB-4312-94A6-12592BEE4CBA'
|
112
112
|
).run
|
113
113
|
end
|
114
114
|
```
|
data/lib/xcmonkey/driver.rb
CHANGED
@@ -16,15 +16,16 @@ class Driver
|
|
16
16
|
puts
|
17
17
|
ensure_device_exists
|
18
18
|
ensure_app_installed
|
19
|
-
terminate_app
|
20
|
-
|
21
|
-
|
19
|
+
terminate_app(bundle_id)
|
20
|
+
launch_app(target_bundle_id: bundle_id, wait_for_state_update: true)
|
21
|
+
@running_apps = list_running_apps
|
22
22
|
end
|
23
23
|
|
24
24
|
def monkey_test(gestures)
|
25
25
|
monkey_test_precondition
|
26
26
|
app_elements = describe_ui.shuffle
|
27
27
|
current_time = Time.now
|
28
|
+
counter = 0
|
28
29
|
while Time.now < current_time + session_duration
|
29
30
|
el1_coordinates = central_coordinates(app_elements.first)
|
30
31
|
el2_coordinates = central_coordinates(app_elements.last)
|
@@ -52,17 +53,17 @@ class Driver
|
|
52
53
|
else
|
53
54
|
next
|
54
55
|
end
|
56
|
+
detect_app_state_change
|
57
|
+
track_running_apps if counter % 5 == 0 # Track running apps after every 5th action to speed up the test
|
58
|
+
counter += 1
|
55
59
|
app_elements = describe_ui.shuffle
|
56
|
-
next unless app_elements.include?(@home_tracker)
|
57
|
-
|
58
|
-
save_session
|
59
|
-
Logger.error('App lost')
|
60
60
|
end
|
61
61
|
save_session
|
62
62
|
end
|
63
63
|
|
64
64
|
def repeat_monkey_test
|
65
65
|
monkey_test_precondition
|
66
|
+
counter = 0
|
66
67
|
session_actions.each do |action|
|
67
68
|
case action['type']
|
68
69
|
when 'tap'
|
@@ -78,15 +79,12 @@ class Driver
|
|
78
79
|
else
|
79
80
|
next
|
80
81
|
end
|
81
|
-
|
82
|
+
detect_app_state_change
|
83
|
+
track_running_apps if counter % 5 == 0
|
84
|
+
counter += 1
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
85
|
-
def open_home_screen(with_tracker: false)
|
86
|
-
`idb ui button --udid #{udid} HOME`
|
87
|
-
detect_home_unique_element if with_tracker
|
88
|
-
end
|
89
|
-
|
90
88
|
def describe_ui
|
91
89
|
JSON.parse(`idb ui describe-all --udid #{udid}`)
|
92
90
|
end
|
@@ -97,13 +95,13 @@ class Driver
|
|
97
95
|
point_info
|
98
96
|
end
|
99
97
|
|
100
|
-
def launch_app
|
101
|
-
`idb launch --udid #{udid} #{
|
102
|
-
wait_until_app_launched
|
98
|
+
def launch_app(target_bundle_id:, wait_for_state_update: false)
|
99
|
+
`idb launch --udid #{udid} #{target_bundle_id}`
|
100
|
+
wait_until_app_launched(target_bundle_id) if wait_for_state_update
|
103
101
|
end
|
104
102
|
|
105
|
-
def terminate_app
|
106
|
-
`idb terminate --udid #{udid} #{
|
103
|
+
def terminate_app(target_bundle_id)
|
104
|
+
`idb terminate --udid #{udid} #{target_bundle_id} 2>/dev/null`
|
107
105
|
end
|
108
106
|
|
109
107
|
def boot_simulator
|
@@ -130,6 +128,10 @@ class Driver
|
|
130
128
|
`idb list-apps --udid #{udid} --json`.split("\n").map! { |app| JSON.parse(app) }
|
131
129
|
end
|
132
130
|
|
131
|
+
def list_running_apps
|
132
|
+
list_apps.select { |app| app['process_state'] == 'Running' }
|
133
|
+
end
|
134
|
+
|
133
135
|
def ensure_app_installed
|
134
136
|
return if list_apps.any? { |app| app['bundle_id'] == bundle_id }
|
135
137
|
|
@@ -144,6 +146,9 @@ class Driver
|
|
144
146
|
if device['type'] == 'simulator'
|
145
147
|
configure_simulator_keyboard
|
146
148
|
boot_simulator
|
149
|
+
else
|
150
|
+
Logger.error('xcmonkey does not support real devices yet. ' \
|
151
|
+
'For more information see https://github.com/alteral/xcmonkey/issues/7')
|
147
152
|
end
|
148
153
|
end
|
149
154
|
|
@@ -220,28 +225,57 @@ class Driver
|
|
220
225
|
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
|
221
226
|
end
|
222
227
|
|
223
|
-
|
228
|
+
# This function takes ≈200ms
|
229
|
+
def track_running_apps
|
230
|
+
current_list_of_running_apps = list_running_apps
|
231
|
+
if @running_apps != current_list_of_running_apps
|
232
|
+
currently_running_bundle_ids = current_list_of_running_apps.map { |app| app['bundle_id'] }
|
233
|
+
previously_running_bundle_ids = @running_apps.map { |app| app['bundle_id'] }
|
234
|
+
new_apps = currently_running_bundle_ids - previously_running_bundle_ids
|
224
235
|
|
225
|
-
|
226
|
-
|
236
|
+
return if new_apps.empty?
|
237
|
+
|
238
|
+
launch_app(target_bundle_id: bundle_id)
|
239
|
+
new_apps.each do |id|
|
240
|
+
Logger.warn("Shutting down: #{id}")
|
241
|
+
terminate_app(id)
|
242
|
+
end
|
243
|
+
end
|
227
244
|
end
|
228
245
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
246
|
+
# This function takes ≈300ms
|
247
|
+
def detect_app_state_change
|
248
|
+
return unless detect_app_in_background
|
249
|
+
|
250
|
+
target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
|
251
|
+
|
252
|
+
if target_app_is_running
|
253
|
+
launch_app(target_bundle_id: bundle_id)
|
254
|
+
else
|
255
|
+
save_session
|
256
|
+
Logger.error("Target app has crashed or been terminated")
|
233
257
|
end
|
234
|
-
@home_tracker
|
235
258
|
end
|
236
259
|
|
237
|
-
def
|
260
|
+
def detect_app_in_background
|
261
|
+
current_app_label = describe_ui.detect { |el| el['type'] == 'Application' }['AXLabel']
|
262
|
+
current_app_label.nil? || current_app_label.strip.empty?
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def ensure_driver_installed
|
268
|
+
Logger.error("'idb' doesn't seem to be installed") if `which idb`.strip.empty?
|
269
|
+
end
|
270
|
+
|
271
|
+
def wait_until_app_launched(target_bundle_id)
|
238
272
|
app_is_running = false
|
239
273
|
current_time = Time.now
|
240
274
|
while !app_is_running && Time.now < current_time + 5
|
241
|
-
app_info = list_apps.detect { |app| app['bundle_id'] ==
|
275
|
+
app_info = list_apps.detect { |app| app['bundle_id'] == target_bundle_id }
|
242
276
|
app_is_running = app_info && app_info['process_state'] == 'Running'
|
243
277
|
end
|
244
|
-
Logger.error("Can't run the app #{
|
278
|
+
Logger.error("Can't run the app #{target_bundle_id}") unless app_is_running
|
245
279
|
Logger.info('App info:', payload: JSON.pretty_generate(app_info))
|
246
280
|
end
|
247
281
|
end
|
data/lib/xcmonkey/version.rb
CHANGED
data/spec/describer_spec.rb
CHANGED
@@ -2,15 +2,17 @@ describe Describer do
|
|
2
2
|
let(:udid) { `xcrun simctl list | grep " iPhone 14 Pro Max"`.split("\n")[0].split('(')[1].split(')')[0] }
|
3
3
|
let(:driver) { Driver.new(udid: udid) }
|
4
4
|
|
5
|
-
|
5
|
+
before do
|
6
6
|
allow(Logger).to receive(:info)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'verifies that point can be described (integer)' do
|
7
10
|
driver.boot_simulator
|
8
11
|
point_info = described_class.new(udid: udid, x: 10, y: 10).run
|
9
12
|
expect(point_info).not_to be_empty
|
10
13
|
end
|
11
14
|
|
12
15
|
it 'verifies that point can be described (string)' do
|
13
|
-
allow(Logger).to receive(:info)
|
14
16
|
driver.boot_simulator
|
15
17
|
point_info = described_class.new(udid: udid, x: '10', y: '10').run
|
16
18
|
expect(point_info).not_to be_empty
|
data/spec/driver_spec.rb
CHANGED
@@ -4,6 +4,10 @@ describe Driver do
|
|
4
4
|
let(:driver) { described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd) }
|
5
5
|
let(:driver_with_session) { described_class.new(udid: udid, bundle_id: bundle_id, session_actions: [{ type: 'tap', x: 0, y: 0 }]) }
|
6
6
|
|
7
|
+
before do
|
8
|
+
allow(Logger).to receive(:info)
|
9
|
+
end
|
10
|
+
|
7
11
|
it 'verifies that sumulator was booted' do
|
8
12
|
error_message = "Failed to boot #{udid}"
|
9
13
|
expect(Logger).not_to receive(:error).with(error_message, payload: nil)
|
@@ -16,18 +20,6 @@ describe Driver do
|
|
16
20
|
expect(ui).not_to be_empty
|
17
21
|
end
|
18
22
|
|
19
|
-
it 'verifies that home screen can be opened' do
|
20
|
-
driver.boot_simulator
|
21
|
-
home_tracker = driver.open_home_screen(with_tracker: true)
|
22
|
-
expect(home_tracker).not_to be_empty
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'verifies that home screen can be opened without tracker' do
|
26
|
-
driver.boot_simulator
|
27
|
-
home_tracker = driver.open_home_screen(with_tracker: false)
|
28
|
-
expect(home_tracker).to be_nil
|
29
|
-
end
|
30
|
-
|
31
23
|
it 'verifies that list of targets can be showed' do
|
32
24
|
list_targets = driver.list_targets
|
33
25
|
expect(list_targets).not_to be_empty
|
@@ -56,9 +48,7 @@ describe Driver do
|
|
56
48
|
end
|
57
49
|
|
58
50
|
it 'verifies that device exists' do
|
59
|
-
payload = driver.list_targets.detect { |target| target['udid'] == udid }
|
60
51
|
expect(Logger).not_to receive(:error)
|
61
|
-
expect(Logger).to receive(:info).with('Device info:', payload: JSON.pretty_generate(payload))
|
62
52
|
expect(driver).to receive(:boot_simulator)
|
63
53
|
expect(driver).to receive(:configure_simulator_keyboard)
|
64
54
|
expect { driver.ensure_device_exists }.not_to raise_error
|
@@ -137,18 +127,25 @@ describe Driver do
|
|
137
127
|
expect(keyboard_state.first).to include('1')
|
138
128
|
end
|
139
129
|
|
140
|
-
it 'verifies that app can be launched' do
|
130
|
+
it 'verifies that app can be launched with waiting' do
|
131
|
+
expect(Logger).not_to receive(:error)
|
132
|
+
expect(driver).to receive(:wait_until_app_launched)
|
133
|
+
driver.boot_simulator
|
134
|
+
driver.terminate_app(bundle_id)
|
135
|
+
expect { driver.launch_app(target_bundle_id: bundle_id, wait_for_state_update: true) }.not_to raise_error
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'verifies that app can be launched without waiting' do
|
141
139
|
expect(Logger).not_to receive(:error)
|
142
|
-
expect(
|
140
|
+
expect(driver).not_to receive(:wait_until_app_launched)
|
143
141
|
driver.boot_simulator
|
144
|
-
driver.terminate_app
|
145
|
-
expect { driver.launch_app }.not_to raise_error
|
142
|
+
driver.terminate_app(bundle_id)
|
143
|
+
expect { driver.launch_app(target_bundle_id: bundle_id) }.not_to raise_error
|
146
144
|
end
|
147
145
|
|
148
146
|
it 'verifies tap in new session' do
|
149
147
|
driver.boot_simulator
|
150
148
|
coordinates = { x: 1, y: 1 }
|
151
|
-
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
152
149
|
driver.tap(coordinates: coordinates)
|
153
150
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
154
151
|
end
|
@@ -156,7 +153,6 @@ describe Driver do
|
|
156
153
|
it 'verifies tap in old session' do
|
157
154
|
driver_with_session.boot_simulator
|
158
155
|
coordinates = { x: 1, y: 1 }
|
159
|
-
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
160
156
|
driver_with_session.tap(coordinates: coordinates)
|
161
157
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
162
158
|
end
|
@@ -165,7 +161,6 @@ describe Driver do
|
|
165
161
|
driver.boot_simulator
|
166
162
|
duration = 0.5
|
167
163
|
coordinates = { x: 1, y: 1 }
|
168
|
-
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
169
164
|
driver.press(coordinates: coordinates, duration: duration)
|
170
165
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
171
166
|
end
|
@@ -174,7 +169,6 @@ describe Driver do
|
|
174
169
|
driver_with_session.boot_simulator
|
175
170
|
duration = 0.5
|
176
171
|
coordinates = { x: 1, y: 1 }
|
177
|
-
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
178
172
|
driver_with_session.press(coordinates: coordinates, duration: duration)
|
179
173
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
180
174
|
end
|
@@ -184,7 +178,6 @@ describe Driver do
|
|
184
178
|
duration = 0.5
|
185
179
|
start_coordinates = { x: 1, y: 1 }
|
186
180
|
end_coordinates = { x: 2, y: 2 }
|
187
|
-
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
188
181
|
driver.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
189
182
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
190
183
|
end
|
@@ -194,7 +187,6 @@ describe Driver do
|
|
194
187
|
duration = 0.5
|
195
188
|
start_coordinates = { x: 1, y: 1 }
|
196
189
|
end_coordinates = { x: 2, y: 2 }
|
197
|
-
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
198
190
|
driver_with_session.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
199
191
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
200
192
|
end
|
@@ -210,12 +202,12 @@ describe Driver do
|
|
210
202
|
app_info = driver.list_apps.detect { |app| app['bundle_id'] == bundle_id }
|
211
203
|
app_is_running = app_info && app_info['process_state'] == 'Running'
|
212
204
|
expect(app_is_running).to be(true)
|
205
|
+
expect(driver.instance_variable_get(:@running_apps)).not_to be_nil
|
213
206
|
end
|
214
207
|
|
215
208
|
it 'verifies that monkey_test works fine' do
|
216
209
|
params = { udid: udid, bundle_id: bundle_id, duration: 1, session_path: Dir.pwd }
|
217
210
|
driver = described_class.new(params)
|
218
|
-
expect(driver).to receive(:monkey_test_precondition)
|
219
211
|
driver.monkey_test(Xcmonkey.new(params).gestures)
|
220
212
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
221
213
|
end
|
@@ -227,8 +219,6 @@ describe Driver do
|
|
227
219
|
{ 'type' => 'swipe', 'x' => 12, 'y' => 12, 'endX' => 15, 'endY' => 15, 'duration' => 0.3 }
|
228
220
|
]
|
229
221
|
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_actions: session_actions)
|
230
|
-
allow(Logger).to receive(:info).twice
|
231
|
-
expect(driver).to receive(:monkey_test_precondition)
|
232
222
|
expect(driver).to receive(:tap).with(coordinates: { x: 10, y: 10 })
|
233
223
|
expect(driver).to receive(:press).with(coordinates: { x: 11, y: 11 }, duration: 1.4)
|
234
224
|
expect(driver).to receive(:swipe).with(start_coordinates: { x: 12, y: 12 }, end_coordinates: { x: 15, y: 15 }, duration: 0.3)
|
@@ -238,7 +228,6 @@ describe Driver do
|
|
238
228
|
|
239
229
|
it 'verifies that unknown actions does not break repeat_monkey_test' do
|
240
230
|
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_actions: [{ 'type' => 'test', 'x' => 10, 'y' => 10 }])
|
241
|
-
allow(Logger).to receive(:info).twice
|
242
231
|
expect(driver).to receive(:monkey_test_precondition)
|
243
232
|
expect(driver).not_to receive(:tap)
|
244
233
|
expect(driver).not_to receive(:press)
|
@@ -247,6 +236,59 @@ describe Driver do
|
|
247
236
|
expect(driver.instance_variable_get(:@session)[:actions]).to be_empty
|
248
237
|
end
|
249
238
|
|
239
|
+
it 'verifies that running apps are tracked' do
|
240
|
+
new_app_bundle_id = 'com.apple.Preferences'
|
241
|
+
driver.terminate_app(new_app_bundle_id)
|
242
|
+
driver.monkey_test_precondition
|
243
|
+
driver.launch_app(target_bundle_id: new_app_bundle_id, wait_for_state_update: true)
|
244
|
+
expect(driver).to receive(:launch_app).with(target_bundle_id: bundle_id)
|
245
|
+
expect(driver).to receive(:terminate_app).with(new_app_bundle_id)
|
246
|
+
driver.track_running_apps
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'verifies that running apps can be determined' do
|
250
|
+
driver.terminate_app(bundle_id)
|
251
|
+
sum = driver.list_running_apps.size
|
252
|
+
driver.launch_app(target_bundle_id: bundle_id)
|
253
|
+
expect(driver.list_running_apps.size).to eq(sum + 1)
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'verifies that app state change can be determined' do
|
257
|
+
driver.launch_app(target_bundle_id: bundle_id)
|
258
|
+
allow(driver).to receive(:detect_app_in_background).and_return(true)
|
259
|
+
expect(driver).not_to receive(:save_session)
|
260
|
+
expect(driver).to receive(:launch_app)
|
261
|
+
expect { driver.detect_app_state_change }.not_to raise_error
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'verifies that background is the invalid app state' do
|
265
|
+
driver.terminate_app(bundle_id)
|
266
|
+
expect(driver).to receive(:save_session)
|
267
|
+
expect { driver.detect_app_state_change }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'verifies that foreground is the valid app state' do
|
271
|
+
driver.launch_app(target_bundle_id: bundle_id, wait_for_state_update: true)
|
272
|
+
expect { driver.detect_app_state_change }.not_to raise_error
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'verifies that background state can be determined' do
|
276
|
+
driver.terminate_app(bundle_id)
|
277
|
+
expect(driver.detect_app_in_background).to be(true)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'verifies that foregroung state can be determined' do
|
281
|
+
driver.monkey_test_precondition
|
282
|
+
expect(driver.detect_app_in_background).to be(false)
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'verifies that xcmonkey behaves as expected on real devices' do
|
286
|
+
udid = '1234-5678'
|
287
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id)
|
288
|
+
allow(driver).to receive(:list_targets).and_return([{ 'udid' => udid, 'type' => 'device' }])
|
289
|
+
expect { driver.ensure_device_exists }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
290
|
+
end
|
291
|
+
|
250
292
|
it 'verifies that simulator was not booted' do
|
251
293
|
driver.shutdown_simulator
|
252
294
|
error_message = "Failed to boot #{udid}"
|
data/spec/xcmonkey_spec.rb
CHANGED
@@ -2,6 +2,10 @@ describe Xcmonkey do
|
|
2
2
|
let(:params) { { udid: '123', bundle_id: 'example.com.app', duration: 10, session_path: Dir.pwd } }
|
3
3
|
let(:duration_error_msg) { 'Duration must be Integer and not less than 1 second' }
|
4
4
|
|
5
|
+
before do
|
6
|
+
allow(Logger).to receive(:info)
|
7
|
+
end
|
8
|
+
|
5
9
|
it 'verifies gestures' do
|
6
10
|
gestures = described_class.new(params).gestures
|
7
11
|
taps = [:precise_tap, :blind_tap] * 10
|
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: 1.
|
4
|
+
version: 1.2.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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|