percy-appium-app 0.0.1 → 0.0.3
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/README.md +191 -1
- data/percy/environment.rb +15 -13
- data/percy/exceptions/exceptions.rb +0 -2
- data/percy/lib/app_percy.rb +36 -34
- data/percy/lib/cache.rb +42 -41
- data/percy/lib/cli_wrapper.rb +94 -92
- data/percy/lib/ignore_region.rb +4 -3
- data/percy/lib/percy_automate.rb +44 -42
- data/percy/lib/percy_options.rb +30 -28
- data/percy/lib/region.rb +15 -13
- data/percy/lib/tile.rb +24 -22
- data/percy/metadata/android_metadata.rb +60 -58
- data/percy/metadata/driver_metadata.rb +29 -27
- data/percy/metadata/ios_metadata.rb +64 -62
- data/percy/metadata/metadata.rb +80 -78
- data/percy/metadata/metadata_resolver.rb +14 -12
- data/percy/{screenshot.rb → percy-appium-app.rb} +3 -3
- data/percy/providers/app_automate.rb +131 -129
- data/percy/providers/generic_provider.rb +166 -164
- data/percy/providers/provider_resolver.rb +9 -7
- data/percy/version.rb +1 -1
- data/percy-appium-app.gemspec +1 -0
- data/specs/android_metadata.rb +2 -4
- data/specs/app_automate.rb +10 -13
- data/specs/app_percy.rb +20 -22
- data/specs/cache.rb +16 -16
- data/specs/cli_wrapper.rb +6 -8
- data/specs/driver_metadata.rb +2 -4
- data/specs/generic_providers.rb +9 -12
- data/specs/ignore_regions.rb +10 -12
- data/specs/ios_metadata.rb +2 -2
- data/specs/metadata.rb +2 -5
- data/specs/metadata_resolver.rb +6 -6
- data/specs/mocks/mock_methods.rb +0 -2
- data/specs/percy_options.rb +16 -16
- data/specs/screenshot.rb +7 -10
- data/specs/tile.rb +2 -2
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a9d215b1a1e9daf04d5672a65a909db34bf2f9118e7551a72a83ba742e3dcbf
|
4
|
+
data.tar.gz: d5bd4454dbb19e0f649025ebb21692a847b1e6f14a4bf85f67c16749ff462349
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10f4a8d07bdc08deb1068025032f05cedacd3959244a1fbd5465ce9ed2eac0a502ab629abeb3087849ad62d9edf874dc298b49a8e14c9c2e36b2b4da71d9f1f6
|
7
|
+
data.tar.gz: ef3897862dacf57e5b64b8026a0272415b7ea47a09cdd80e83bc8bcc51d7be02dc4243b4b3e99f268a841934b0131112d37610abef8c5801b21e0e208eb17c8d
|
data/README.md
CHANGED
@@ -1 +1,191 @@
|
|
1
|
-
# percy-appium-ruby
|
1
|
+
# percy-appium-ruby
|
2
|
+
|
3
|
+
[Percy](https://percy.io) visual testing for Ruby Appium.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
npm install `@percy/cli`:
|
8
|
+
|
9
|
+
```sh-session
|
10
|
+
$ npm install --save-dev @percy/cli
|
11
|
+
```
|
12
|
+
|
13
|
+
gem install Percy appium package:
|
14
|
+
|
15
|
+
```ssh-session
|
16
|
+
$ gem install percy-appium-app
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
This is an example test using the `percy_screenshot` function.
|
23
|
+
|
24
|
+
``` ruby
|
25
|
+
require 'appium_lib'
|
26
|
+
require 'percy-appium-app'
|
27
|
+
|
28
|
+
username = '<BROWSERSTACK_USERNAME>'
|
29
|
+
access_key = '<ACCESS_KEY>'
|
30
|
+
|
31
|
+
capabilities = {
|
32
|
+
'platformName' => 'android',
|
33
|
+
'platformVersion' => '13.0',
|
34
|
+
'deviceName' => 'Google Pixel 7',
|
35
|
+
'bstack:options' => {
|
36
|
+
'appiumVersion' => '2.0.1'
|
37
|
+
},
|
38
|
+
'app' => '<APP LINK>',
|
39
|
+
'appium:percyOptions' => {
|
40
|
+
# enabled is default True. This can be used to disable visual testing for certain capabilities
|
41
|
+
'enabled' => true
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
appium_driver = Appium::Driver.new(
|
46
|
+
{
|
47
|
+
'caps' => capabilities,
|
48
|
+
'appium_lib' => {
|
49
|
+
server_url: "https://#{username}:#{access_key}@hub-cloud.browserstack.com/wd/hub"
|
50
|
+
}
|
51
|
+
}, true
|
52
|
+
)
|
53
|
+
driver = appium_driver.start_driver
|
54
|
+
|
55
|
+
# take a screenshot
|
56
|
+
percy_screenshot(driver, 'here is some name')
|
57
|
+
```
|
58
|
+
|
59
|
+
Running the test above normally will result in the following log:
|
60
|
+
|
61
|
+
```sh-session
|
62
|
+
[percy] Percy is not running, disabling screenshots
|
63
|
+
```
|
64
|
+
|
65
|
+
When running with [`percy
|
66
|
+
app:exec`](https://github.com/percy/cli/tree/master/packages/cli-exec#app-exec), and your project's
|
67
|
+
`PERCY_TOKEN`, a new Percy build will be created and screenshots will be uploaded to your project.
|
68
|
+
|
69
|
+
```sh-session
|
70
|
+
$ export PERCY_TOKEN=[your-project-token]
|
71
|
+
$ percy app:exec -- [ruby test command]
|
72
|
+
[percy] Percy has started!
|
73
|
+
[percy] Created build #1: https://percy.io/[your-project]
|
74
|
+
[percy] Screenshot taken "Ruby example"
|
75
|
+
[percy] Stopping percy...
|
76
|
+
[percy] Finalized build #1: https://percy.io/[your-project]
|
77
|
+
[percy] Done!
|
78
|
+
```
|
79
|
+
|
80
|
+
## Configuration
|
81
|
+
|
82
|
+
`percy_screenshot(driver, name[, **kwargs])`
|
83
|
+
|
84
|
+
- `driver` (**required**) - A appium driver instance
|
85
|
+
- `name` (**required**) - The screenshot name; must be unique to each screenshot
|
86
|
+
- `device_name` (**optional**) - The device name used for capturing screenshot
|
87
|
+
- `orientation` (**optional**) - Orientation of device while capturing screeenshot; Allowed values [`portrait` | `landscape`]
|
88
|
+
- `status_bar_height` (**optional**) - Height of status bar; int
|
89
|
+
- `nav_bar_height` (**optional**) - Height of navigation bar; int
|
90
|
+
- `fullpage` (**optional**) - [Alpha] Only supported on App Automate driver sessions [ needs @percy/cli 1.20.2+ ]; boolean
|
91
|
+
- `screen_lengths` (**optional**) - [Alpha] Max screen lengths for fullPage; int
|
92
|
+
- In case scrollview is overlapping with other app elements. Offsets can be provided to reduce the area which needs to be considered for scrolling:
|
93
|
+
- `top_scrollview_offset`: (**optional**) - [Alpha] Offset from top of scrollview; int
|
94
|
+
- `bottom_scrollview_offset` (**optional**) - [Alpha] Offset from bottom of scrollview; int
|
95
|
+
- `full_screen` (**optional**) - Indicate whether app is full screen; boolean [ needs @percy/cli 1.20.2+ ];
|
96
|
+
- `scrollable_xpath` (**optional**) - [Alpha] Scrollable element xpath for fullpage [ needs @percy/cli 1.20.2+ ]; string
|
97
|
+
- `scrollable_id` (**optional**) - [Alpha] Scrollable element accessibility id for fullpage [ needs @percy/cli 1.20.2+ ]; string
|
98
|
+
- `ignore_regions_xpaths` (**optional**) - Elements xpaths that user want to ignore in visual diff [ needs @percy/cli 1.23.0+ ]; list of string
|
99
|
+
- `ignore_region_accessibility_ids` (**optional**) - Elements accessibility_ids that user want to ignore in visual diff [ needs @percy/cli 1.23.0+ ]; list of string
|
100
|
+
- `ignore_region_appium_elements` (**optional**) - Appium elements that user want to ignore in visual diff [ needs @percy/cli 1.23.0+ ]; list of appium element object
|
101
|
+
- `custom_ignore_regions` (**optional**) - Custom locations that user want to ignore in visual diff [ needs @percy/cli 1.23.0+ ]; list of ignore_region object
|
102
|
+
- IgnoreRegion:-
|
103
|
+
- Description: This class represents a rectangular area on a screen that needs to be ignored for visual diff.
|
104
|
+
- Constructor:
|
105
|
+
```
|
106
|
+
init(self, top, bottom, left, right)
|
107
|
+
```
|
108
|
+
- Parameters:
|
109
|
+
- `top` (int): Top coordinate of the ignore region.
|
110
|
+
- `bottom` (int): Bottom coordinate of the ignore region.
|
111
|
+
- `left` (int): Left coordinate of the ignore region.
|
112
|
+
- `right` (int): Right coordinate of the ignore region.
|
113
|
+
|
114
|
+
## Running with Hybrid Apps
|
115
|
+
|
116
|
+
For a hybrid app, we need to switch to native context before taking screenshot.
|
117
|
+
|
118
|
+
- Add a helper method similar to following for say flutter based hybrid app:
|
119
|
+
```ruby
|
120
|
+
def percy_screenshot_flutter(driver, name: str, **kwargs):
|
121
|
+
driver.switch_to.context('NATIVE_APP')
|
122
|
+
percy_screenshot(driver, name, **kwargs)
|
123
|
+
driver.switch_to.context('FLUTTER')
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
- Call PercyScreenshotFlutter helper function when you want to take screenshot.
|
128
|
+
```ruby
|
129
|
+
percy_screenshot_flutter(driver, name, **kwargs)
|
130
|
+
```
|
131
|
+
|
132
|
+
> Note:
|
133
|
+
>
|
134
|
+
> For other hybrid apps the `driver.switch_to.context('FLUTTER')` would change to context that it uses like say WEBVIEW etc.
|
135
|
+
>
|
136
|
+
|
137
|
+
## Running Percy on Automate
|
138
|
+
`percy_screenshot(driver, name, options)` [ needs @percy/cli 1.27.0-beta.0+ ];
|
139
|
+
- `driver` (**required**) - A appium driver instance
|
140
|
+
- `name` (**required**) - The screenshot name; must be unique to each screenshot
|
141
|
+
- `options` (**optional**) - There are various options supported by percy_screenshot to server further functionality.
|
142
|
+
- `freeze_animated_image` - Boolean value by default it falls back to `false`, you can pass `true` and percy will freeze image based animations.
|
143
|
+
- `freeze_image_by_selectors` -List of selectors. Images will be freezed which are passed using selectors. For this to work `freeze_animated_image` must be set to true.
|
144
|
+
- `freeze_image_by_xpaths` - List of xpaths. Images will be freezed which are passed using xpaths. For this to work `freeze_animated_image` must be set to true.
|
145
|
+
- `percy_css` - Custom CSS to be added to DOM before the screenshot being taken. Note: This gets removed once the screenshot is taken.
|
146
|
+
- `ignore_region_xpaths` - List of xpaths. elements in the DOM can be ignored using xpath
|
147
|
+
- `ignore_region_selectors` - List of selectors. elements in the DOM can be ignored using selectors.
|
148
|
+
- `ignore_region_appium_elements` - List of appium web-element. elements can be ignored using appium_elements.
|
149
|
+
- `custom_ignore_regions` - List of custom objects. elements can be ignored using custom boundaries. Just passing a simple object for it like below.
|
150
|
+
- example: ```{"top": 10, "right": 10, "bottom": 120, "left": 10}```
|
151
|
+
- In above example it will draw rectangle of ignore region as per given coordinates.
|
152
|
+
- `top` (int): Top coordinate of the ignore region.
|
153
|
+
- `bottom` (int): Bottom coordinate of the ignore region.
|
154
|
+
- `left` (int): Left coordinate of the ignore region.
|
155
|
+
- `right` (int): Right coordinate of the ignore region.
|
156
|
+
- `consider_region_xpaths` - List of xpaths. elements in the DOM can be considered for diffing and will be ignored by Intelli Ignore using xpaths.
|
157
|
+
- `consider_region_selectors` - List of selectors. elements in the DOM can be considered for diffing and will be ignored by Intelli Ignore using selectors.
|
158
|
+
- `consider_region_appium_elements` - List of appium web-element. elements can be considered for diffing and will be ignored by Intelli Ignore using appium_elements.
|
159
|
+
- `custom_consider_regions` - List of custom objects. elements can be considered for diffing and will be ignored by Intelli Ignore using custom boundaries
|
160
|
+
- example:```{"top": 10, "right": 10, "bottom": 120, "left": 10}```
|
161
|
+
- In above example it will draw rectangle of consider region will be drawn.
|
162
|
+
- Parameters:
|
163
|
+
- `top` (int): Top coordinate of the consider region.
|
164
|
+
- `bottom` (int): Bottom coordinate of the consider region.
|
165
|
+
- `left` (int): Left coordinate of the consider region.
|
166
|
+
- `right` (int): Right coordinate of the consider region.
|
167
|
+
|
168
|
+
### Creating Percy on automate build
|
169
|
+
Note: Automate Percy Token starts with `auto` keyword. The command can be triggered using `exec` keyword.
|
170
|
+
|
171
|
+
```sh-session
|
172
|
+
$ export PERCY_TOKEN=[your-project-token]
|
173
|
+
$ percy exec -- [ruby test command]
|
174
|
+
[percy] Percy has started!
|
175
|
+
[percy] [Ruby example] : Starting automate screenshot ...
|
176
|
+
[percy] Screenshot taken "Ruby example"
|
177
|
+
[percy] Stopping percy...
|
178
|
+
[percy] Finalized build #1: https://percy.io/[your-project]
|
179
|
+
[percy] Done!
|
180
|
+
```
|
181
|
+
|
182
|
+
Refer to docs here: [Percy on Automate](https://docs.percy.io/docs/integrate-functional-testing-with-visual-testing)
|
183
|
+
|
184
|
+
### Migrating Config
|
185
|
+
|
186
|
+
If you have a previous Percy configuration file, migrate it to the newest version with the
|
187
|
+
[`config:migrate`](https://github.com/percy/cli/tree/master/packages/cli-config#percy-configmigrate-filepath-output) command:
|
188
|
+
|
189
|
+
```sh-session
|
190
|
+
$ percy config:migrate
|
191
|
+
```
|
data/percy/environment.rb
CHANGED
@@ -4,22 +4,24 @@ require 'appium_lib'
|
|
4
4
|
require 'appium_lib/version'
|
5
5
|
require_relative 'version'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
module Percy
|
8
|
+
class Percy::Environment
|
9
|
+
@percy_build_id = nil
|
10
|
+
@percy_build_url = nil
|
11
|
+
@session_type = nil
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
class << self
|
14
|
+
attr_accessor :percy_build_id, :percy_build_url, :session_type
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def get_client_info(flag = false)
|
17
|
+
sdk_version = Percy::VERSION
|
18
|
+
flag ? "percy-appium-app-ruby/#{sdk_version}" : "percy-appium-app/#{sdk_version}"
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def get_env_info
|
22
|
+
appium_version = Appium::VERSION # This assumes the 'appium_lib' gem provides a VERSION constant.
|
23
|
+
["appium/#{appium_version}", "ruby/#{RUBY_VERSION}"]
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
data/percy/lib/app_percy.rb
CHANGED
@@ -6,50 +6,52 @@ require_relative 'percy_options'
|
|
6
6
|
require_relative '../providers/provider_resolver'
|
7
7
|
require_relative '../metadata/metadata_resolver'
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
module Percy
|
10
|
+
class AppPercy
|
11
|
+
attr_accessor :metadata, :provider
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
def initialize(driver)
|
14
|
+
raise DriverNotSupported unless driver.is_a?(Appium::Core::Base::Driver)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
@driver = driver
|
17
|
+
@metadata = Percy::MetadataResolver.resolve(@driver)
|
18
|
+
@provider = Percy::ProviderResolver.resolve(@driver)
|
19
|
+
@percy_options = Percy::PercyOptions.new(@metadata.capabilities)
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def screenshot(name, **kwargs)
|
23
|
+
return nil unless @percy_options.enabled
|
23
24
|
|
24
|
-
|
25
|
+
raise TypeError, 'Argument name should be a String' unless name.is_a?(String)
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
device_name = kwargs[:device_name]
|
28
|
+
raise TypeError, 'Argument device_name should be a String' if device_name && !device_name.is_a?(String)
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
fullscreen = kwargs[:full_screen]
|
31
|
+
if fullscreen && !fullscreen.is_a?(TrueClass) && !fullscreen.is_a?(FalseClass)
|
32
|
+
raise TypeError,
|
33
|
+
'Argument fullscreen should be a Boolean'
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
status_bar_height = kwargs[:status_bar_height]
|
37
|
+
if status_bar_height && !status_bar_height.is_a?(Integer)
|
38
|
+
raise TypeError,
|
39
|
+
'Argument status_bar_height should be an Integer'
|
40
|
+
end
|
41
|
+
|
42
|
+
nav_bar_height = kwargs[:nav_bar_height]
|
43
|
+
raise TypeError, 'Argument nav_bar_height should be an Integer' if nav_bar_height && !nav_bar_height.is_a?(Integer)
|
40
44
|
|
41
|
-
|
42
|
-
|
45
|
+
orientation = kwargs[:orientation]
|
46
|
+
if orientation && !orientation.is_a?(String)
|
47
|
+
raise TypeError,
|
48
|
+
'Argument orientation should be a String and portrait/landscape'
|
49
|
+
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
raise TypeError,
|
47
|
-
'Argument orientation should be a String and portrait/landscape'
|
51
|
+
@provider.screenshot(name, **kwargs)
|
52
|
+
nil
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
nil
|
55
|
+
attr_reader :percy_options
|
52
56
|
end
|
53
|
-
|
54
|
-
attr_reader :percy_options
|
55
57
|
end
|
data/percy/lib/cache.rb
CHANGED
@@ -1,53 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
module Percy
|
3
|
+
class Cache
|
4
|
+
attr_reader :cache
|
5
|
+
|
6
|
+
@@cache = {}
|
7
|
+
CACHE_TIMEOUT = 5 * 60 # 5 * 60 seconds
|
8
|
+
TIMEOUT_KEY = 'last_access_time'
|
9
|
+
|
10
|
+
SESSION_DETAILS = 'session_details'
|
11
|
+
SYSTEM_BARS = 'system_bars'
|
12
|
+
WINDOW_SIZE = 'window_size'
|
13
|
+
VIEWPORT = 'viewport'
|
14
|
+
SESSION_CAPABILITIES = 'session_capabilities'
|
15
|
+
CAPABILITIES = 'capabilities'
|
16
|
+
COMMAND_EXECUTOR_URL = 'command_executor_url'
|
17
|
+
|
18
|
+
def cache
|
19
|
+
@@cache
|
20
|
+
end
|
2
21
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@@cache = {}
|
7
|
-
CACHE_TIMEOUT = 5 * 60 # 5 * 60 seconds
|
8
|
-
TIMEOUT_KEY = 'last_access_time'
|
9
|
-
|
10
|
-
SESSION_DETAILS = 'session_details'
|
11
|
-
SYSTEM_BARS = 'system_bars'
|
12
|
-
WINDOW_SIZE = 'window_size'
|
13
|
-
VIEWPORT = 'viewport'
|
14
|
-
SESSION_CAPABILITIES = 'session_capabilities'
|
15
|
-
CAPABILITIES = 'capabilities'
|
16
|
-
COMMAND_EXECUTOR_URL = 'command_executor_url'
|
17
|
-
|
18
|
-
def cache
|
19
|
-
@@cache
|
20
|
-
end
|
22
|
+
def self.set_cache(session_id, property, value)
|
23
|
+
raise TypeError, 'Argument session_id should be a String' unless session_id.is_a?(String)
|
24
|
+
raise TypeError, 'Argument property should be a String' unless property.is_a?(String)
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
session = @@cache.fetch(session_id, {})
|
27
|
+
session[TIMEOUT_KEY] = Time.now.to_i
|
28
|
+
session[property] = value
|
29
|
+
@@cache[session_id] = session
|
30
|
+
end
|
25
31
|
|
26
|
-
|
27
|
-
|
28
|
-
session[property] = value
|
29
|
-
@@cache[session_id] = session
|
30
|
-
end
|
32
|
+
def self.get_cache(session_id, property)
|
33
|
+
cleanup_cache
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
raise TypeError, 'Argument session_id should be a String' unless session_id.is_a?(String)
|
36
|
+
raise TypeError, 'Argument property should be a String' unless property.is_a?(String)
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
session = @@cache.fetch(session_id, {})
|
39
|
+
session.fetch(property, nil)
|
40
|
+
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
def self.cleanup_cache
|
43
|
+
now = Time.now.to_i
|
44
|
+
session_ids = []
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
@@cache.each do |session_id, session|
|
47
|
+
timestamp = session[TIMEOUT_KEY]
|
48
|
+
session_ids << session_id if now - timestamp >= CACHE_TIMEOUT
|
49
|
+
end
|
45
50
|
|
46
|
-
|
47
|
-
timestamp = session[TIMEOUT_KEY]
|
48
|
-
session_ids << session_id if now - timestamp >= CACHE_TIMEOUT
|
51
|
+
session_ids.each { |session_id| @@cache.delete(session_id) }
|
49
52
|
end
|
50
|
-
|
51
|
-
session_ids.each { |session_id| @@cache.delete(session_id) }
|
52
53
|
end
|
53
54
|
end
|
data/percy/lib/cli_wrapper.rb
CHANGED
@@ -9,124 +9,126 @@ require_relative '../exceptions/exceptions'
|
|
9
9
|
require_relative '../version'
|
10
10
|
require_relative '../environment'
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
module Percy
|
13
|
+
CLIENT_INFO = "percy-appium-app/#{VERSION}"
|
14
|
+
ENV_INFO = ["appium/#{Appium::VERSION}", "ruby/#{RUBY_VERSION}"].freeze
|
14
15
|
|
15
|
-
PERCY_CLI_API = ENV['PERCY_CLI_API'] || 'http://localhost:5338'
|
16
|
+
PERCY_CLI_API = ENV['PERCY_CLI_API'] || 'http://localhost:5338'
|
16
17
|
|
17
|
-
class CLIWrapper
|
18
|
-
|
18
|
+
class CLIWrapper
|
19
|
+
def initialize; end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def self.percy_enabled?
|
22
|
+
@percy_enabled ||= begin
|
23
|
+
uri = URI("#{PERCY_CLI_API}/percy/healthcheck")
|
24
|
+
response = Net::HTTP.get_response(uri)
|
24
25
|
|
25
|
-
|
26
|
+
raise CLIException, response.body unless response.is_a?(Net::HTTPSuccess)
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
data = JSON.parse(response.body)
|
29
|
+
raise CLIException, data['error'] unless data['success']
|
30
|
+
|
31
|
+
Percy::Environment.percy_build_id = data['build']['id']
|
32
|
+
Percy::Environment.percy_build_url = data['build']['url']
|
33
|
+
Percy::Environment.session_type = data.fetch('type', nil)
|
29
34
|
|
30
|
-
|
31
|
-
|
32
|
-
|
35
|
+
version = response['x-percy-core-version']
|
36
|
+
if version.split('.')[0] != '1'
|
37
|
+
log("Unsupported Percy CLI version, #{version}")
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
return true unless version.split('.')[1].to_i < 27
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
|
42
|
+
log('Please upgrade to the latest CLI version for using this SDK. Minimum compatible version is 1.27.0-beta.0')
|
43
|
+
return false
|
44
|
+
rescue StandardError => e
|
45
|
+
log('Percy is not running, disabling screenshots')
|
46
|
+
log(e, on_debug: true)
|
37
47
|
return false
|
38
48
|
end
|
39
|
-
return true unless version.split('.')[1].to_i < 27
|
40
|
-
|
41
|
-
log('Please upgrade to the latest CLI version for using this SDK. Minimum compatible version is 1.27.0-beta.0')
|
42
|
-
return false
|
43
|
-
rescue StandardError => e
|
44
|
-
log('Percy is not running, disabling screenshots')
|
45
|
-
log(e, on_debug: true)
|
46
|
-
return false
|
47
49
|
end
|
48
|
-
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
def post_screenshots(name, tag, tiles, external_debug_url = nil, ignored_elements_data = nil,
|
52
|
+
considered_elements_data = nil)
|
53
|
+
body = request_body(name, tag, tiles, external_debug_url, ignored_elements_data, considered_elements_data)
|
54
|
+
body['client_info'] = Percy::Environment.get_client_info
|
55
|
+
body['environment_info'] = Percy::Environment.get_env_info
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
uri = URI("#{PERCY_CLI_API}/percy/comparison")
|
58
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
59
|
+
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
60
|
+
request.body = body.to_json
|
60
61
|
|
61
|
-
|
62
|
-
|
62
|
+
response = http.request(request)
|
63
|
+
data = JSON.parse(response.body)
|
63
64
|
|
64
|
-
|
65
|
+
raise CLIException, data.fetch('error', 'UnknownException') if response.code != '200'
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def self.post_failed_event(error)
|
70
|
-
body = {
|
71
|
-
'clientInfo' => Environment.get_client_info(true),
|
72
|
-
'message' => error,
|
73
|
-
'errorKind' => 'sdk'
|
74
|
-
}
|
67
|
+
data
|
68
|
+
end
|
75
69
|
|
76
|
-
|
77
|
-
|
70
|
+
def self.post_failed_event(error)
|
71
|
+
body = {
|
72
|
+
'clientInfo' => Percy::Environment.get_client_info(true),
|
73
|
+
'message' => error,
|
74
|
+
'errorKind' => 'sdk'
|
75
|
+
}
|
76
|
+
|
77
|
+
uri = URI("#{PERCY_CLI_API}/percy/events")
|
78
|
+
response = Net::HTTP.post(uri, body.to_json, 'Content-Type' => 'application/json')
|
79
|
+
|
80
|
+
# Handle errors
|
81
|
+
if response.code.to_i != 200
|
82
|
+
data = JSON.parse(response.body)
|
83
|
+
error_message = data.fetch('error', 'UnknownException')
|
84
|
+
raise CLIException, error_message
|
85
|
+
end
|
78
86
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
raise CLIException, error_message
|
87
|
+
JSON.parse(response.body)
|
88
|
+
rescue StandardError => e
|
89
|
+
log(e.message, on_debug: true)
|
90
|
+
nil
|
84
91
|
end
|
85
92
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
93
|
+
def post_poa_screenshots(name, session_id, command_executor_url, capabilities, desired_capabilities, options = nil)
|
94
|
+
body = {
|
95
|
+
'sessionId' => session_id,
|
96
|
+
'commandExecutorUrl' => command_executor_url,
|
97
|
+
'capabilities' => capabilities.dup, # In Ruby, you can duplicate the hash with `dup`
|
98
|
+
'sessionCapabilities' => desired_capabilities.dup,
|
99
|
+
'snapshotName' => name,
|
100
|
+
'options' => options
|
101
|
+
}
|
91
102
|
|
92
|
-
|
93
|
-
|
94
|
-
'sessionId' => session_id,
|
95
|
-
'commandExecutorUrl' => command_executor_url,
|
96
|
-
'capabilities' => capabilities.dup, # In Ruby, you can duplicate the hash with `dup`
|
97
|
-
'sessionCapabilities' => desired_capabilities.dup,
|
98
|
-
'snapshotName' => name,
|
99
|
-
'options' => options
|
100
|
-
}
|
103
|
+
body['client_info'] = Percy::Environment.get_client_info # Using class method without the underscore
|
104
|
+
body['environment_info'] = Percy::Environment.get_env_info
|
101
105
|
|
102
|
-
|
103
|
-
|
106
|
+
uri = URI("#{PERCY_CLI_API}/percy/automateScreenshot")
|
107
|
+
response = Net::HTTP.post(uri, body.to_json, 'Content-Type' => 'application/json')
|
104
108
|
|
105
|
-
|
106
|
-
|
109
|
+
# Handle errors
|
110
|
+
raise CLIException, "Error: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
|
107
111
|
|
108
|
-
|
109
|
-
raise CLIException, "Error: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
|
112
|
+
data = JSON.parse(response.body)
|
110
113
|
|
111
|
-
|
114
|
+
if response.code != '200'
|
115
|
+
error_message = data.fetch('error', 'UnknownException')
|
116
|
+
raise CLIException, error_message
|
117
|
+
end
|
112
118
|
|
113
|
-
|
114
|
-
error_message = data.fetch('error', 'UnknownException')
|
115
|
-
raise CLIException, error_message
|
119
|
+
data
|
116
120
|
end
|
117
121
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
'considered_elements_data' => considered_elements_data
|
130
|
-
}
|
122
|
+
def request_body(name, tag, tiles, external_debug_url, ignored_elements_data, considered_elements_data)
|
123
|
+
tiles = tiles.map(&:to_h)
|
124
|
+
{
|
125
|
+
'name' => name,
|
126
|
+
'tag' => tag,
|
127
|
+
'tiles' => tiles,
|
128
|
+
'ignored_elements_data' => ignored_elements_data,
|
129
|
+
'external_debug_url' => external_debug_url,
|
130
|
+
'considered_elements_data' => considered_elements_data
|
131
|
+
}
|
132
|
+
end
|
131
133
|
end
|
132
134
|
end
|