xcmonkey 0.1.1 → 0.2.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/FUNDING.yml +1 -4
- data/.github/workflows/test.yml +38 -0
- data/README.md +74 -11
- data/assets/images/xcmonkey.png +0 -0
- data/fastlane/Fastfile +28 -0
- data/lib/xcmonkey/driver.rb +78 -18
- data/lib/xcmonkey/version.rb +1 -1
- data/lib/xcmonkey.rb +5 -2
- data/requirements.txt +1 -0
- data/sonar-project.properties +11 -0
- data/spec/driver_spec.rb +74 -4
- data/spec/logger_spec.rb +20 -0
- data/spec/xcmonkey_spec.rb +13 -1
- metadata +6 -3
- data/.github/workflows/Test.yml +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be6683e9fefe9666fb294e9939f5ebd83c71915307002dc6425413dfd1d9ae8b
|
4
|
+
data.tar.gz: d4782c2269b36dd950b7567c88d554c7b000cc81452f79e6659d4061135c1ebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d027d46289c6ea5bfba7359ff5c0b8172ea7d12d5d9cd1019a428b7b74416e6562930db5ce14508198937caed55ac2b5f1c68f1ce34c82fbe6ecbf3a92ea7100
|
7
|
+
data.tar.gz: 6d336b581f1bb93db017414b7080e038557200909e8a87b3a23c37fce4dfdd62b9fc5186c45272e662002f073c00a800f172488495b9bf84cd91d0a9089e275c
|
data/.github/FUNDING.yml
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
- develop
|
8
|
+
|
9
|
+
pull_request:
|
10
|
+
types: [opened, synchronize]
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
chat:
|
14
|
+
name: Automated Code Review
|
15
|
+
runs-on: macos-12
|
16
|
+
timeout-minutes: 30
|
17
|
+
env:
|
18
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
19
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v3.2.0
|
22
|
+
|
23
|
+
- uses: actions/setup-python@v4.4.0
|
24
|
+
with:
|
25
|
+
python-version: 3.11
|
26
|
+
cache: 'pip'
|
27
|
+
|
28
|
+
- run: bundle install
|
29
|
+
|
30
|
+
- run: brew install sonar-scanner
|
31
|
+
|
32
|
+
- run: brew install facebook/fb/idb-companion
|
33
|
+
|
34
|
+
- run: pip install -r requirements.txt
|
35
|
+
|
36
|
+
- run: bundle exec fastlane code_review
|
37
|
+
|
38
|
+
- run: bundle exec fastlane sonar_upload
|
data/README.md
CHANGED
@@ -1,36 +1,99 @@
|
|
1
|
-
|
1
|
+
<p align="center">
|
2
|
+
<img src="/assets/images/xcmonkey.png"/>
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p align="center">
|
6
|
+
<a href="https://github.com/alteral/xcmonkey/actions"><img src="https://github.com/alteral/xcmonkey/actions/workflows/test.yml/badge.svg" /></a>
|
7
|
+
<a href="https://sonarcloud.io/summary/new_code?id=alteral_xcmonkey"><img src="https://sonarcloud.io/api/project_badges/measure?project=alteral_xcmonkey&metric=coverage" /></a>
|
8
|
+
<a href="https://rubygems.org/gems/xcmonkey"><img src="https://img.shields.io/gem/v/xcmonkey.svg?style=flat" /></a>
|
9
|
+
<a href="/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat" /></a>
|
10
|
+
</p>
|
11
|
+
|
12
|
+
## Description
|
13
|
+
|
14
|
+
*xcmonkey* is a tool for doing randomised UI testing of iOS apps.
|
15
|
+
|
16
|
+
It is inspired by and has similar goals to Android [monkey](https://developer.android.com/studio/test/monkey).
|
17
|
+
|
18
|
+
*xcmonkey* uses [idb](https://fbidb.io) as a driver that's why it's quite smart and can do a lot of things, such as taps, swipes and presses. Because *xcmonkey* has access to the screen hierarchy, it can either do things blindly (like tapping on random points) or precisely (like tapping on the existing elements).
|
2
19
|
|
3
20
|
## Requirements
|
4
21
|
|
5
22
|
```bash
|
6
|
-
brew
|
7
|
-
|
23
|
+
brew install facebook/fb/idb-companion
|
24
|
+
pip3.6 install fb-idb
|
8
25
|
```
|
9
26
|
|
10
27
|
## Installation
|
11
28
|
|
12
29
|
```bash
|
13
|
-
|
30
|
+
gem install xcmonkey
|
14
31
|
```
|
15
32
|
|
16
|
-
|
33
|
+
If you prefer to use [bundler](https://bundler.io/), add the following line to your `Gemfile`:
|
17
34
|
|
18
|
-
```
|
19
|
-
gem
|
35
|
+
```ruby
|
36
|
+
gem 'xcmonkey'
|
20
37
|
```
|
21
38
|
|
22
39
|
## Usage
|
23
40
|
|
24
|
-
###
|
41
|
+
### To run a stress test
|
25
42
|
|
26
43
|
```bash
|
27
|
-
xcmonkey test --udid "
|
44
|
+
$ xcmonkey test --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA" --bundle-id "com.apple.Maps" --duration 100
|
45
|
+
12:44:19.343: 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
|
46
|
+
|
47
|
+
12:44:22.550: App info: com.apple.Maps | Maps | system | arm64, x86_64 | Running | Not Debuggable | pid=74636
|
48
|
+
|
49
|
+
12:44:23.203: Tap: {
|
50
|
+
"x": 53,
|
51
|
+
"y": 749
|
52
|
+
}
|
53
|
+
|
54
|
+
12:44:23.511: Swipe (0.5s): {
|
55
|
+
"x": 196,
|
56
|
+
"y": 426
|
57
|
+
} => {
|
58
|
+
"x": 143,
|
59
|
+
"y": 447
|
60
|
+
}
|
61
|
+
|
62
|
+
12:44:24.355: Press (1.2s): {
|
63
|
+
"x": 143,
|
64
|
+
"y": 323
|
65
|
+
}
|
28
66
|
```
|
29
67
|
|
30
|
-
###
|
68
|
+
### To describe the required point
|
31
69
|
|
32
70
|
```bash
|
33
|
-
xcmonkey describe -x
|
71
|
+
$ xcmonkey describe -x 20 -y 625 --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
|
72
|
+
20:05:20.212: 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
|
73
|
+
|
74
|
+
20:05:21.713: x:20 y:625 point info: {
|
75
|
+
"AXFrame": "{{19, 624.33333333333337}, {86, 130.66666666666663}}",
|
76
|
+
"AXUniqueId": "ShortcutsRowCell",
|
77
|
+
"frame": {
|
78
|
+
"y": 624.3333333333334,
|
79
|
+
"x": 19,
|
80
|
+
"width": 86,
|
81
|
+
"height": 130.66666666666663
|
82
|
+
},
|
83
|
+
"role_description": "button",
|
84
|
+
"AXLabel": "Home",
|
85
|
+
"content_required": false,
|
86
|
+
"type": "Button",
|
87
|
+
"title": null,
|
88
|
+
"help": null,
|
89
|
+
"custom_actions": [
|
90
|
+
|
91
|
+
],
|
92
|
+
"AXValue": "Add",
|
93
|
+
"enabled": true,
|
94
|
+
"role": "AXButton",
|
95
|
+
"subrole": null
|
96
|
+
}
|
34
97
|
```
|
35
98
|
|
36
99
|
## Code of Conduct
|
Binary file
|
data/fastlane/Fastfile
CHANGED
@@ -20,6 +20,34 @@ lane :code_review do
|
|
20
20
|
sh('bundle exec rake')
|
21
21
|
end
|
22
22
|
|
23
|
+
lane :sonar_upload do
|
24
|
+
update_simplecov_report
|
25
|
+
sonar_options =
|
26
|
+
if ENV['GITHUB_EVENT_NAME'] == 'pull_request'
|
27
|
+
{
|
28
|
+
sonar_login: ENV.fetch('SONAR_TOKEN', nil),
|
29
|
+
pull_request_branch: ENV.fetch('GITHUB_HEAD_REF', nil),
|
30
|
+
pull_request_base: ENV.fetch('GITHUB_BASE_REF', nil),
|
31
|
+
pull_request_key: ENV.fetch('PR_NUMBER', nil)
|
32
|
+
}
|
33
|
+
else
|
34
|
+
{
|
35
|
+
sonar_login: ENV.fetch('SONAR_TOKEN', nil),
|
36
|
+
branch_name: ENV['BRANCH_NAME'] || git_branch,
|
37
|
+
project_version: version
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
sonar(sonar_options)
|
42
|
+
end
|
43
|
+
|
44
|
+
private_lane :update_simplecov_report do
|
45
|
+
coverage = JSON.parse(File.read('../coverage/.resultset.json'))["RSpec"]["coverage"]
|
46
|
+
transformed_values = coverage.transform_values { |l| l["lines"] }
|
47
|
+
new_format = { "RSpec" => { "coverage" => transformed_values, "timestamp" => Time.now.to_i } }
|
48
|
+
File.write('../coverage/.sonar.json', JSON.pretty_generate(new_format))
|
49
|
+
end
|
50
|
+
|
23
51
|
def version
|
24
52
|
version_path = '../lib/xcmonkey/version.rb'
|
25
53
|
File.read(version_path).scan(/\d+/).join('.')
|
data/lib/xcmonkey/driver.rb
CHANGED
@@ -18,20 +18,34 @@ class Driver
|
|
18
18
|
when :precise_tap
|
19
19
|
tap(coordinates: el1_coordinates)
|
20
20
|
when :blind_tap
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
when :
|
25
|
-
|
21
|
+
tap(coordinates: random_coordinates)
|
22
|
+
when :precise_press
|
23
|
+
press(coordinates: el1_coordinates, duration: press_duration)
|
24
|
+
when :blind_press
|
25
|
+
press(coordinates: random_coordinates, duration: press_duration)
|
26
|
+
when :precise_swipe
|
27
|
+
swipe(
|
28
|
+
start_coordinates: el1_coordinates,
|
29
|
+
end_coordinates: el2_coordinates,
|
30
|
+
duration: swipe_duration
|
31
|
+
)
|
32
|
+
when :blind_swipe
|
33
|
+
swipe(
|
34
|
+
start_coordinates: random_coordinates,
|
35
|
+
end_coordinates: random_coordinates,
|
36
|
+
duration: swipe_duration
|
37
|
+
)
|
38
|
+
else
|
39
|
+
next
|
26
40
|
end
|
27
41
|
app_elements = describe_ui.shuffle
|
28
42
|
Logger.error('App lost') if app_elements.include?(@home_tracker)
|
29
43
|
end
|
30
44
|
end
|
31
45
|
|
32
|
-
def open_home_screen(
|
46
|
+
def open_home_screen(with_tracker: false)
|
33
47
|
`idb ui button --udid #{udid} HOME`
|
34
|
-
detect_home_unique_element if
|
48
|
+
detect_home_unique_element if with_tracker
|
35
49
|
end
|
36
50
|
|
37
51
|
def describe_ui
|
@@ -46,6 +60,7 @@ class Driver
|
|
46
60
|
|
47
61
|
def launch_app
|
48
62
|
`idb launch --udid #{udid} #{bundle_id}`
|
63
|
+
wait_until_app_launched
|
49
64
|
end
|
50
65
|
|
51
66
|
def terminate_app
|
@@ -54,7 +69,7 @@ class Driver
|
|
54
69
|
|
55
70
|
def boot_simulator
|
56
71
|
`idb boot #{udid}`
|
57
|
-
|
72
|
+
Logger.error("Failed to boot #{udid}") if device_info['state'] != 'Booted'
|
58
73
|
end
|
59
74
|
|
60
75
|
def shutdown_simulator
|
@@ -81,11 +96,6 @@ class Driver
|
|
81
96
|
boot_simulator if device.include?('simulator')
|
82
97
|
end
|
83
98
|
|
84
|
-
def ensure_simulator_was_booted
|
85
|
-
sim = list_booted_simulators.detect { |target| target.include?(udid) }
|
86
|
-
Logger.error("Failed to boot #{udid}") if sim.nil?
|
87
|
-
end
|
88
|
-
|
89
99
|
def list_apps
|
90
100
|
`idb list-apps --udid #{udid}`
|
91
101
|
end
|
@@ -95,20 +105,58 @@ class Driver
|
|
95
105
|
`idb ui tap --udid #{udid} #{coordinates[:x]} #{coordinates[:y]}`
|
96
106
|
end
|
97
107
|
|
98
|
-
def
|
99
|
-
Logger.info(
|
108
|
+
def press(coordinates:, duration:)
|
109
|
+
Logger.info("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
110
|
+
`idb ui tap --udid #{udid} --duration #{duration} #{coordinates[:x]} #{coordinates[:y]}`
|
111
|
+
end
|
112
|
+
|
113
|
+
def swipe(start_coordinates:, end_coordinates:, duration:)
|
114
|
+
Logger.info(
|
115
|
+
"Swipe (#{duration}s):",
|
116
|
+
payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
|
117
|
+
)
|
100
118
|
coordinates = "#{start_coordinates[:x]} #{start_coordinates[:y]} #{end_coordinates[:x]} #{end_coordinates[:y]}"
|
101
|
-
`idb ui swipe --udid #{udid} --duration
|
119
|
+
`idb ui swipe --udid #{udid} --duration #{duration} #{coordinates}`
|
102
120
|
end
|
103
121
|
|
104
122
|
def central_coordinates(element)
|
105
123
|
frame = element['frame']
|
124
|
+
x = (frame['x'] + (frame['width'] / 2)).abs.to_i
|
125
|
+
y = (frame['y'] + (frame['height'] / 2)).abs.to_i
|
126
|
+
{
|
127
|
+
x: x > screen_size[:width].to_i ? rand(0..screen_size[:width].to_i) : x,
|
128
|
+
y: y > screen_size[:height].to_i ? rand(0..screen_size[:height].to_i) : y
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def random_coordinates
|
106
133
|
{
|
107
|
-
x: (
|
108
|
-
y: (
|
134
|
+
x: rand(0..screen_size[:width].to_i),
|
135
|
+
y: rand(0..screen_size[:height].to_i)
|
109
136
|
}
|
110
137
|
end
|
111
138
|
|
139
|
+
def device_info
|
140
|
+
@device_info ||= JSON.parse(`idb describe --udid #{udid} --json`)
|
141
|
+
@device_info
|
142
|
+
end
|
143
|
+
|
144
|
+
def screen_size
|
145
|
+
screen_dimensions = device_info['screen_dimensions']
|
146
|
+
{
|
147
|
+
width: screen_dimensions['width_points'],
|
148
|
+
height: screen_dimensions['height_points']
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def swipe_duration
|
153
|
+
rand(0.1..0.7).ceil(1)
|
154
|
+
end
|
155
|
+
|
156
|
+
def press_duration
|
157
|
+
rand(0.5..1.5).ceil(1)
|
158
|
+
end
|
159
|
+
|
112
160
|
private
|
113
161
|
|
114
162
|
def ensure_driver_installed
|
@@ -122,4 +170,16 @@ class Driver
|
|
122
170
|
end
|
123
171
|
@home_tracker
|
124
172
|
end
|
173
|
+
|
174
|
+
def wait_until_app_launched
|
175
|
+
app_info = nil
|
176
|
+
current_time = Time.now
|
177
|
+
while app_info.nil? && Time.now < current_time + 5
|
178
|
+
app_info = list_apps.split("\n").detect do |app|
|
179
|
+
app =~ /#{bundle_id}.*Running/
|
180
|
+
end
|
181
|
+
end
|
182
|
+
Logger.error("Can't run the app #{bundle_id}") if app_info.nil?
|
183
|
+
Logger.info('App info:', payload: app_info)
|
184
|
+
end
|
125
185
|
end
|
data/lib/xcmonkey/version.rb
CHANGED
data/lib/xcmonkey.rb
CHANGED
@@ -21,13 +21,16 @@ module Xcmonkey
|
|
21
21
|
driver.ensure_device_exists
|
22
22
|
driver.ensure_app_installed
|
23
23
|
driver.terminate_app
|
24
|
-
driver.open_home_screen(
|
24
|
+
driver.open_home_screen(with_tracker: true)
|
25
25
|
driver.launch_app
|
26
26
|
driver.monkey_test(gestures)
|
27
27
|
end
|
28
28
|
|
29
29
|
def gestures
|
30
|
-
[:precise_tap, :blind_tap
|
30
|
+
taps = [:precise_tap, :blind_tap] * 10
|
31
|
+
swipes = [:precise_swipe, :blind_swipe] * 5
|
32
|
+
presses = [:precise_press, :blind_press]
|
33
|
+
taps + swipes + presses
|
31
34
|
end
|
32
35
|
|
33
36
|
def ensure_required_params(params)
|
data/requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
fb-idb
|
@@ -0,0 +1,11 @@
|
|
1
|
+
sonar.projectKey=alteral_xcmonkey
|
2
|
+
sonar.projectName=xcmonkey
|
3
|
+
sonar.host.url=https://sonarcloud.io
|
4
|
+
sonar.organization=alteral
|
5
|
+
sonar.ruby.coverage.reportPaths=coverage/.sonar.json
|
6
|
+
sonar.ruby.coverage.framework=RSpec
|
7
|
+
sonar.sources=lib/
|
8
|
+
sonar.inclusions=lib/**/*.rb
|
9
|
+
sonar.exclusions=lib/**/version.rb
|
10
|
+
sonar.tests=spec/
|
11
|
+
sonar.test.inclusions=spec/*_spec.rb
|
data/spec/driver_spec.rb
CHANGED
@@ -6,8 +6,7 @@ describe Driver do
|
|
6
6
|
it 'verifies that sumulator was booted' do
|
7
7
|
error_message = "Failed to boot #{udid}"
|
8
8
|
expect(Logger).not_to receive(:error).with(error_message, payload: nil)
|
9
|
-
expect
|
10
|
-
driver.boot_simulator
|
9
|
+
expect { driver.boot_simulator }.not_to raise_error
|
11
10
|
end
|
12
11
|
|
13
12
|
it 'verifies that there are booted simulators' do
|
@@ -24,10 +23,16 @@ describe Driver do
|
|
24
23
|
|
25
24
|
it 'verifies that home screen can be opened' do
|
26
25
|
driver.boot_simulator
|
27
|
-
home_tracker = driver.open_home_screen(
|
26
|
+
home_tracker = driver.open_home_screen(with_tracker: true)
|
28
27
|
expect(home_tracker).not_to be_empty
|
29
28
|
end
|
30
29
|
|
30
|
+
it 'verifies that home screen can be opened without tracker' do
|
31
|
+
driver.boot_simulator
|
32
|
+
home_tracker = driver.open_home_screen(with_tracker: false)
|
33
|
+
expect(home_tracker).to be_nil
|
34
|
+
end
|
35
|
+
|
31
36
|
it 'verifies that list of targets can be showed' do
|
32
37
|
list_targets = driver.list_targets
|
33
38
|
expect(list_targets).not_to be_empty
|
@@ -85,10 +90,75 @@ describe Driver do
|
|
85
90
|
expect(actual_coordinates).to eq(expected_coordinates)
|
86
91
|
end
|
87
92
|
|
93
|
+
it 'verifies that device info can be for booted simulator' do
|
94
|
+
driver.boot_simulator
|
95
|
+
expect(driver.device_info).not_to be_empty
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'verifies that device info can be for not booted simulator' do
|
99
|
+
driver.shutdown_simulator
|
100
|
+
expect(driver.device_info).not_to be_empty
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'verifies that screen size can be found' do
|
104
|
+
driver.boot_simulator
|
105
|
+
screen_size = driver.screen_size
|
106
|
+
expect(screen_size[:width]).to be > 0
|
107
|
+
expect(screen_size[:height]).to be > 0
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'verifies that random coordinates can be found' do
|
111
|
+
driver.boot_simulator
|
112
|
+
coordinates = driver.random_coordinates
|
113
|
+
expect(coordinates[:x]).to be > 0
|
114
|
+
expect(coordinates[:y]).to be > 0
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'verifies swipe duration' do
|
118
|
+
expect(driver.swipe_duration).to be_between(0.1, 0.7)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'verifies press duration' do
|
122
|
+
expect(driver.press_duration).to be_between(0.5, 1.5)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'verifies that app can be launched' do
|
126
|
+
expect(Logger).not_to receive(:error)
|
127
|
+
expect(Logger).to receive(:info)
|
128
|
+
driver.boot_simulator
|
129
|
+
driver.terminate_app
|
130
|
+
expect { driver.launch_app }.not_to raise_error
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'verifies tap' do
|
134
|
+
driver.boot_simulator
|
135
|
+
coordinates = { x: 1, y: 1 }
|
136
|
+
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
137
|
+
driver.tap(coordinates: coordinates)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'verifies press' do
|
141
|
+
driver.boot_simulator
|
142
|
+
duration = 0.5
|
143
|
+
coordinates = { x: 1, y: 1 }
|
144
|
+
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
145
|
+
driver.press(coordinates: coordinates, duration: duration)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'verifies swipe' do
|
149
|
+
driver.boot_simulator
|
150
|
+
duration = 0.5
|
151
|
+
start_coordinates = { x: 1, y: 1 }
|
152
|
+
end_coordinates = { x: 2, y: 2 }
|
153
|
+
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
154
|
+
driver.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
155
|
+
end
|
156
|
+
|
88
157
|
it 'verifies that simulator was not booted' do
|
89
158
|
driver.shutdown_simulator
|
90
159
|
error_message = "Failed to boot #{udid}"
|
160
|
+
allow(driver).to receive(:device_info).and_return({ 'state' => 'Unknown' })
|
91
161
|
expect(Logger).to receive(:log).with(error_message, color: :light_red, payload: nil)
|
92
|
-
expect { driver.
|
162
|
+
expect { driver.boot_simulator }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
93
163
|
end
|
94
164
|
end
|
data/spec/logger_spec.rb
CHANGED
@@ -33,4 +33,24 @@ describe Logger do
|
|
33
33
|
expect { described_class.error(message, payload: payload) }
|
34
34
|
.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
35
35
|
end
|
36
|
+
|
37
|
+
it 'verifies custom log without payload' do
|
38
|
+
color = :blue
|
39
|
+
time = Time.now
|
40
|
+
allow(Time).to receive(:now).and_return(time)
|
41
|
+
expected_output = "#{time.strftime('%k:%M:%S.%L')}: #{message}".colorize(color)
|
42
|
+
expect do
|
43
|
+
described_class.log(message, color: color, payload: nil)
|
44
|
+
end.to output("#{expected_output}\n\n").to_stdout
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'verifies custom log with payload' do
|
48
|
+
color = :blue
|
49
|
+
time = Time.now
|
50
|
+
allow(Time).to receive(:now).and_return(time)
|
51
|
+
expected_output = "#{time.strftime('%k:%M:%S.%L')}: #{message}".colorize(color)
|
52
|
+
expect do
|
53
|
+
described_class.log(message, color: color, payload: payload)
|
54
|
+
end.to output("#{expected_output} #{payload.colorize(:light_green)}\n\n").to_stdout
|
55
|
+
end
|
36
56
|
end
|
data/spec/xcmonkey_spec.rb
CHANGED
@@ -5,7 +5,10 @@ describe Xcmonkey do
|
|
5
5
|
|
6
6
|
it 'verifies gestures' do
|
7
7
|
gestures = described_class.new(params).gestures
|
8
|
-
|
8
|
+
taps = [:precise_tap, :blind_tap] * 10
|
9
|
+
swipes = [:precise_swipe, :blind_swipe] * 5
|
10
|
+
presses = [:precise_press, :blind_press]
|
11
|
+
expect(gestures) =~ presses + taps + swipes
|
9
12
|
end
|
10
13
|
|
11
14
|
it 'verifies required params' do
|
@@ -42,5 +45,14 @@ describe Xcmonkey do
|
|
42
45
|
expect(Logger).to receive(:error).with(duration_error_msg)
|
43
46
|
described_class.new(params)
|
44
47
|
end
|
48
|
+
|
49
|
+
it 'verifies version' do
|
50
|
+
current_version = Gem::Version.new(Xcmonkey::VERSION)
|
51
|
+
expect(current_version).to be > Gem::Version.new('0.1.0')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'verifies gem name' do
|
55
|
+
expect(Xcmonkey::GEM_NAME).to eq('xcmonkey')
|
56
|
+
end
|
45
57
|
end
|
46
58
|
end
|
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: 0.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: 2022-12-
|
11
|
+
date: 2022-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -218,7 +218,7 @@ files:
|
|
218
218
|
- ".github/FUNDING.yml"
|
219
219
|
- ".github/dependabot.yml"
|
220
220
|
- ".github/pull_request_template.md"
|
221
|
-
- ".github/workflows/
|
221
|
+
- ".github/workflows/test.yml"
|
222
222
|
- ".gitignore"
|
223
223
|
- ".rspec"
|
224
224
|
- ".rubocop.yml"
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- LICENSE
|
228
228
|
- README.md
|
229
229
|
- Rakefile
|
230
|
+
- assets/images/xcmonkey.png
|
230
231
|
- bin/console
|
231
232
|
- bin/setup
|
232
233
|
- bin/xcmonkey
|
@@ -236,6 +237,8 @@ files:
|
|
236
237
|
- lib/xcmonkey/driver.rb
|
237
238
|
- lib/xcmonkey/logger.rb
|
238
239
|
- lib/xcmonkey/version.rb
|
240
|
+
- requirements.txt
|
241
|
+
- sonar-project.properties
|
239
242
|
- spec/describer_spec.rb
|
240
243
|
- spec/driver_spec.rb
|
241
244
|
- spec/logger_spec.rb
|
data/.github/workflows/Test.yml
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
name: Test
|
2
|
-
|
3
|
-
on:
|
4
|
-
pull_request:
|
5
|
-
types: [opened, synchronize]
|
6
|
-
|
7
|
-
jobs:
|
8
|
-
chat:
|
9
|
-
name: Checks
|
10
|
-
timeout-minutes: 15
|
11
|
-
runs-on: macos-12
|
12
|
-
steps:
|
13
|
-
- uses: actions/checkout@v3.1.0
|
14
|
-
with:
|
15
|
-
fetch-depth: '0'
|
16
|
-
- uses: actions/cache@v3
|
17
|
-
id: bundler-cache
|
18
|
-
with:
|
19
|
-
path: vendor/bundle
|
20
|
-
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
21
|
-
restore-keys: |
|
22
|
-
${{ runner.os }}-gems-
|
23
|
-
- name: Bundler
|
24
|
-
run: |
|
25
|
-
gem install bundler
|
26
|
-
bundle config path vendor/bundle
|
27
|
-
bundle check || bundle install --jobs 4 --retry 3
|
28
|
-
- name: Rubocop
|
29
|
-
run: bundle exec rake
|