eyes_selenium 3.14.10 → 3.15.0.beta
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/lib/applitools/selenium/concerns/browser_types.rb +15 -0
- data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
- data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
- data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
- data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
- data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
- data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
- data/lib/applitools/selenium/concerns/test_list.rb +23 -0
- data/lib/applitools/selenium/eyes.rb +13 -854
- data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
- data/lib/applitools/selenium/selenium_configuration.rb +64 -0
- data/lib/applitools/selenium/selenium_eyes.rb +860 -0
- data/lib/applitools/selenium/target.rb +2 -2
- data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
- data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
- data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
- data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
- data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
- data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
- data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
- data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
- data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
- data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
- data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
- data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
- data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
- data/lib/applitools/version.rb +1 -1
- data/lib/eyes_selenium.rb +3 -0
- metadata +46 -7
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'applitools/selenium/concerns/stitch_modes'
|
2
|
+
require 'applitools/selenium/concerns/stitch_modes'
|
3
|
+
require 'applitools/selenium/concerns/browsers_info'
|
4
|
+
|
5
|
+
module Applitools
|
6
|
+
module Selenium
|
7
|
+
class SeleniumConfiguration < Applitools::EyesBaseConfiguration
|
8
|
+
DEFAULT_CONFIG = proc do
|
9
|
+
{
|
10
|
+
force_full_page_screenshot: false,
|
11
|
+
wait_before_screenshots: 100,
|
12
|
+
stitch_mode: Applitools::Selenium::Concerns::StitchModes::CSS,
|
13
|
+
hide_scrollbars: false,
|
14
|
+
hide_caret: false,
|
15
|
+
browsers_info: Applitools::Selenium::Concerns::BrowsersInfo.new
|
16
|
+
}
|
17
|
+
end
|
18
|
+
class << self
|
19
|
+
def default_config
|
20
|
+
super.merge DEFAULT_CONFIG.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
boolean_field :force_full_page_screenshot
|
25
|
+
int_field :wait_before_screenshots
|
26
|
+
enum_field :stitch_mode, Applitools::Selenium::Concerns::StitchModes.enum_values
|
27
|
+
boolean_field :hide_scrollbars
|
28
|
+
boolean_field :hide_caret
|
29
|
+
boolean_field :send_dom
|
30
|
+
|
31
|
+
object_field :browsers_info, Applitools::Selenium::Concerns::BrowsersInfo
|
32
|
+
|
33
|
+
int_field :concurrent_sessions
|
34
|
+
|
35
|
+
# private int waitBeforeScreenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS;
|
36
|
+
# private StitchMode stitchMode = StitchMode.SCROLL;
|
37
|
+
# private boolean hideScrollbars = true;
|
38
|
+
# private boolean hideCaret = true;
|
39
|
+
#
|
40
|
+
# //Rendering Configuration
|
41
|
+
# private int concurrentSessions = 3;
|
42
|
+
# private boolean isThrowExceptionOn = false;
|
43
|
+
# private Boolean isRenderingConfig = false;
|
44
|
+
#
|
45
|
+
# public enum BrowserType {CHROME, FIREFOX}
|
46
|
+
# private List<RenderBrowserInfo> browsersInfo =
|
47
|
+
def add_browser(b = nil)
|
48
|
+
browser = b || Applitools::Selenium::RenderBrowserInfo.new
|
49
|
+
yield(Applitools::Selenium::Concerns::RenderBrowserInfoFluent.new(browser)) if block_given?
|
50
|
+
browsers_info.add browser
|
51
|
+
self.viewport_size = browser.viewport_size unless viewport_size
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def deep_clone
|
56
|
+
new_config = self.class.new
|
57
|
+
config_keys.each do |k|
|
58
|
+
new_config.send("#{k}=", self.send(k).clone)
|
59
|
+
end
|
60
|
+
new_config
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,860 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module Applitools::Selenium
|
4
|
+
# The main API gateway for the SDK
|
5
|
+
class SeleniumEyes < Applitools::EyesBase
|
6
|
+
# @!visibility private
|
7
|
+
UNKNOWN_DEVICE_PIXEL_RATIO = 0
|
8
|
+
|
9
|
+
# The pixel ratio will be used if detection of device pixel ratio is failed
|
10
|
+
DEFAULT_DEVICE_PIXEL_RATIO = 1
|
11
|
+
|
12
|
+
DEFAULT_WAIT_BEFORE_SCREENSHOTS = 0.1 # Seconds
|
13
|
+
|
14
|
+
USE_DEFAULT_MATCH_TIMEOUT = -1
|
15
|
+
|
16
|
+
DEFAULT_STITCHING_OVERLAP = 50 # Pixels
|
17
|
+
|
18
|
+
ENTIRE_ELEMENT_SCREENSHOT = 0
|
19
|
+
|
20
|
+
FULLPAGE_SCREENSHOT = 1
|
21
|
+
|
22
|
+
VIEWPORT_SCREENSHOT = 2
|
23
|
+
|
24
|
+
extend Forwardable
|
25
|
+
# @!visibility public
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def eyes_driver(driver, eyes = nil)
|
29
|
+
if driver.respond_to? :driver_for_eyes
|
30
|
+
driver.driver_for_eyes eyes
|
31
|
+
else
|
32
|
+
unless driver.is_a?(Applitools::Selenium::Driver)
|
33
|
+
Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
|
34
|
+
is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
|
35
|
+
Applitools::Selenium::Driver.new(eyes, driver: driver, is_mobile_device: is_mobile_device)
|
36
|
+
end
|
37
|
+
raise Applitools::EyesError.new "Unknown driver #{driver}!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def obtain_screenshot_type(is_element, inside_a_frame, stitch_content, force_fullpage)
|
42
|
+
if stitch_content || force_fullpage
|
43
|
+
unless inside_a_frame
|
44
|
+
return FULLPAGE_SCREENSHOT if force_fullpage && !stitch_content
|
45
|
+
return FULLPAGE_SCREENSHOT if stitch_content && !is_element
|
46
|
+
end
|
47
|
+
return ENTIRE_ELEMENT_SCREENSHOT if inside_a_frame
|
48
|
+
return ENTIRE_ELEMENT_SCREENSHOT if stitch_content
|
49
|
+
else
|
50
|
+
return VIEWPORT_SCREENSHOT unless stitch_content || force_fullpage
|
51
|
+
end
|
52
|
+
VIEWPORT_SCREENSHOT
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set the viewport size.
|
56
|
+
#
|
57
|
+
# @param [Applitools::Selenium::Driver] driver The driver instance.
|
58
|
+
# @param [Hash] viewport_size The required browser's viewport size.
|
59
|
+
def set_viewport_size(driver, viewport_size)
|
60
|
+
Applitools::ArgumentGuard.not_nil(driver, 'Driver')
|
61
|
+
Applitools::ArgumentGuard.not_nil(viewport_size, 'viewport_size')
|
62
|
+
Applitools::ArgumentGuard.is_a?(viewport_size, 'viewport_size', Applitools::RectangleSize)
|
63
|
+
begin
|
64
|
+
Applitools::Utils::EyesSeleniumUtils.set_viewport_size eyes_driver(driver), viewport_size
|
65
|
+
rescue => e
|
66
|
+
Applitools::EyesLogger.error e.class
|
67
|
+
Applitools::EyesLogger.error e.message
|
68
|
+
raise Applitools::EyesError.new 'Failed to set viewport size!'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!attribute [rw] force_full_page_screenshot
|
74
|
+
# Forces a full page screenshot (by scrolling and stitching) if the
|
75
|
+
# browser only supports viewport screenshots.
|
76
|
+
# @return [boolean] force full page screenshot flag
|
77
|
+
# @!attribute [rw] wait_before_screenshots
|
78
|
+
# Sets the time to wait just before taking a screenshot (e.g., to allow
|
79
|
+
# positioning to stabilize when performing a full page stitching).
|
80
|
+
# @return [Float] The time to wait (Seconds). Values
|
81
|
+
# smaller or equal to 0, will cause the default value to be used.
|
82
|
+
# @!attribute [rw] hide_scrollbars
|
83
|
+
# Turns on/off hiding scrollbars before taking a screenshot
|
84
|
+
# @return [boolean] hide_scrollbars flag
|
85
|
+
# @!attribute [rw] scroll_to_region
|
86
|
+
# If set to +true+ browser will scroll to specified region (even it is out of viewport window)
|
87
|
+
# when check_region is called
|
88
|
+
# @return [boolean] scroll_to_region flag
|
89
|
+
# @!attribute [rw] stitch_mode
|
90
|
+
# May be set to :CSS or :SCROLL (:SCROLL is default).
|
91
|
+
# When :CSS - SDK will use CSS transitions to perform scrolling, otherwise it will use Javascript
|
92
|
+
# window.scroll_to() function for scrolling purposes
|
93
|
+
# @return [boolean] stitch_mode (:CSS or :SCROLL)
|
94
|
+
# @!attribute [Applitools::RectangleSize] explicit_entire_size
|
95
|
+
# May be set to an Applitools::RectangleSize instance or +nil+ (default).
|
96
|
+
# @return [Applitools::RectangleSize] explicit_entire_size
|
97
|
+
|
98
|
+
attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
|
99
|
+
:wait_before_screenshots, :debug_screenshots, :stitch_mode, :disable_horizontal_scrolling,
|
100
|
+
:disable_vertical_scrolling, :explicit_entire_size, :debug_screenshot_provider, :stitching_overlap,
|
101
|
+
:full_page_capture_algorithm_left_top_offset, :screenshot_type, :send_dom, :use_dom, :enable_patterns
|
102
|
+
attr_reader :driver
|
103
|
+
|
104
|
+
def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
|
105
|
+
|
106
|
+
# Creates a new (possibly disabled) Eyes instance that interacts with the
|
107
|
+
# Eyes Server at the specified url.
|
108
|
+
#
|
109
|
+
# @param server_url The Eyes Server URL.
|
110
|
+
def initialize(server_url = nil)
|
111
|
+
super
|
112
|
+
self.base_agent_id = "eyes.selenium.ruby/#{Applitools::VERSION}".freeze
|
113
|
+
self.check_frame_or_element = false
|
114
|
+
self.region_to_check = nil
|
115
|
+
self.force_full_page_screenshot = false
|
116
|
+
self.dont_get_title = false
|
117
|
+
self.hide_scrollbars = false
|
118
|
+
self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
|
119
|
+
self.stitch_mode = Applitools::STITCH_MODE[:scroll]
|
120
|
+
self.wait_before_screenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS
|
121
|
+
self.region_visibility_strategy = MoveToRegionVisibilityStrategy.new
|
122
|
+
self.debug_screenshots = false
|
123
|
+
self.debug_screenshot_provider = Applitools::DebugScreenshotProvider.new
|
124
|
+
.tag_access { tag_for_debug }
|
125
|
+
.debug_flag_access { debug_screenshots }
|
126
|
+
self.disable_horizontal_scrolling = false
|
127
|
+
self.disable_vertical_scrolling = false
|
128
|
+
self.explicit_entire_size = nil
|
129
|
+
self.force_driver_resolution_as_viewport_size = false
|
130
|
+
self.stitching_overlap = DEFAULT_STITCHING_OVERLAP
|
131
|
+
self.full_page_capture_algorithm_left_top_offset = Applitools::Location::TOP_LEFT
|
132
|
+
self.send_dom = false
|
133
|
+
self.use_dom = false
|
134
|
+
self.enable_patterns = false
|
135
|
+
self.prevent_dom_processing = false
|
136
|
+
end
|
137
|
+
|
138
|
+
# Starts a test
|
139
|
+
#
|
140
|
+
# @param options [Hash] options
|
141
|
+
# @option options :driver The driver that controls the browser hosting the application
|
142
|
+
# under the test. (*Required* option)
|
143
|
+
# @option options [String] :app_name The name of the application under the test. (*Required* option)
|
144
|
+
# @option options [String] :test_name The test name (*Required* option)
|
145
|
+
# @option options [String | Hash] :viewport_size The required browser's viewport size
|
146
|
+
# (i.e., the visible part of the document's body) or +nil+ to use the current window's viewport.
|
147
|
+
# @option options :session_type The type of the test (e.g., standard test / visual performance test).
|
148
|
+
# Default value is 'SEQUENTAL'
|
149
|
+
# @return [Applitools::Selenium::Driver] A wrapped web driver which enables Eyes
|
150
|
+
# trigger recording and frame handling
|
151
|
+
def open(options = {})
|
152
|
+
original_driver = options.delete(:driver)
|
153
|
+
options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size] if
|
154
|
+
options[:viewport_size]
|
155
|
+
Applitools::ArgumentGuard.not_nil original_driver, 'options[:driver]'
|
156
|
+
Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]
|
157
|
+
|
158
|
+
if disabled?
|
159
|
+
logger.info('Ignored')
|
160
|
+
return driver
|
161
|
+
end
|
162
|
+
|
163
|
+
@driver = self.class.eyes_driver(original_driver, self)
|
164
|
+
perform_driver_specific_settings(original_driver)
|
165
|
+
|
166
|
+
self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
|
167
|
+
self.position_provider = self.class.position_provider(
|
168
|
+
stitch_mode, driver, disable_horizontal_scrolling, disable_vertical_scrolling, explicit_entire_size
|
169
|
+
)
|
170
|
+
|
171
|
+
self.eyes_screenshot_factory = lambda do |image|
|
172
|
+
Applitools::Selenium::ViewportScreenshot.new(
|
173
|
+
image, driver: @driver, force_offset: position_provider.force_offset
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
open_base(options) do
|
178
|
+
self.viewport_size = nil if force_driver_resolution_as_viewport_size
|
179
|
+
ensure_running_session
|
180
|
+
end
|
181
|
+
@driver
|
182
|
+
end
|
183
|
+
|
184
|
+
def perform_driver_specific_settings(original_driver)
|
185
|
+
modifier = original_driver.class.to_s.downcase.gsub(/::/, '_')
|
186
|
+
method_name = "perform_driver_settings_for_#{modifier}"
|
187
|
+
send(method_name) if respond_to?(method_name, :include_private)
|
188
|
+
end
|
189
|
+
|
190
|
+
private :perform_driver_specific_settings
|
191
|
+
|
192
|
+
# Sets the stitch mode.
|
193
|
+
#
|
194
|
+
# @param [Hash] value The desired type of stitching (:SCROLL is default).
|
195
|
+
# @option value [Symbol] :css use Css to perform stitching.
|
196
|
+
# @option value [Symbol] :scroll Scroll to perform stitching.
|
197
|
+
# @return [Symbol] The type of stitching.
|
198
|
+
def stitch_mode=(value)
|
199
|
+
@stitch_mode = if value.to_s.upcase == Applitools::STITCH_MODE[:css].to_s
|
200
|
+
Applitools::STITCH_MODE[:css]
|
201
|
+
else
|
202
|
+
Applitools::STITCH_MODE[:scroll]
|
203
|
+
end
|
204
|
+
unless driver.nil?
|
205
|
+
self.position_provider = self.class.position_provider(
|
206
|
+
stitch_mode, driver, disable_horizontal_scrolling, disable_vertical_scrolling, explicit_entire_size
|
207
|
+
)
|
208
|
+
end
|
209
|
+
if stitch_mode == Applitools::STITCH_MODE[:css]
|
210
|
+
@css_transition_original_hide_scrollbars = hide_scrollbars
|
211
|
+
self.hide_scrollbars = true
|
212
|
+
else
|
213
|
+
self.hide_scrollbars = @css_transition_original_hide_scrollbars || false
|
214
|
+
end
|
215
|
+
value
|
216
|
+
end
|
217
|
+
|
218
|
+
# Takes a snapshot of the application under test and matches it with the expected output.
|
219
|
+
#
|
220
|
+
# @param [String] tag An optional tag to be assosiated with the snapshot.
|
221
|
+
# @param [Fixnum] match_timeout The amount of time to retry matching (seconds)
|
222
|
+
def check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT)
|
223
|
+
target = Applitools::Selenium::Target.window.tap do |t|
|
224
|
+
t.timeout(match_timeout)
|
225
|
+
t.fully if force_full_page_screenshot
|
226
|
+
end
|
227
|
+
check(tag, target)
|
228
|
+
end
|
229
|
+
|
230
|
+
# @!visibility private
|
231
|
+
def title
|
232
|
+
return driver.title unless dont_get_title
|
233
|
+
rescue StandardError => e
|
234
|
+
logger.warn "failed (#{e.message})"
|
235
|
+
self.dont_get_title = false
|
236
|
+
''
|
237
|
+
end
|
238
|
+
|
239
|
+
# @!visibility private
|
240
|
+
def get_viewport_size(web_driver = driver)
|
241
|
+
Applitools::ArgumentGuard.not_nil 'web_driver', web_driver
|
242
|
+
Applitools::Utils::EyesSeleniumUtils.extract_viewport_size(driver)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Takes a snapshot and matches it with the expected output.
|
246
|
+
#
|
247
|
+
# @param [String] name The name of the tag.
|
248
|
+
# @param [Applitools::Selenium::Target] target which area of the window to check.
|
249
|
+
# @return [Applitools::MatchResult] The match results.
|
250
|
+
def check(name, target)
|
251
|
+
logger.info "check(#{name}) is called"
|
252
|
+
self.tag_for_debug = name
|
253
|
+
Applitools::ArgumentGuard.is_a? target, 'target', Applitools::Selenium::Target
|
254
|
+
target_to_check = target.finalize
|
255
|
+
original_overflow = nil
|
256
|
+
original_position_provider = position_provider
|
257
|
+
original_force_full_page_screenshot = force_full_page_screenshot
|
258
|
+
|
259
|
+
eyes_element = nil
|
260
|
+
timeout = target_to_check.options[:timeout] || USE_DEFAULT_MATCH_TIMEOUT
|
261
|
+
|
262
|
+
self.eyes_screenshot_factory = lambda do |image|
|
263
|
+
Applitools::Selenium::ViewportScreenshot.new(
|
264
|
+
image,
|
265
|
+
region_provider: region_to_check
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
# self.prevent_dom_processing = !((!target.options[:send_dom].nil? && target.options[:send_dom]) ||
|
270
|
+
# send_dom || stitch_mode == Applitools::STITCH_MODE[:css])
|
271
|
+
|
272
|
+
self.prevent_dom_processing = !((!target.options[:send_dom].nil? && target.options[:send_dom]) ||
|
273
|
+
send_dom)
|
274
|
+
|
275
|
+
check_in_frame target_frames: target_to_check.frames do
|
276
|
+
begin
|
277
|
+
match_data = Applitools::MatchWindowData.new
|
278
|
+
match_data.tag = name
|
279
|
+
update_default_settings(match_data)
|
280
|
+
eyes_element = target_to_check.region_to_check.call(driver)
|
281
|
+
|
282
|
+
unless force_full_page_screenshot
|
283
|
+
region_visibility_strategy.move_to_region original_position_provider,
|
284
|
+
Applitools::Location.new(eyes_element.location.x.to_i, eyes_element.location.y.to_i)
|
285
|
+
driver.find_element(:css, 'html').scroll_data_attribute = true
|
286
|
+
end
|
287
|
+
|
288
|
+
region_provider = Applitools::Selenium::RegionProvider.new(driver, region_for_element(eyes_element))
|
289
|
+
|
290
|
+
self.region_to_check = region_provider
|
291
|
+
|
292
|
+
match_data.read_target(target_to_check, driver)
|
293
|
+
match_data.use_dom = use_dom unless match_data.use_dom
|
294
|
+
match_data.enable_patterns = enable_patterns unless match_data.enable_patterns
|
295
|
+
|
296
|
+
is_element = eyes_element.is_a? Applitools::Selenium::Element
|
297
|
+
inside_a_frame = !driver.frame_chain.empty?
|
298
|
+
|
299
|
+
self.screenshot_type = self.class.obtain_screenshot_type(
|
300
|
+
is_element,
|
301
|
+
inside_a_frame,
|
302
|
+
target_to_check.options[:stitch_content],
|
303
|
+
force_full_page_screenshot
|
304
|
+
)
|
305
|
+
|
306
|
+
case screenshot_type
|
307
|
+
when ENTIRE_ELEMENT_SCREENSHOT
|
308
|
+
if eyes_element.is_a? Applitools::Selenium::Element
|
309
|
+
original_overflow = eyes_element.overflow
|
310
|
+
eyes_element.overflow = 'hidden'
|
311
|
+
eyes_element.scroll_data_attribute = true
|
312
|
+
eyes_element.overflow_data_attribute = original_overflow
|
313
|
+
self.position_provider = Applitools::Selenium::CssTranslateElementPositionProvider.new(
|
314
|
+
driver,
|
315
|
+
eyes_element
|
316
|
+
)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
check_window_base(
|
321
|
+
region_provider, timeout, match_data
|
322
|
+
)
|
323
|
+
ensure
|
324
|
+
eyes_element.overflow = original_overflow unless original_overflow.nil?
|
325
|
+
self.check_frame_or_element = false
|
326
|
+
self.force_full_page_screenshot = original_force_full_page_screenshot
|
327
|
+
self.position_provider = original_position_provider
|
328
|
+
self.region_to_check = nil
|
329
|
+
self.full_page_capture_algorithm_left_top_offset = Applitools::Location::TOP_LEFT
|
330
|
+
region_visibility_strategy.return_to_original_position original_position_provider
|
331
|
+
end
|
332
|
+
# rubocop:enable BlockLength
|
333
|
+
end
|
334
|
+
self.prevent_dom_processing = false
|
335
|
+
end
|
336
|
+
|
337
|
+
# Validates the contents of an iframe and matches it with the expected output.
|
338
|
+
#
|
339
|
+
# @param [Hash] options The specific parameters of the desired screenshot.
|
340
|
+
# @option options [Array] :target_frames The frames to check.
|
341
|
+
def check_in_frame(options)
|
342
|
+
frames = options.delete :target_frames
|
343
|
+
|
344
|
+
Applitools::ArgumentGuard.is_a? options, 'options', Hash
|
345
|
+
Applitools::ArgumentGuard.is_a? frames, 'target_frames: []', Array
|
346
|
+
|
347
|
+
return yield if block_given? && frames.empty?
|
348
|
+
|
349
|
+
original_frame_chain = driver.frame_chain
|
350
|
+
|
351
|
+
logger.info 'Switching to target frame according to frames path...'
|
352
|
+
driver.switch_to.frames(frames_path: frames)
|
353
|
+
frame_chain_to_reset = driver.frame_chain
|
354
|
+
logger.info 'Done!'
|
355
|
+
|
356
|
+
ensure_frame_visible
|
357
|
+
|
358
|
+
yield if block_given?
|
359
|
+
|
360
|
+
reset_frames_scroll_position(frame_chain_to_reset)
|
361
|
+
|
362
|
+
logger.info 'Switching back into top level frame...'
|
363
|
+
driver.switch_to.default_content
|
364
|
+
return unless original_frame_chain
|
365
|
+
logger.info 'Switching back into original frame...'
|
366
|
+
driver.switch_to.frames frame_chain: original_frame_chain
|
367
|
+
end
|
368
|
+
|
369
|
+
# Creates a region instance.
|
370
|
+
#
|
371
|
+
# @param [Applitools::Element] element The element.
|
372
|
+
# @return [Applitools::Region] The relevant region.
|
373
|
+
def region_for_element(element)
|
374
|
+
return element if element.is_a? Applitools::Region
|
375
|
+
|
376
|
+
p = element.location
|
377
|
+
d = element.size
|
378
|
+
|
379
|
+
border_left_width = element.border_left_width
|
380
|
+
border_top_width = element.border_top_width
|
381
|
+
border_right_width = element.border_right_width
|
382
|
+
border_bottom_width = element.border_bottom_width
|
383
|
+
|
384
|
+
Applitools::Region.new(
|
385
|
+
p.x.round + border_left_width,
|
386
|
+
p.y.round + border_top_width,
|
387
|
+
d.width - border_left_width - border_right_width,
|
388
|
+
d.height - border_top_width - border_bottom_width
|
389
|
+
).tap do |r|
|
390
|
+
border_padding = Applitools::PaddingBounds.new(
|
391
|
+
border_left_width,
|
392
|
+
border_top_width,
|
393
|
+
border_right_width,
|
394
|
+
border_bottom_width
|
395
|
+
)
|
396
|
+
r.padding(border_padding)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
private :check_in_frame
|
401
|
+
private :region_for_element
|
402
|
+
|
403
|
+
# Takes a snapshot of the application under test and matches a region of
|
404
|
+
# a specific element with the expected region output.
|
405
|
+
#
|
406
|
+
# @param [Applitools::Selenium::Element] element Represents a region to check.
|
407
|
+
# @param [Symbol] how a finder, such :css or :id. Selects a finder will be used to find an element
|
408
|
+
# See Selenium::Webdriver::Element#find_element documentation for full list of possible finders.
|
409
|
+
# @param [String] what The value will be passed to a specified finder. If finder is :css it must be a css selector.
|
410
|
+
# @param [Hash] options
|
411
|
+
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
412
|
+
# @option options [Fixnum] :match_timeout The amount of time to retry matching. (Seconds)
|
413
|
+
# @option options [Boolean] :stitch_content If set to true, will try to get full content of the element
|
414
|
+
# (including hidden content due overflow settings) by scrolling the element,
|
415
|
+
# taking and stitching partial screenshots.
|
416
|
+
# @example Check region by element
|
417
|
+
# check_region(element, tag: 'Check a region by element', match_timeout: 3, stitch_content: false)
|
418
|
+
# @example Check region by css selector
|
419
|
+
# check_region(:css, '.form-row .input#e_mail', tag: 'Check a region by element', match_timeout: 3,
|
420
|
+
# stitch_content: false)
|
421
|
+
# @!parse def check_region(element, how=nil, what=nil, options = {}); end
|
422
|
+
def check_region(*args)
|
423
|
+
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge! Applitools::Utils.extract_options!(args)
|
424
|
+
target = Applitools::Selenium::Target.new.region(*args).timeout(options[:match_timeout])
|
425
|
+
target.fully if options[:stitch_content]
|
426
|
+
check(options[:tag], target)
|
427
|
+
end
|
428
|
+
|
429
|
+
# Validates the contents of an iframe and matches it with the expected output.
|
430
|
+
#
|
431
|
+
# @param [Hash] options The specific parameters of the desired screenshot.
|
432
|
+
# @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
|
433
|
+
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
434
|
+
# @option options [String] :frame Frame element or frame name or frame id.
|
435
|
+
# @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
|
436
|
+
# @option options [String] :frame_element The frame element (deprecated. use :frame instead).
|
437
|
+
# @return [Applitools::MatchResult] The match results.
|
438
|
+
|
439
|
+
def check_frame(options = {})
|
440
|
+
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)
|
441
|
+
frame = options[:frame] || options[:frame_element] || options[:name_or_id]
|
442
|
+
target = Applitools::Selenium::Target.frame(frame).timeout(options[:timeout]).fully
|
443
|
+
check(options[:tag], target)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Validates the contents of a region in an iframe and matches it with the expected output.
|
447
|
+
#
|
448
|
+
# @param [Hash] options The specific parameters of the desired screenshot.
|
449
|
+
# @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
|
450
|
+
# @option options [String] :frame_element The frame element (deprecated. use :frame instead).
|
451
|
+
# @option options [String] :frame Frame element or frame name or frame id.
|
452
|
+
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
453
|
+
# @option options [Symbol] :by By which identifier to find the region (e.g :css, :id).
|
454
|
+
# @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
|
455
|
+
# @option options [Boolean] :stitch_content Whether to stitch the content or not.
|
456
|
+
# @return [Applitools::MatchResult] The match results.
|
457
|
+
def check_region_in_frame(options = {})
|
458
|
+
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil, stitch_content: false }.merge!(options)
|
459
|
+
Applitools::ArgumentGuard.not_nil options[:by], 'options[:by]'
|
460
|
+
Applitools::ArgumentGuard.is_a? options[:by], 'options[:by]', Array
|
461
|
+
|
462
|
+
how_what = options.delete(:by)
|
463
|
+
frame = options[:frame] || options[:frame_element] || options[:name_or_id]
|
464
|
+
|
465
|
+
target = Applitools::Selenium::Target.new.timeout(options[:timeout])
|
466
|
+
target.frame(frame) if frame
|
467
|
+
target.fully if options[:stitch_content]
|
468
|
+
target.region(*how_what)
|
469
|
+
|
470
|
+
check(options[:tag], target)
|
471
|
+
end
|
472
|
+
|
473
|
+
# Use this method to perform seamless testing with selenium through eyes driver.
|
474
|
+
# It yields a block and passes to it an Applitools::Selenium::Driver instance, which wraps standard driver.
|
475
|
+
# Using Selenium methods inside the 'test' block will send the messages to Selenium
|
476
|
+
# after creating the Eyes triggers for them. Options are similar to {open}
|
477
|
+
# @yieldparam driver [Applitools::Selenium::Driver] Gives a driver to a block, which translates calls to a native
|
478
|
+
# Selemium::Driver instance
|
479
|
+
# @example
|
480
|
+
# eyes.test(app_name: 'my app', test_name: 'my test') do |driver|
|
481
|
+
# driver.get "http://www.google.com"
|
482
|
+
# driver.check_window("initial")
|
483
|
+
# end
|
484
|
+
def test(options = {}, &_block)
|
485
|
+
open(options)
|
486
|
+
yield(driver)
|
487
|
+
close
|
488
|
+
ensure
|
489
|
+
abort_if_not_closed
|
490
|
+
end
|
491
|
+
|
492
|
+
# @!visibility private
|
493
|
+
def scroll_to_region
|
494
|
+
region_visibility_strategy.is_a? Applitools::Selenium::MoveToRegionVisibilityStrategy
|
495
|
+
end
|
496
|
+
|
497
|
+
# @!visibility private
|
498
|
+
def scroll_to_region=(value)
|
499
|
+
if value
|
500
|
+
self.region_visibility_strategy = Applitools::Selenium::MoveToRegionVisibilityStrategy.new
|
501
|
+
else
|
502
|
+
self.region_visibility_strategy = Applitools::Selenium::NopRegionVisibilityStrategy.new
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def dom_data
|
507
|
+
return {} if prevent_dom_processing
|
508
|
+
begin
|
509
|
+
Applitools::Selenium::DomCapture.get_window_dom(driver, logger)
|
510
|
+
rescue Applitools::EyesError => e
|
511
|
+
logger.error "DOM capture failed! #{e.message}"
|
512
|
+
return {}
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
private
|
517
|
+
|
518
|
+
attr_accessor :check_frame_or_element, :region_to_check, :dont_get_title,
|
519
|
+
:device_pixel_ratio, :position_provider, :scale_provider, :tag_for_debug,
|
520
|
+
:region_visibility_strategy, :eyes_screenshot_factory, :force_driver_resolution_as_viewport_size,
|
521
|
+
:prevent_dom_processing
|
522
|
+
|
523
|
+
def image_provider
|
524
|
+
Applitools::Selenium::TakesScreenshotImageProvider.new(
|
525
|
+
driver,
|
526
|
+
debug_screenshot_provider: debug_screenshot_provider
|
527
|
+
)
|
528
|
+
end
|
529
|
+
|
530
|
+
def capture_screenshot
|
531
|
+
logger.info 'Getting screenshot (capture_screenshot() has been invoked)'
|
532
|
+
|
533
|
+
update_scaling_params
|
534
|
+
|
535
|
+
if hide_scrollbars
|
536
|
+
begin
|
537
|
+
original_overflow = Applitools::Utils::EyesSeleniumUtils.hide_scrollbars driver
|
538
|
+
driver.find_element(:css, 'html').overflow_data_attribute = original_overflow
|
539
|
+
rescue Applitools::EyesDriverOperationException => e
|
540
|
+
logger.warn "Failed to hide scrollbars! Error: #{e.message}"
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
begin
|
545
|
+
algo = Applitools::Selenium::FullPageCaptureAlgorithm.new(
|
546
|
+
debug_screenshot_provider: debug_screenshot_provider
|
547
|
+
)
|
548
|
+
case screenshot_type
|
549
|
+
when ENTIRE_ELEMENT_SCREENSHOT
|
550
|
+
self.screenshot = entire_element_screenshot(algo)
|
551
|
+
when FULLPAGE_SCREENSHOT
|
552
|
+
self.screenshot = full_page_screenshot(algo)
|
553
|
+
when VIEWPORT_SCREENSHOT
|
554
|
+
self.screenshot = viewport_screenshot
|
555
|
+
end
|
556
|
+
ensure
|
557
|
+
begin
|
558
|
+
Applitools::Utils::EyesSeleniumUtils.set_overflow driver, original_overflow
|
559
|
+
rescue Applitools::EyesDriverOperationException => e
|
560
|
+
logger.warn "Failed to revert overflow! Error: #{e.message}"
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def full_page_screenshot(algo)
|
566
|
+
logger.info 'Full page screenshot requested'
|
567
|
+
original_frame = driver.frame_chain
|
568
|
+
driver.switch_to.default_content
|
569
|
+
region_provider = Applitools::Selenium::RegionProvider.new(driver, Applitools::Region::EMPTY)
|
570
|
+
|
571
|
+
full_page_image = algo.get_stitched_region(
|
572
|
+
image_provider: image_provider,
|
573
|
+
region_to_check: region_provider,
|
574
|
+
origin_provider: Applitools::Selenium::ScrollPositionProvider.new(driver),
|
575
|
+
position_provider: position_provider,
|
576
|
+
scale_provider: scale_provider,
|
577
|
+
cut_provider: cut_provider,
|
578
|
+
wait_before_screenshots: wait_before_screenshots,
|
579
|
+
eyes_screenshot_factory: eyes_screenshot_factory,
|
580
|
+
stitching_overlap: stitching_overlap
|
581
|
+
)
|
582
|
+
|
583
|
+
# binding.pry
|
584
|
+
unless original_frame.empty?
|
585
|
+
logger.info 'Switching back to original frame...'
|
586
|
+
driver.switch_to.frames frame_chain: original_frame
|
587
|
+
logger.info 'Done switching!'
|
588
|
+
end
|
589
|
+
logger.info 'Creating EyesWebDriver screenshot instance..'
|
590
|
+
result = Applitools::Selenium::FullpageScreenshot.new(
|
591
|
+
full_page_image,
|
592
|
+
region_provider: region_to_check
|
593
|
+
)
|
594
|
+
logger.info 'Done creating EyesWebDriver screenshot instance!'
|
595
|
+
result
|
596
|
+
end
|
597
|
+
|
598
|
+
def entire_element_screenshot(algo)
|
599
|
+
logger.info 'Entire element screenshot requested'
|
600
|
+
entire_frame_or_element = algo.get_stitched_region(
|
601
|
+
image_provider: image_provider,
|
602
|
+
region_to_check: region_to_check,
|
603
|
+
origin_provider: position_provider,
|
604
|
+
position_provider: position_provider,
|
605
|
+
scale_provider: scale_provider,
|
606
|
+
cut_provider: cut_provider,
|
607
|
+
wait_before_screenshots: wait_before_screenshots,
|
608
|
+
eyes_screenshot_factory: eyes_screenshot_factory,
|
609
|
+
stitching_overlap: stitching_overlap,
|
610
|
+
top_left_position: full_page_capture_algorithm_left_top_offset
|
611
|
+
)
|
612
|
+
|
613
|
+
logger.info 'Building screenshot object (EyesStitchedElementScreenshot)...'
|
614
|
+
result = Applitools::Selenium::EntireElementScreenshot.new(
|
615
|
+
entire_frame_or_element,
|
616
|
+
region_provider: region_to_check
|
617
|
+
)
|
618
|
+
logger.info 'Done!'
|
619
|
+
result
|
620
|
+
end
|
621
|
+
|
622
|
+
def viewport_screenshot
|
623
|
+
logger.info 'Viewport screenshot requested'
|
624
|
+
sleep wait_before_screenshots
|
625
|
+
image = image_provider.take_screenshot
|
626
|
+
scale_provider.scale_image(image) if scale_provider
|
627
|
+
local_cut_provider = (
|
628
|
+
cut_provider || Applitools::Selenium::FixedCutProvider.viewport(image, viewport_size, region_to_check)
|
629
|
+
)
|
630
|
+
local_cut_provider.cut(image) if local_cut_provider
|
631
|
+
eyes_screenshot_factory.call(image)
|
632
|
+
end
|
633
|
+
|
634
|
+
def vp_size=(value, skip_check_if_open = false)
|
635
|
+
raise Applitools::EyesNotOpenException.new 'set_viewport_size: Eyes not open!' unless skip_check_if_open || open?
|
636
|
+
original_frame = driver.frame_chain
|
637
|
+
driver.switch_to.default_content
|
638
|
+
begin
|
639
|
+
Applitools::Utils::EyesSeleniumUtils.set_viewport_size driver, value
|
640
|
+
rescue => e
|
641
|
+
logger.error e.class.to_s
|
642
|
+
logger.error e.message
|
643
|
+
raise Applitools::TestFailedError.new "#{e.class} - #{e.message}"
|
644
|
+
ensure
|
645
|
+
driver.switch_to.frames(frame_chain: original_frame)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
alias set_viewport_size vp_size=
|
650
|
+
|
651
|
+
def get_driver(options)
|
652
|
+
# TODO: remove the "browser" related block when possible. It's for backward compatibility.
|
653
|
+
if options.key?(:browser)
|
654
|
+
logger.warn('"browser" key is deprecated, please use "driver" instead.')
|
655
|
+
return options[:browser]
|
656
|
+
end
|
657
|
+
|
658
|
+
options.fetch(:driver, nil)
|
659
|
+
end
|
660
|
+
|
661
|
+
def update_scaling_params
|
662
|
+
return unless device_pixel_ratio == UNKNOWN_DEVICE_PIXEL_RATIO
|
663
|
+
|
664
|
+
logger.info 'Trying to extract device pixel ratio...'
|
665
|
+
begin
|
666
|
+
self.device_pixel_ratio = Applitools::Utils::EyesSeleniumUtils.device_pixel_ratio(driver)
|
667
|
+
rescue Applitools::EyesDriverOperationException
|
668
|
+
logger.warn 'Failed to extract device pixel ratio! Using default.'
|
669
|
+
self.device_pixel_ratio = DEFAULT_DEVICE_PIXEL_RATIO
|
670
|
+
end
|
671
|
+
|
672
|
+
logger.info "Device pixel_ratio: #{device_pixel_ratio}"
|
673
|
+
logger.info 'Setting scale provider...'
|
674
|
+
|
675
|
+
begin
|
676
|
+
self.scale_provider = Applitools::Selenium::ContextBasedScaleProvider.new(position_provider.entire_size,
|
677
|
+
viewport_size, device_pixel_ratio)
|
678
|
+
rescue StandardError
|
679
|
+
logger.info 'Failed to set ContextBasedScaleProvider'
|
680
|
+
logger.info 'Using FixedScaleProvider instead'
|
681
|
+
self.scale_provider = Applitools::FixedScaleProvider.new(1.to_f / device_pixel_ratio)
|
682
|
+
end
|
683
|
+
logger.info 'Done!'
|
684
|
+
end
|
685
|
+
|
686
|
+
def _add_text_trigger(control, text)
|
687
|
+
unless last_screenshot
|
688
|
+
logger.info "Ignoring #{text} (no screenshot)"
|
689
|
+
return
|
690
|
+
end
|
691
|
+
|
692
|
+
unless driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
|
693
|
+
logger.info "Ignoring #{text} (different_frame)"
|
694
|
+
return
|
695
|
+
end
|
696
|
+
|
697
|
+
add_text_trigger_base(control, text)
|
698
|
+
end
|
699
|
+
|
700
|
+
def add_text_trigger(control, text)
|
701
|
+
if disabled?
|
702
|
+
logger.info "Ignoring #{text} (disabled)"
|
703
|
+
return
|
704
|
+
end
|
705
|
+
|
706
|
+
Applitools::ArgumentGuard.not_nil control, 'control'
|
707
|
+
return _add_text_trigger(control, text) if control.is_a? Applitools::Region
|
708
|
+
|
709
|
+
pl = control.location
|
710
|
+
ds = control.size
|
711
|
+
|
712
|
+
element_region = Applitools::Region.new(pl.x, pl.y, ds.width, ds.height)
|
713
|
+
|
714
|
+
return _add_text_trigger(element_region, text) if control.is_a? Applitools::Selenium::Element
|
715
|
+
end
|
716
|
+
|
717
|
+
def add_mouse_trigger(mouse_action, element)
|
718
|
+
if disabled?
|
719
|
+
logger.info "Ignoring #{mouse_action} (disabled)"
|
720
|
+
return
|
721
|
+
end
|
722
|
+
|
723
|
+
if element.is_a? Hash
|
724
|
+
return add_mouse_trigger_by_region_and_location(mouse_action, element[:region], element[:location]) if
|
725
|
+
element.key?(:location) && element.key?(:region)
|
726
|
+
raise Applitools::EyesIllegalArgument.new 'Element[] doesn\'t contain required keys!'
|
727
|
+
end
|
728
|
+
|
729
|
+
Applitools::ArgumentGuard.not_nil element, 'element'
|
730
|
+
Applitools::ArgumentGuard.is_a? element, 'element', Applitools::Selenium::Element
|
731
|
+
|
732
|
+
pl = element.location
|
733
|
+
ds = element.size
|
734
|
+
|
735
|
+
element_region = Applitools::Region.new(pl.x, pl.y, ds.width, ds.height)
|
736
|
+
|
737
|
+
unless last_screenshot
|
738
|
+
logger.info "Ignoring #{mouse_action} (no screenshot)"
|
739
|
+
return
|
740
|
+
end
|
741
|
+
|
742
|
+
unless driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
|
743
|
+
logger.info "Ignoring #{mouse_action} (different_frame)"
|
744
|
+
return
|
745
|
+
end
|
746
|
+
|
747
|
+
add_mouse_trigger_base(mouse_action, element_region, element_region.middle_offset)
|
748
|
+
end
|
749
|
+
|
750
|
+
# control - Region
|
751
|
+
# cursor - Location
|
752
|
+
def add_mouse_trigger_by_region_and_location(mouse_action, control, cursor)
|
753
|
+
unless last_screenshot
|
754
|
+
logger.info "Ignoring #{mouse_action} (no screenshot)"
|
755
|
+
return
|
756
|
+
end
|
757
|
+
|
758
|
+
Applitools::ArgumentGuard.is_a? control, 'control', Applitools::Region
|
759
|
+
Applitools::ArgumentGuard.is_a? cursor, 'cursor', Applitools::Location
|
760
|
+
|
761
|
+
if driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
|
762
|
+
logger.info "Ignoring #{mouse_action} (different_frame)"
|
763
|
+
return
|
764
|
+
end
|
765
|
+
|
766
|
+
add_mouse_trigger_base(mouse_action, control, cursor)
|
767
|
+
end
|
768
|
+
|
769
|
+
public :add_text_trigger, :add_mouse_trigger, :add_mouse_trigger_by_region_and_location
|
770
|
+
|
771
|
+
protected
|
772
|
+
|
773
|
+
def app_environment
|
774
|
+
app_env = super
|
775
|
+
if app_env.os.nil?
|
776
|
+
logger.info 'No OS set, checking for mobile OS...'
|
777
|
+
underlying_driver = Applitools::Utils::EyesSeleniumUtils.mobile_device?(driver)
|
778
|
+
unless underlying_driver.nil?
|
779
|
+
logger.info 'Mobile device detected! Checking device type...'
|
780
|
+
if Applitools::Utils::EyesSeleniumUtils.android?(underlying_driver)
|
781
|
+
logger.info 'Android detected...'
|
782
|
+
platform_name = 'Android'
|
783
|
+
elsif Applitools::Utils::EyesSeleniumUtils.ios?(underlying_driver)
|
784
|
+
logger.info 'iOS detected...'
|
785
|
+
platform_name = 'iOS'
|
786
|
+
else
|
787
|
+
logger.info 'Unknown device type'
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
if platform_name && !platform_name.empty?
|
792
|
+
os = platform_name
|
793
|
+
platform_version = Applitools::Utils::EyesSeleniumUtils.platform_version(underlying_driver).to_s
|
794
|
+
unless platform_version.empty?
|
795
|
+
major_version = platform_version.split(/\./).first
|
796
|
+
os << " #{major_version}"
|
797
|
+
end
|
798
|
+
logger.info "Setting OS: #{os}"
|
799
|
+
app_env.os = os
|
800
|
+
end
|
801
|
+
else
|
802
|
+
logger.info 'No mobile OS detected.'
|
803
|
+
end
|
804
|
+
app_env
|
805
|
+
end
|
806
|
+
|
807
|
+
def inferred_environment
|
808
|
+
return @inferred_environment unless @inferred_environment.nil?
|
809
|
+
|
810
|
+
user_agent = driver.user_agent
|
811
|
+
return "useragent: #{user_agent}" if user_agent && !user_agent.empty?
|
812
|
+
|
813
|
+
nil
|
814
|
+
end
|
815
|
+
|
816
|
+
def ensure_frame_visible
|
817
|
+
original_fc = driver.frame_chain
|
818
|
+
return original_fc if original_fc.empty?
|
819
|
+
fc = Applitools::Selenium::FrameChain.new other: original_fc
|
820
|
+
until fc.empty?
|
821
|
+
driver.switch_to.parent_frame
|
822
|
+
position_provider.position = fc.pop.location
|
823
|
+
end
|
824
|
+
driver.switch_to.frames(frame_chain: original_fc)
|
825
|
+
original_fc
|
826
|
+
end
|
827
|
+
|
828
|
+
def reset_frames_scroll_position(original_fc)
|
829
|
+
return original_fc if original_fc.empty?
|
830
|
+
fc = Applitools::Selenium::FrameChain.new other: original_fc
|
831
|
+
until fc.empty?
|
832
|
+
driver.switch_to.parent_frame
|
833
|
+
position_provider.position = fc.pop.parent_scroll_position
|
834
|
+
end
|
835
|
+
driver.switch_to.frames(frame_chain: original_fc)
|
836
|
+
original_fc
|
837
|
+
end
|
838
|
+
|
839
|
+
class << self
|
840
|
+
def position_provider(stitch_mode, driver, disable_horizontal = false, disable_vertical = false,
|
841
|
+
explicit_entire_size = nil)
|
842
|
+
|
843
|
+
max_width = nil
|
844
|
+
max_height = nil
|
845
|
+
unless explicit_entire_size.nil?
|
846
|
+
max_width = explicit_entire_size.width
|
847
|
+
max_height = explicit_entire_size.height
|
848
|
+
end
|
849
|
+
case stitch_mode
|
850
|
+
when :SCROLL
|
851
|
+
Applitools::Selenium::ScrollPositionProvider.new(driver, disable_horizontal, disable_vertical,
|
852
|
+
max_width, max_height)
|
853
|
+
when :CSS
|
854
|
+
Applitools::Selenium::CssTranslatePositionProvider.new(driver, disable_horizontal, disable_vertical,
|
855
|
+
max_width, max_height)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
860
|
+
end
|