eyes_selenium 3.14.10 → 3.15.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/selenium/concerns/browser_types.rb +15 -0
  3. data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
  4. data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
  5. data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
  6. data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
  7. data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
  8. data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
  9. data/lib/applitools/selenium/concerns/test_list.rb +23 -0
  10. data/lib/applitools/selenium/eyes.rb +13 -854
  11. data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
  12. data/lib/applitools/selenium/selenium_configuration.rb +64 -0
  13. data/lib/applitools/selenium/selenium_eyes.rb +860 -0
  14. data/lib/applitools/selenium/target.rb +2 -2
  15. data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
  16. data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
  17. data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
  18. data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
  19. data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
  20. data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
  21. data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
  22. data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
  23. data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
  24. data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
  25. data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
  26. data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
  27. data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
  28. data/lib/applitools/version.rb +1 -1
  29. data/lib/eyes_selenium.rb +3 -0
  30. metadata +46 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96e0b2d98c498cc0f8b0b196dbfc2ae3d37e1c3c6c29dc372b61e049697fd293
4
- data.tar.gz: ddd642143902692e6a86d8417aa5a37ba74ab7a307f9de87d7ed2d83d79079bd
3
+ metadata.gz: 27cfe7cfe214411e122538c9fe51e6677fdc68741269dfa8c168b475fb54670e
4
+ data.tar.gz: 82fe22114714117850b1a6ca99b4863950c96465ac9d4eded6f1e9c02a613871
5
5
  SHA512:
6
- metadata.gz: 58801fc423d3aa44d36ad34b66b4cae2b81321716335aaa8d49c99a8382fe90e4662d3ebb4373829722291ae2c6867af1e56c6d8ab4b2b1bb09ad24b8be463d9
7
- data.tar.gz: dba14c3b600f60d4a835037781dbeaa45a32ef0a7c80a1cd62d0d790ddf6deca9a3dde83161c008f6276caddfcbc49831f3569faf15cc2356835db26a8a32a8c
6
+ metadata.gz: 32f774769649c2187966e03b8381a39aaff1f9c824d5266d817e44519fd315400917f160c715b55ee5d9373f2afbf2beb56c0eef495c1edde7435e89b2ba126b
7
+ data.tar.gz: bf8e3617978ef915b33d39bded83ba241c156e097541984dfff8d9ed4fe6d0656b9be7758bc28530613217b74ff7d62fd2ce69a3fba58560183a901f86441eb4
@@ -0,0 +1,15 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ module BrowserTypes
5
+ extend self
6
+ CHROME = :chrome
7
+ FIREFOX = :firefox
8
+
9
+ def enum_values
10
+ [CHROME, FIREFOX]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ class BrowsersInfo < Set
5
+ def add(obj)
6
+ return super if obj.is_a? Applitools::Selenium::RenderBrowserInfo
7
+ raise(
8
+ Applitools::EyesIllegalArgument,
9
+ "It is expected the value to be an Applitools::Selenium::RenderBrowserInfo instance," \
10
+ " but got #{obj.class} instead"
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ require 'css_parser'
2
+ require 'pry'
3
+
4
+ module Applitools
5
+ module Selenium
6
+ module Concerns
7
+ class ExternalCssResources
8
+ include CssParser
9
+ def initialize(url, base_url = nil)
10
+ @parser = CssParser::Parser.new(absolute_paths: true)
11
+ @parser.load_uri!(url)
12
+ @parser.compact!
13
+ end
14
+
15
+ def flatten_rules
16
+ @flatten ||= flatten_hash(hash, 0)
17
+ end
18
+
19
+ def hash
20
+ @h ||= @parser.to_h
21
+ end
22
+
23
+ def images
24
+ result = []
25
+ @parser.each_rule_set { |s| s.expand_background_shorthand!; result.push(s) unless s.get_value('background-image').empty? }
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ class RenderBrowserInfoFluent
5
+ attr_accessor :browser_info
6
+ def initialize(browser_info)
7
+ self.browser_info = browser_info
8
+ end
9
+
10
+ def width(value)
11
+ browser_info.viewport_size.width = value
12
+ self
13
+ end
14
+
15
+ def height(value)
16
+ browser_info.viewport_size.height = value
17
+ self
18
+ end
19
+
20
+ def type(value)
21
+ browser_info.browser_type = value
22
+ self
23
+ end
24
+
25
+ def platform(value)
26
+ browser_info.platform = value
27
+ self
28
+ end
29
+
30
+ def size_mode(value)
31
+ browser_info.size_mode = value
32
+ self
33
+ end
34
+
35
+ def environment(value)
36
+ browser_info.baseline_env_name = value
37
+ self
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ class RenderResources < Hash
5
+ def []=(key, value)
6
+ raise Applitools::EyesIllegalArgument, "Expected key to be an instance of URI (but got #{key.class})" unless
7
+ key.is_a? URI
8
+ raise Applitools::EyesIllegalArgument, "Expected key to be an instance of Applitools::Selenium::VGResource" \
9
+ " (but got #{value.class})" unless value.is_a? Applitools::Selenium::VGResource
10
+ return super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ class RGridDom
5
+ CONTENT_TYPE = 'x-applitools-html/cdt'.freeze
6
+ include Applitools::Jsonable
7
+ json_fields :hash, :hashFormat
8
+ attr_accessor :dom_nodes, :resources, :url, :data
9
+
10
+ def initialize(*args)
11
+ options = Applitools::Utils.extract_options! args
12
+ self.url = options[:url]
13
+ self.dom_nodes = options[:dom_nodes]
14
+ self.resources = options[:resources]
15
+ self.hash_format = 'sha256'
16
+ self.data = {
17
+ 'resources' => resources,
18
+ 'domNodes' => dom_nodes
19
+ }
20
+ self.hash = calculate_sha_256
21
+ end
22
+
23
+ def calculate_sha_256
24
+ # str = Applitools::Utils.stringify_for_hash(data)
25
+ Digest::SHA256.hexdigest(content)
26
+ end
27
+
28
+ # def hash
29
+ # calculate_sha_256
30
+ # end
31
+
32
+ def content
33
+ Oj.dump(json_value(data.sort.to_h))
34
+ end
35
+
36
+ def resource
37
+ Applitools::Selenium::VGResource.new(
38
+ url,
39
+ CONTENT_TYPE,
40
+ content
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Concerns
4
+ module StitchModes
5
+ extend self
6
+ CSS = :CSS
7
+ SCROLL = :SCROLL
8
+
9
+ def enum_values
10
+ [CSS, SCROLL]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Applitools
2
+ module Selenium
3
+ class TestList < Array
4
+ def <<(value)
5
+ raise(
6
+ Applitools::EyesIllegalArgument,
7
+ "Expected value to be instance of Applitools::Selenium::RunningTest but got #{value.class} instead"
8
+ ) unless value.is_a? Applitools::Selenium::RunningTest
9
+ super
10
+ end
11
+
12
+ def push(*args)
13
+ wrong_values = args.select { |a| !a.is_a? Applitools::Selenium::RunningTest }
14
+ raise(
15
+ Applitools::EyesIllegalArgument,
16
+ "Expected values to contain only Applitools::Selenium::RunningTest instances, " \
17
+ "but got [#{wrong_values.map(&:class).join(', ')}]"
18
+ ) unless wrong_values.empty?
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,860 +1,19 @@
1
- # frozen_string_literal: false
2
-
3
- module Applitools::Selenium
4
- # The main API gateway for the SDK
5
- class Eyes < 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
1
+ module Applitools
2
+ module Selenium
3
+ class Eyes < SimpleDelegator
4
+
5
+ def initialize(*args)
6
+ options = Applitools::Utils.extract_options!(args)
7
+ server_url = options.delete(:server_url)
8
+ server_url = args.first unless server_url
9
+ runner = options.delete(:visual_grid_runner)
10
+ if runner.is_a? Applitools::Selenium::VisualGridRunner
11
+ super Applitools::Selenium::VisualGridEyes.new(runner, server_url)
31
12
  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
13
+ super Applitools::Selenium::SeleniumEyes.new(server_url)
800
14
  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
15
  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
16
 
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
17
  end
859
18
  end
860
- end
19
+ end