percy-appium-app 0.0.1
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 +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +55 -0
- data/.github/dependabot.yml +14 -0
- data/.github/release-drafter.yml +28 -0
- data/.github/workflows/Semgrep.yml +48 -0
- data/.github/workflows/changelog.yml +11 -0
- data/.github/workflows/release.yml +22 -0
- data/.github/workflows/stale.yml +30 -0
- data/.github/workflows/test.yml +40 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/Makefile +3 -0
- data/README.md +1 -0
- data/Rakefile +1 -0
- data/percy/common/common.rb +19 -0
- data/percy/configs/devices.json +232 -0
- data/percy/environment.rb +25 -0
- data/percy/exceptions/exceptions.rb +13 -0
- data/percy/lib/app_percy.rb +55 -0
- data/percy/lib/cache.rb +53 -0
- data/percy/lib/cli_wrapper.rb +132 -0
- data/percy/lib/ignore_region.rb +8 -0
- data/percy/lib/percy_automate.rb +59 -0
- data/percy/lib/percy_options.rb +37 -0
- data/percy/lib/region.rb +22 -0
- data/percy/lib/tile.rb +28 -0
- data/percy/metadata/android_metadata.rb +79 -0
- data/percy/metadata/driver_metadata.rb +40 -0
- data/percy/metadata/ios_metadata.rb +83 -0
- data/percy/metadata/metadata.rb +108 -0
- data/percy/metadata/metadata_resolver.rb +21 -0
- data/percy/providers/app_automate.rb +159 -0
- data/percy/providers/generic_provider.rb +205 -0
- data/percy/providers/provider_resolver.rb +17 -0
- data/percy/screenshot.rb +23 -0
- data/percy/version.rb +5 -0
- data/percy-appium-app.gemspec +37 -0
- data/specs/android_metadata.rb +79 -0
- data/specs/app_automate.rb +124 -0
- data/specs/app_percy.rb +175 -0
- data/specs/cache.rb +56 -0
- data/specs/cli_wrapper.rb +135 -0
- data/specs/driver_metadata.rb +71 -0
- data/specs/generic_providers.rb +370 -0
- data/specs/ignore_regions.rb +51 -0
- data/specs/ios_metadata.rb +88 -0
- data/specs/metadata.rb +105 -0
- data/specs/metadata_resolver.rb +41 -0
- data/specs/mocks/mock_methods.rb +147 -0
- data/specs/percy_options.rb +114 -0
- data/specs/screenshot.rb +342 -0
- data/specs/tile.rb +33 -0
- metadata +194 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative '../common/common'
|
5
|
+
require_relative '../lib/tile'
|
6
|
+
require_relative 'generic_provider'
|
7
|
+
require_relative '../environment'
|
8
|
+
|
9
|
+
class AppAutomate < GenericProvider
|
10
|
+
def self.supports(remote_url)
|
11
|
+
r_index = remote_url.rindex(ENV['AA_DOMAIN'].nil? ? 'browserstack' : ENV['AA_DOMAIN'])
|
12
|
+
if r_index
|
13
|
+
r_index > -1
|
14
|
+
else
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def screenshot(name, **kwargs)
|
20
|
+
session_details = execute_percy_screenshot_begin(name)
|
21
|
+
|
22
|
+
if session_details
|
23
|
+
metadata.device_name = session_details['deviceName']
|
24
|
+
metadata.os_version = session_details['osVersion']
|
25
|
+
set_debug_url(session_details)
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
response = super(name, **kwargs)
|
30
|
+
percy_screenshot_url = response.fetch('link', '')
|
31
|
+
execute_percy_screenshot_end(name, percy_screenshot_url, 'success')
|
32
|
+
rescue StandardError => e
|
33
|
+
execute_percy_screenshot_end(name, percy_screenshot_url, 'failure', e.message)
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_debug_url(session_details)
|
39
|
+
build_hash = session_details['buildHash'].to_s
|
40
|
+
session_hash = session_details['sessionHash'].to_s
|
41
|
+
@debug_url = "https://app-automate.browserstack.com/dashboard/v2/builds/#{build_hash}/sessions/#{session_hash}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def _get_tiles(**kwargs)
|
45
|
+
fullpage_ss = kwargs[:fullpage] || false
|
46
|
+
if ENV['PERCY_DISABLE_REMOTE_UPLOADS'] == 'true'
|
47
|
+
puts("Full page screenshots are only supported when 'PERCY_DISABLE_REMOTE_UPLOADS' is not set") if fullpage_ss
|
48
|
+
return super(**kwargs) unless fullpage_ss
|
49
|
+
end
|
50
|
+
screenshot_type = fullpage_ss ? 'fullpage' : 'singlepage'
|
51
|
+
screen_lengths = kwargs[:screen_lengths] || 4
|
52
|
+
scrollable_xpath = kwargs[:scollable_xpath]
|
53
|
+
scrollable_id = kwargs[:scrollable_id]
|
54
|
+
top_scrollview_offset = kwargs[:top_scrollview_offset]
|
55
|
+
bottom_scrollview_offset = kwargs[:top_scrollview_offset]
|
56
|
+
|
57
|
+
data = execute_percy_screenshot(
|
58
|
+
metadata.device_screen_size.fetch('height', 1),
|
59
|
+
screenshot_type,
|
60
|
+
screen_lengths,
|
61
|
+
scrollable_xpath,
|
62
|
+
scrollable_id,
|
63
|
+
metadata.scale_factor,
|
64
|
+
top_scrollview_offset,
|
65
|
+
bottom_scrollview_offset
|
66
|
+
)
|
67
|
+
tiles = []
|
68
|
+
status_bar_height = metadata.status_bar_height
|
69
|
+
nav_bar_height = metadata.navigation_bar_height
|
70
|
+
|
71
|
+
JSON.parse(data['result']).each do |tile_data|
|
72
|
+
tiles << Tile.new(
|
73
|
+
status_bar_height,
|
74
|
+
nav_bar_height,
|
75
|
+
tile_data['header_height'],
|
76
|
+
tile_data['footer_height'],
|
77
|
+
sha: tile_data['sha'].split('-')[0]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
tiles
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute_percy_screenshot_begin(name)
|
85
|
+
request_body = {
|
86
|
+
action: 'percyScreenshot',
|
87
|
+
arguments: {
|
88
|
+
state: 'begin',
|
89
|
+
percyBuildId: Environment.percy_build_id,
|
90
|
+
percyBuildUrl: Environment.percy_build_url,
|
91
|
+
name: name
|
92
|
+
}
|
93
|
+
}
|
94
|
+
command = "browserstack_executor: #{request_body.to_json}"
|
95
|
+
begin
|
96
|
+
response = metadata.execute_script(command)
|
97
|
+
JSON.parse(response)
|
98
|
+
rescue StandardError => e
|
99
|
+
log('Could not set session as Percy session')
|
100
|
+
log('Error occurred during begin call', on_debug: true)
|
101
|
+
log(e, on_debug: true)
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def execute_percy_screenshot_end(name, percy_screenshot_url, status, status_message = nil)
|
107
|
+
request_body = {
|
108
|
+
action: 'percyScreenshot',
|
109
|
+
arguments: {
|
110
|
+
state: 'end',
|
111
|
+
percyScreenshotUrl: percy_screenshot_url,
|
112
|
+
name: name,
|
113
|
+
status: status
|
114
|
+
}
|
115
|
+
}
|
116
|
+
request_body[:arguments][:statusMessage] = status_message if status_message
|
117
|
+
command = "browserstack_executor: #{request_body.to_json}"
|
118
|
+
begin
|
119
|
+
metadata.execute_script(command)
|
120
|
+
rescue StandardError => e
|
121
|
+
log('Error occurred during end call', on_debug: true)
|
122
|
+
log(e, on_debug: true)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def execute_percy_screenshot(device_height, screenshotType, screen_lengths, scrollable_xpath = nil,
|
127
|
+
scrollable_id = nil, scale_factor = 1, top_scrollview_offset = 0,
|
128
|
+
bottom_scrollview_offset = 0)
|
129
|
+
project_id = ENV['PERCY_ENABLE_DEV'] == 'true' ? 'percy-dev' : 'percy-prod'
|
130
|
+
request_body = {
|
131
|
+
action: 'percyScreenshot',
|
132
|
+
arguments: {
|
133
|
+
state: 'screenshot',
|
134
|
+
percyBuildId: Environment.percy_build_id,
|
135
|
+
screenshotType: screenshotType,
|
136
|
+
projectId: project_id,
|
137
|
+
scaleFactor: scale_factor,
|
138
|
+
options: {
|
139
|
+
numOfTiles: screen_lengths,
|
140
|
+
deviceHeight: device_height,
|
141
|
+
scrollableXpath: scrollable_xpath,
|
142
|
+
scrollableId: scrollable_id,
|
143
|
+
topScrollviewOffset: top_scrollview_offset,
|
144
|
+
bottomScrollviewOffset: bottom_scrollview_offset,
|
145
|
+
'FORCE_FULL_PAGE' => ENV['FORCE_FULL_PAGE'] == 'true'
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
command = "browserstack_executor: #{request_body.to_json}"
|
150
|
+
begin
|
151
|
+
response = metadata.execute_script(command)
|
152
|
+
JSON.parse(response)
|
153
|
+
rescue StandardError => e
|
154
|
+
log('Error occurred during screenshot call', on_debug: true)
|
155
|
+
log(e, on_debug: true)
|
156
|
+
raise e
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'pathname'
|
6
|
+
require 'base64'
|
7
|
+
require 'fileutils'
|
8
|
+
require_relative '../lib/cli_wrapper'
|
9
|
+
require_relative '../lib/tile'
|
10
|
+
require_relative '../common/common'
|
11
|
+
|
12
|
+
class GenericProvider
|
13
|
+
attr_accessor :driver, :metadata, :debug_url
|
14
|
+
|
15
|
+
def initialize(driver, metadata)
|
16
|
+
@driver = driver
|
17
|
+
@metadata = metadata
|
18
|
+
@debug_url = ''
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.supports(_remote_url)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def screenshot(name, **kwargs)
|
26
|
+
tiles = _get_tiles(**kwargs)
|
27
|
+
tag = _get_tag(**kwargs)
|
28
|
+
ignore_regions = {
|
29
|
+
'ignoreElementsData' => _find_regions(
|
30
|
+
xpaths: kwargs.fetch(:ignore_regions_xpaths, []),
|
31
|
+
accessibility_ids: kwargs.fetch(:ignore_region_accessibility_ids, []),
|
32
|
+
appium_elements: kwargs.fetch(:ignore_region_appium_elements, []),
|
33
|
+
custom_locations: kwargs.fetch(:custom_ignore_regions, [])
|
34
|
+
)
|
35
|
+
}
|
36
|
+
consider_regions = {
|
37
|
+
'considerElementsData' => _find_regions(
|
38
|
+
xpaths: kwargs.fetch(:consider_regions_xpaths, []),
|
39
|
+
accessibility_ids: kwargs.fetch(:consider_region_accessibility_ids, []),
|
40
|
+
appium_elements: kwargs.fetch(:consider_region_appium_elements, []),
|
41
|
+
custom_locations: kwargs.fetch(:custom_consider_regions, [])
|
42
|
+
)
|
43
|
+
}
|
44
|
+
|
45
|
+
_post_screenshots(name, tag, tiles, get_debug_url, ignore_regions, consider_regions)
|
46
|
+
end
|
47
|
+
|
48
|
+
def _get_tag(**kwargs)
|
49
|
+
name = kwargs[:device_name] || metadata.device_name
|
50
|
+
os_name = metadata.os_name
|
51
|
+
os_version = metadata.os_version
|
52
|
+
width = metadata.device_screen_size['width'] || 1
|
53
|
+
height = metadata.device_screen_size['height'] || 1
|
54
|
+
orientation = metadata.get_orientation(**kwargs).downcase
|
55
|
+
|
56
|
+
{
|
57
|
+
'name' => name,
|
58
|
+
'os_name' => os_name,
|
59
|
+
'os_version' => os_version,
|
60
|
+
'width' => width,
|
61
|
+
'height' => height,
|
62
|
+
'orientation' => orientation
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def _get_tiles(**kwargs)
|
67
|
+
fullpage_ss = kwargs[:fullpage] || false
|
68
|
+
if fullpage_ss
|
69
|
+
log('Full page screenshot is only supported on App Automate. Falling back to single page screenshot.')
|
70
|
+
end
|
71
|
+
|
72
|
+
png_bytes = driver.screenshot_as(:png)
|
73
|
+
directory = _get_dir
|
74
|
+
path = _write_screenshot(png_bytes, directory)
|
75
|
+
|
76
|
+
fullscreen = kwargs[:full_screen] || false
|
77
|
+
status_bar_height = kwargs[:status_bar_height] || metadata.status_bar_height
|
78
|
+
nav_bar_height = kwargs[:nav_bar_height] || metadata.navigation_bar_height
|
79
|
+
header_height = 0
|
80
|
+
footer_height = 0
|
81
|
+
[
|
82
|
+
Tile.new(status_bar_height, nav_bar_height, header_height, footer_height, filepath: path, fullscreen: fullscreen)
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
def _find_regions(xpaths:, accessibility_ids:, appium_elements:, custom_locations:)
|
87
|
+
elements_array = []
|
88
|
+
get_regions_by_xpath(elements_array, xpaths)
|
89
|
+
get_regions_by_ids(elements_array, accessibility_ids)
|
90
|
+
get_regions_by_elements(elements_array, appium_elements)
|
91
|
+
get_regions_by_location(elements_array, custom_locations)
|
92
|
+
elements_array
|
93
|
+
end
|
94
|
+
|
95
|
+
def _post_screenshots(name, tag, tiles, debug_url, ignored_regions, considered_regions)
|
96
|
+
CLIWrapper.new.post_screenshots(name, tag, tiles, debug_url, ignored_regions, considered_regions)
|
97
|
+
end
|
98
|
+
|
99
|
+
def _write_screenshot(png_bytes, directory)
|
100
|
+
filepath = _get_path(directory)
|
101
|
+
File.open(filepath, 'wb') { |f| f.write(png_bytes) }
|
102
|
+
filepath
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_region_object(selector, element)
|
106
|
+
scale_factor = metadata.scale_factor
|
107
|
+
location = hashed(element.location)
|
108
|
+
size = hashed(element.size)
|
109
|
+
coordinates = {
|
110
|
+
'top' => location['y'] * scale_factor,
|
111
|
+
'bottom' => (location['y'] + size['height']) * scale_factor,
|
112
|
+
'left' => location['x'] * scale_factor,
|
113
|
+
'right' => (location['x'] + size['width']) * scale_factor
|
114
|
+
}
|
115
|
+
{ 'selector' => selector, 'coOrdinates' => coordinates }
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_regions_by_xpath(elements_array, xpaths)
|
119
|
+
xpaths.each do |xpath|
|
120
|
+
element = driver.find_element(Appium::Core::Base::SearchContext::FINDERS[:xpath], xpath)
|
121
|
+
selector = "xpath: #{xpath}"
|
122
|
+
if element
|
123
|
+
region = get_region_object(selector, element)
|
124
|
+
elements_array << region
|
125
|
+
end
|
126
|
+
rescue Appium::Core::Error::NoSuchElementError => e
|
127
|
+
log("Appium Element with xpath: #{xpath} not found. Ignoring this xpath.")
|
128
|
+
log(e, on_debug: true)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_regions_by_ids(elements_array, ids)
|
133
|
+
ids.each do |id|
|
134
|
+
element = driver.find_element(Appium::Core::Base::SearchContext::FINDERS[:accessibility_id], id)
|
135
|
+
selector = "id: #{id}"
|
136
|
+
region = get_region_object(selector, element)
|
137
|
+
elements_array << region
|
138
|
+
rescue Appium::Core::Error::NoSuchElementError => e
|
139
|
+
log("Appium Element with id: #{id} not found. Ignoring this id.")
|
140
|
+
log(e, on_debug: true)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def get_regions_by_elements(elements_array, elements)
|
145
|
+
elements.each_with_index do |element, index|
|
146
|
+
class_name = element.attribute('class')
|
147
|
+
selector = "element: #{index} #{class_name}"
|
148
|
+
region = get_region_object(selector, element)
|
149
|
+
elements_array << region
|
150
|
+
rescue Appium::Core::Error::NoSuchElementError => e
|
151
|
+
log("Correct Element not passed at index #{index}")
|
152
|
+
log(e, on_debug: true)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_regions_by_location(elements_array, custom_locations)
|
157
|
+
custom_locations.each_with_index do |custom_location, index|
|
158
|
+
screen_width = metadata.device_screen_size['width']
|
159
|
+
screen_height = metadata.device_screen_size['height']
|
160
|
+
if custom_location.valid?(screen_height, screen_width)
|
161
|
+
region = {
|
162
|
+
selector: "custom ignore region: #{index}",
|
163
|
+
coOrdinates: {
|
164
|
+
top: custom_location.top,
|
165
|
+
bottom: custom_location.bottom,
|
166
|
+
left: custom_location.left,
|
167
|
+
right: custom_location.right
|
168
|
+
}
|
169
|
+
}
|
170
|
+
elements_array << region
|
171
|
+
else
|
172
|
+
log("Values passed in custom ignored region at index: #{index} are not valid")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def log(message, on_debug: false)
|
178
|
+
puts message if on_debug
|
179
|
+
end
|
180
|
+
|
181
|
+
def get_debug_url
|
182
|
+
debug_url
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_device_name
|
186
|
+
''
|
187
|
+
end
|
188
|
+
|
189
|
+
def _get_dir
|
190
|
+
dir_path = ENV['PERCY_TMP_DIR'] || nil
|
191
|
+
if dir_path
|
192
|
+
Pathname.new(dir_path).mkpath
|
193
|
+
return dir_path
|
194
|
+
end
|
195
|
+
Dir.mktmpdir
|
196
|
+
end
|
197
|
+
|
198
|
+
def _get_path(directory)
|
199
|
+
suffix = '.png'
|
200
|
+
prefix = 'percy-appium-'
|
201
|
+
file = Tempfile.new([prefix, suffix], directory)
|
202
|
+
file.close
|
203
|
+
file.path
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../exceptions/exceptions'
|
4
|
+
require_relative '../metadata/metadata_resolver'
|
5
|
+
require_relative 'app_automate'
|
6
|
+
require_relative 'generic_provider'
|
7
|
+
|
8
|
+
class ProviderResolver
|
9
|
+
def self.resolve(driver)
|
10
|
+
metadata = MetadataResolver.resolve(driver)
|
11
|
+
providers = [AppAutomate, GenericProvider]
|
12
|
+
providers.each do |provider|
|
13
|
+
return provider.new(driver, metadata) if provider.supports(metadata.remote_url)
|
14
|
+
end
|
15
|
+
raise UnknownProvider
|
16
|
+
end
|
17
|
+
end
|
data/percy/screenshot.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'common/common'
|
4
|
+
require_relative 'lib/app_percy'
|
5
|
+
require_relative 'lib/percy_automate'
|
6
|
+
require_relative 'lib/cli_wrapper'
|
7
|
+
require_relative 'environment'
|
8
|
+
|
9
|
+
def percy_screenshot(driver, name, **kwargs)
|
10
|
+
return nil unless CLIWrapper.percy_enabled?
|
11
|
+
|
12
|
+
app_percy = nil
|
13
|
+
provider_class = Environment.session_type == 'automate' ? PercyOnAutomate : AppPercy
|
14
|
+
app_percy = provider_class.new(driver)
|
15
|
+
app_percy.screenshot(name, **kwargs)
|
16
|
+
rescue StandardError => e
|
17
|
+
CLIWrapper.post_failed_event(e.to_s)
|
18
|
+
log("Could not take screenshot \"#{name}\"")
|
19
|
+
raise e if app_percy && !app_percy.percy_options.ignore_errors
|
20
|
+
|
21
|
+
log(e, on_debug: true)
|
22
|
+
nil
|
23
|
+
end
|
data/percy/version.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
percy = File.expand_path('../percy', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(percy) unless $LOAD_PATH.include?(percy)
|
4
|
+
require 'version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'percy-appium-app'
|
8
|
+
spec.version = Percy::VERSION
|
9
|
+
spec.authors = ['BroswerStack']
|
10
|
+
spec.email = ['support@browserstack.com']
|
11
|
+
spec.summary = %q{Percy visual testing for Ruby Appium Mobile Apps}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = '>= 2.6.0'
|
16
|
+
|
17
|
+
|
18
|
+
spec.metadata = {
|
19
|
+
'bug_tracker_uri' => 'https://github.com/percy/percy-appium-ruby/issues',
|
20
|
+
'source_code_uri' => 'https://github.com/percy/percy-appium-ruby',
|
21
|
+
}
|
22
|
+
|
23
|
+
spec.files = `git ls-files -z`.split("\x0")
|
24
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
+
spec.require_paths = ['percy']
|
27
|
+
|
28
|
+
spec.add_runtime_dependency 'appium_lib', '~> 12.0'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '~> 2.4'
|
31
|
+
spec.add_development_dependency 'minitest', '~> 5.20'
|
32
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
33
|
+
spec.add_development_dependency 'percy-style', '~> 0.7.0'
|
34
|
+
spec.add_development_dependency 'webmock', '~> 3.18'
|
35
|
+
spec.add_development_dependency 'webrick', '~> 1.3'
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/MethodLength
|
4
|
+
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/mock'
|
7
|
+
require 'appium_lib'
|
8
|
+
require_relative 'mocks/mock_methods'
|
9
|
+
require_relative '../percy/metadata/android_metadata'
|
10
|
+
|
11
|
+
# Test suite for the AndroidMetadata class
|
12
|
+
class TestAndroidMetadata < Minitest::Test
|
13
|
+
def setup
|
14
|
+
@mock_webdriver = Minitest::Mock.new
|
15
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
16
|
+
@android_metadata = AndroidMetadata.new(@mock_webdriver)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_android_execute_script
|
20
|
+
command = 'some dummy command'
|
21
|
+
output = 'some output'
|
22
|
+
@mock_webdriver.expect(:execute_script, output, [command])
|
23
|
+
|
24
|
+
assert_equal(output, @android_metadata.execute_script(command))
|
25
|
+
@mock_webdriver.verify
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_viewport
|
29
|
+
viewport = { 'left' => 0, 'top' => 84, 'width' => 1440, 'height' => 2708 }
|
30
|
+
android_capabilities = get_android_capabilities
|
31
|
+
@mock_webdriver.expect(:capabilities, android_capabilities.merge('viewportRect' => viewport))
|
32
|
+
|
33
|
+
assert(viewport, @android_metadata.viewport)
|
34
|
+
|
35
|
+
@mock_webdriver.verify
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_get_system_bars
|
39
|
+
system_bars = {
|
40
|
+
'statusBar' => { 'height' => 83 },
|
41
|
+
'navigationBar' => { 'height' => 44 }
|
42
|
+
}
|
43
|
+
android_capabilities = get_android_capabilities
|
44
|
+
session_id = 'session_id_123'
|
45
|
+
@mock_webdriver.expect(:session_id, session_id)
|
46
|
+
@mock_webdriver.expect(:session_id, session_id)
|
47
|
+
@mock_webdriver.expect(:capabilities, android_capabilities.merge('viewportRect' => nil))
|
48
|
+
@mock_webdriver.expect(:get_system_bars, system_bars)
|
49
|
+
|
50
|
+
assert(system_bars, @android_metadata.get_system_bars)
|
51
|
+
@mock_webdriver.verify
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_status_bar
|
55
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
56
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
57
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
58
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
59
|
+
mock_get_system_bars = { 'statusBar' => { 'height' => 1 } }
|
60
|
+
@android_metadata.stub(:get_system_bars, mock_get_system_bars) do
|
61
|
+
assert_equal(0, @android_metadata.status_bar_height)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_navigation_bar
|
66
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
67
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
68
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
69
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
70
|
+
mock_get_system_bars = { 'navigationBar' => { 'height' => 1 } }
|
71
|
+
@android_metadata.stub(:get_system_bars, mock_get_system_bars) do
|
72
|
+
assert_equal(0, @android_metadata.navigation_bar_height)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_scale_factor
|
77
|
+
assert_equal(1, @android_metadata.scale_factor)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/MethodLength
|
4
|
+
# rubocop:disable Metrics/AbcSize
|
5
|
+
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'minitest/mock'
|
8
|
+
require_relative '../percy/providers/app_automate'
|
9
|
+
require_relative '../percy/metadata/android_metadata'
|
10
|
+
require_relative 'mocks/mock_methods'
|
11
|
+
|
12
|
+
# Test suite for the AppAutomate class
|
13
|
+
class TestAppAutomate < Minitest::Test
|
14
|
+
COMPARISON_RESPONSE = { 'success' => true, 'link' => 'https://snapshots-url' }.freeze
|
15
|
+
|
16
|
+
def setup
|
17
|
+
@mock_webdriver = Minitest::Mock.new
|
18
|
+
@mock_webdriver.expect(:capabilities, get_android_capabilities)
|
19
|
+
@metadata = AndroidMetadata.new(@mock_webdriver)
|
20
|
+
@app_automate = AppAutomate.new(@mock_webdriver, @metadata)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_app_automate_get_debug_url
|
24
|
+
@app_automate.set_debug_url('deviceName' => 'Google Pixel 4', 'osVersion' => '12.0', 'buildHash' => 'abc',
|
25
|
+
'sessionHash' => 'def')
|
26
|
+
debug_url = @app_automate.get_debug_url
|
27
|
+
assert_equal 'https://app-automate.browserstack.com/dashboard/v2/builds/abc/sessions/def', debug_url
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_app_automate_supports_with_correct_url
|
31
|
+
app_automate_session = AppAutomate.supports('https://hub-cloud.browserstack.com/wd/hub')
|
32
|
+
assert_equal true, app_automate_session
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_app_automate_supports_with_incorrect_url
|
36
|
+
app_automate_session = AppAutomate.supports('https://hub-cloud.generic.com/wd/hub')
|
37
|
+
assert_equal false, app_automate_session
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_app_automate_supports_with_AA_DOMAIN
|
41
|
+
ENV['AA_DOMAIN'] = 'bsstag'
|
42
|
+
app_automate_session = AppAutomate.supports('bsstag.com')
|
43
|
+
assert_equal true, app_automate_session
|
44
|
+
ENV['AA_DOMAIN'] = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_app_automate_execute_percy_screenshot_begin
|
48
|
+
@mock_webdriver.expect(:execute_script, '{}', [String])
|
49
|
+
assert_empty @app_automate.execute_percy_screenshot_begin('Screebshot 1')
|
50
|
+
@mock_webdriver.verify
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_app_automate_execute_percy_screenshot_end
|
54
|
+
@mock_webdriver.expect(:execute_script, '{}', [String])
|
55
|
+
assert_equal '{}',
|
56
|
+
@app_automate.execute_percy_screenshot_end('Screenshot 1', COMPARISON_RESPONSE['link'], 'success')
|
57
|
+
@mock_webdriver.verify
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_app_automate_execute_percy_screenshot
|
61
|
+
@mock_webdriver.expect(:execute_script, '{"result": "result"}', [String])
|
62
|
+
@app_automate.execute_percy_screenshot(1080, 'singlepage', 5)
|
63
|
+
@mock_webdriver.verify
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_execute_percy_screenshot_end_throws_error
|
67
|
+
@mock_webdriver.expect(:execute_script, proc { raise 'SomeException' }, [String])
|
68
|
+
@app_automate.execute_percy_screenshot_end('Screenshot 1', 'snapshot-url', 'success')
|
69
|
+
@mock_webdriver.verify
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_execute_percy_screenshot_end
|
73
|
+
@app_automate.stub(:execute_percy_screenshot_begin, 'deviceName' => 'abc', 'osVersion' => '123') do
|
74
|
+
@app_automate.stub(:execute_percy_screenshot_end, nil) do
|
75
|
+
@app_automate.stub(:screenshot, 'link' => 'https://link') do
|
76
|
+
@app_automate.screenshot('name')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_get_tiles
|
83
|
+
# Mocking Metadata's session_id method
|
84
|
+
metadata_mock = Minitest::Mock.new
|
85
|
+
metadata_mock.expect(:session_id, 'session_id_123')
|
86
|
+
|
87
|
+
# Mocking AndroidMetadata's methods
|
88
|
+
android_metadata_mock = Minitest::Mock.new
|
89
|
+
android_metadata_mock.expect(:device_screen_size, { 'width' => 1080, 'height' => 1920 })
|
90
|
+
android_metadata_mock.expect(:navigation_bar_height, 150)
|
91
|
+
android_metadata_mock.expect(:status_bar_height, 100)
|
92
|
+
|
93
|
+
Metadata.class_eval do
|
94
|
+
define_method(:session_id) do
|
95
|
+
metadata_mock.session_id
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
AndroidMetadata.class_eval do
|
100
|
+
define_method(:device_screen_size) do
|
101
|
+
android_metadata_mock.device_screen_size
|
102
|
+
end
|
103
|
+
|
104
|
+
define_method(:navigation_bar_height) do
|
105
|
+
android_metadata_mock.navigation_bar_height
|
106
|
+
end
|
107
|
+
|
108
|
+
define_method(:status_bar_height) do
|
109
|
+
android_metadata_mock.status_bar_height
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@app_automate.stub(:execute_percy_screenshot, {
|
114
|
+
'result' => '[{"sha":"sha-25568755","status_bar":null,"nav_bar":null,"header_height":120,"footer_height":80,"index":0}]'
|
115
|
+
}) do
|
116
|
+
result = @app_automate._get_tiles(fullpage: true)[0]
|
117
|
+
assert_equal('sha', result.sha)
|
118
|
+
assert_equal(100, result.status_bar_height)
|
119
|
+
assert_equal(150, result.nav_bar_height)
|
120
|
+
assert_equal(120, result.header_height)
|
121
|
+
assert_equal(80, result.footer_height)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|