xcmonkey 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48bd9b79b2370e4f20366973e689af344f6c75729b361658fbc5bb2c02c38253
4
- data.tar.gz: c207e8dbe7c820623f60a9c0b9e8ea8a7e5bce18bfe10f9d5c27e8928829889d
3
+ metadata.gz: be5b38b4ec7977038ba4f739a4516a376bc79652447a0cc52c10465ea85657d2
4
+ data.tar.gz: 97d3eefd65f8a438bfb2ed2919bdaa6bf42e0324235d10a7f7df5b0ede316d88
5
5
  SHA512:
6
- metadata.gz: 9c0f1f52ad5e8118799556e4e1ba3a49574c504cf2a840fe0369e06c6dbab260fd0a9c275ada9efb789a865759ac48f7d46f9348cde5b2cb53a44969b5189cf9
7
- data.tar.gz: 761ffee247c8646bc24dee5fd3f473414402033d3c9a773fe87623579aef93155ecbafc3d412111396945ae485d74b28d6916e5e287754a82b15fcfc792b0c47
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
@@ -1,9 +1,14 @@
1
1
  # Changes
2
2
 
3
3
  ## References
4
+
4
5
  - https://github.com/alteral/xcmonkey/issues/XXX
5
6
 
7
+ ## Description
8
+
9
+
6
10
  ## Risks
11
+
7
12
  - [ ] None
8
13
  - [ ] Low
9
14
  - [ ] High
data/.gitignore CHANGED
@@ -39,3 +39,6 @@ DerivedData
39
39
 
40
40
  # Sonar
41
41
  .scannerwork
42
+
43
+ # Cache
44
+ xcmonkey-session.json
data/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  ## Description
13
13
 
14
- *xcmonkey* is a tool for doing randomised UI testing of iOS apps. It's inspired by and has similar goals to [*monkey*](https://developer.android.com/studio/test/monkey) on Android.
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.action do |args, options|
22
- options.default(duration: 60, enable_simulator_keyboard: true)
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
- simulator_keyboard: options.enable_simulator_keyboard
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 |args, options|
56
+ c.action do |_, options|
40
57
  params = {
41
58
  udid: options.udid,
42
59
  x: options.x,
@@ -1,9 +1,8 @@
1
1
  class Describer
2
- attr_accessor :udid, :x, :y, :driver
2
+ attr_accessor :x, :y, :driver
3
3
 
4
4
  def initialize(params)
5
5
  ensure_required_params(params)
6
- self.udid = params[:udid]
7
6
  self.x = params[:x]
8
7
  self.y = params[:y]
9
8
  self.driver = Driver.new(params)
@@ -1,18 +1,30 @@
1
1
  class Driver
2
- attr_accessor :udid, :bundle_id, :duration, :enable_simulator_keyboard
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.duration = params[:duration]
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 + duration
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
- Logger.error('App lost') if app_elements.include?(@home_tracker)
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
@@ -1,4 +1,4 @@
1
1
  module Xcmonkey
2
- VERSION = '0.3.0'
2
+ VERSION = '1.0.0'
3
3
  GEM_NAME = 'xcmonkey'
4
4
  end
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 :udid, :bundle_id, :duration, :driver
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 press' do
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
@@ -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.3.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-08 00:00:00.000000000 Z
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