eyes_selenium 2.39.1 → 3.0.6

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/capybara.rb +1 -1
  3. data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +41 -0
  4. data/lib/applitools/selenium/browser.rb +1 -0
  5. data/lib/applitools/selenium/capybara/capybara_settings.rb +8 -0
  6. data/lib/applitools/selenium/capybara/driver.rb +1 -0
  7. data/lib/applitools/selenium/context_based_scale_provider.rb +36 -0
  8. data/lib/applitools/selenium/css_translate_position_provider.rb +66 -0
  9. data/lib/applitools/selenium/driver.rb +159 -44
  10. data/lib/applitools/selenium/element.rb +100 -8
  11. data/lib/applitools/selenium/element_position_provider.rb +48 -0
  12. data/lib/applitools/selenium/eyes.rb +922 -0
  13. data/lib/applitools/selenium/eyes_target_locator.rb +191 -0
  14. data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +274 -0
  15. data/lib/applitools/selenium/frame.rb +24 -0
  16. data/lib/applitools/selenium/frame_chain.rb +68 -0
  17. data/lib/applitools/selenium/full_page_capture_algorithm.rb +162 -0
  18. data/lib/applitools/selenium/keyboard.rb +1 -0
  19. data/lib/applitools/selenium/match_window_task.rb +0 -197
  20. data/lib/applitools/selenium/mouse.rb +1 -0
  21. data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +33 -0
  22. data/lib/applitools/selenium/nop_region_visibility_strategy.rb +16 -0
  23. data/lib/applitools/selenium/scroll_position_provider.rb +52 -0
  24. data/lib/applitools/selenium/takes_screenshot_image_provider.rb +36 -0
  25. data/lib/applitools/version.rb +1 -1
  26. data/lib/eyes_selenium.rb +19 -30
  27. metadata +24 -254
  28. data/.gitignore +0 -18
  29. data/.rspec +0 -2
  30. data/.rubocop.yml +0 -57
  31. data/.travis.yml +0 -17
  32. data/Gemfile +0 -4
  33. data/LICENSE.txt +0 -13
  34. data/README.md +0 -38
  35. data/Rakefile +0 -11
  36. data/certs/cacert.pem +0 -3557
  37. data/examples/appium_example_script.rb +0 -59
  38. data/examples/capybara_example.rb +0 -82
  39. data/examples/sauce_capybara_example.rb +0 -41
  40. data/examples/sauce_example.rb +0 -34
  41. data/examples/simple_test_script.rb +0 -23
  42. data/examples/watir_test_script.rb +0 -26
  43. data/eyes_selenium.gemspec +0 -89
  44. data/lib/applitools/appium_driver.rb +0 -7
  45. data/lib/applitools/base/batch_info.rb +0 -21
  46. data/lib/applitools/base/dimension.rb +0 -36
  47. data/lib/applitools/base/environment.rb +0 -19
  48. data/lib/applitools/base/image_position.rb +0 -10
  49. data/lib/applitools/base/mouse_trigger.rb +0 -33
  50. data/lib/applitools/base/point.rb +0 -34
  51. data/lib/applitools/base/region.rb +0 -112
  52. data/lib/applitools/base/server_connector.rb +0 -120
  53. data/lib/applitools/base/session.rb +0 -15
  54. data/lib/applitools/base/start_info.rb +0 -34
  55. data/lib/applitools/base/test_results.rb +0 -28
  56. data/lib/applitools/base/text_trigger.rb +0 -22
  57. data/lib/applitools/extensions.rb +0 -17
  58. data/lib/applitools/eyes.rb +0 -460
  59. data/lib/applitools/eyes_logger.rb +0 -38
  60. data/lib/applitools/method_tracer.rb +0 -22
  61. data/lib/applitools/poltergeist/applitools_compatible.rb +0 -28
  62. data/lib/applitools/poltergeist/driver.rb +0 -11
  63. data/lib/applitools/sauce.rb +0 -2
  64. data/lib/applitools/selenium/match_window_data.rb +0 -28
  65. data/lib/applitools/selenium_webdriver.rb +0 -8
  66. data/lib/applitools/utils/image_delta_compressor.rb +0 -148
  67. data/lib/applitools/utils/image_utils.rb +0 -143
  68. data/lib/applitools/utils/utils.rb +0 -49
  69. data/lib/applitools/watir_browser.rb +0 -8
  70. data/spec/driver_passthrough_spec.rb +0 -68
  71. data/spec/fixtures/static_test_file.html +0 -15
  72. data/spec/spec_helper.rb +0 -17
@@ -1,38 +0,0 @@
1
- require 'logger'
2
- require 'forwardable'
3
-
4
- module Applitools::EyesLogger
5
- class NullLogger < Logger
6
- def initialize(*_args) end
7
-
8
- def add(*_args, &_block) end
9
- end
10
-
11
- extend Forwardable
12
- extend self
13
-
14
- MANDATORY_METHODS = [:debug, :info, :close].freeze
15
- OPTIONAL_METHODS = [:warn, :error, :fatal].freeze
16
-
17
- def_delegators :@log_handler, *MANDATORY_METHODS
18
-
19
- @log_handler = NullLogger.new
20
-
21
- def log_handler=(log_handler)
22
- raise Applitools::EyesError.new('log_handler must implement Logger!') unless valid?(log_handler)
23
-
24
- @log_handler = log_handler
25
- end
26
-
27
- OPTIONAL_METHODS.each do |method|
28
- define_singleton_method(method) do |msg|
29
- @log_handler.respond_to?(method) ? @log_handler.send(method, msg) : @log_handler.info(msg)
30
- end
31
- end
32
-
33
- private
34
-
35
- def valid?(log_handler)
36
- MANDATORY_METHODS.all? { |method| log_handler.respond_to?(method) }
37
- end
38
- end
@@ -1,22 +0,0 @@
1
- module Applitools::MethodTracer
2
- def self.included(base)
3
- instance_methods = base.instance_methods(false) + base.private_instance_methods(false)
4
- class_methods = base.methods(false)
5
-
6
- base.class_eval do
7
- def self.trace_method(base, method_name, instance = true)
8
- original_method = instance ? instance_method(method_name) : method(method_name)
9
-
10
- send(instance ? :define_method : :define_singleton_method, method_name) do |*args, &block|
11
- Applitools::EyesLogger.debug "-> #{base}##{method_name}"
12
- return_value = (instance ? original_method.bind(self) : original_method).call(*args, &block)
13
- Applitools::EyesLogger.debug "<- #{base}##{method_name}"
14
- return_value
15
- end
16
- end
17
-
18
- instance_methods.each { |method_name| trace_method(base, method_name) }
19
- class_methods.each { |method_name| trace_method(base, method_name, false) }
20
- end
21
- end
22
- end
@@ -1,28 +0,0 @@
1
- # This module is used for compatibility with Applitools API.
2
- # Should be extended by Poltergeist driver instance.
3
- module Applitools::Poltergeist
4
- module ApplitoolsCompatible
5
- # Implementation of `screenshot_as` method for PhantomJS.
6
- # Realisation uses Poltergeist binding to `renderBase64` PhantomJS method.
7
- def screenshot_as(fmt)
8
- Base64.decode64(browser.render_base64(fmt))
9
- end
10
-
11
- # Poltergeist driver does not have `manage` and `window` methods.
12
- # In Applitools these methods are used in a chain to get size by `size` method call.
13
- %w(manage window).each do |method_name|
14
- define_method(method_name) { self }
15
- end
16
-
17
- # Method provides opened window size in Applitools format.
18
- def size
19
- size = window_size(current_window_handle)
20
- Applitools::Base::Dimension.new(size[0], size[1])
21
- end
22
-
23
- # Method changes opened window size in a way how original Applitools::Selenium::Driver does.
24
- def size=(new_size)
25
- resize(new_size.width, new_size.height)
26
- end
27
- end
28
- end
@@ -1,11 +0,0 @@
1
- # Applitools::Poltergeist::Driver is a small class implemented
2
- # for compatibility with Applitools API.
3
- # It gives required for Applitools methods to Poltergeist driver.
4
- module Applitools::Poltergeist
5
- class Driver < Applitools::Selenium::Driver
6
- def initialize(eyes, options)
7
- options[:driver].extend Applitools::Poltergeist::ApplitoolsCompatible
8
- super(eyes, options)
9
- end
10
- end
11
- end
@@ -1,2 +0,0 @@
1
- require_relative 'capybara' if defined? Capybara
2
- Applitools.require_dir 'selenium/sauce'
@@ -1,28 +0,0 @@
1
- module Applitools::Selenium
2
- class MatchWindowData
3
- attr_reader :user_inputs, :app_output, :tag, :ignore_mismatch, :screenshot
4
-
5
- def initialize(app_output, tag, ignore_mismatch, screenshot, user_inputs = [])
6
- @user_inputs = user_inputs
7
- @app_output = app_output
8
- @tag = tag
9
- @ignore_mismatch = ignore_mismatch
10
- @screenshot = screenshot
11
- end
12
-
13
- # IMPORTANT This method returns a hash WITHOUT the screenshot property. This is on purpose! The screenshot should
14
- # not be included as part of the json.
15
- def to_hash
16
- {
17
- user_inputs: user_inputs.map(&:to_hash),
18
- app_output: Hash[app_output.each_pair.to_a],
19
- tag: @tag,
20
- ignore_mismatch: @ignore_mismatch
21
- }
22
- end
23
-
24
- def screenshot
25
- @screenshot.to_blob.force_encoding('BINARY')
26
- end
27
- end
28
- end
@@ -1,8 +0,0 @@
1
- if defined? Selenium::WebDriver::Driver
2
- Selenium::WebDriver::Driver.class_eval do
3
- def driver_for_eyes(eyes)
4
- is_mobile_device = capabilities['platformName'] ? true : false
5
- Applitools::Selenium::Driver.new(eyes, driver: self, is_mobile_device: is_mobile_device)
6
- end
7
- end
8
- end
@@ -1,148 +0,0 @@
1
- require 'oily_png'
2
-
3
- module Applitools::Utils
4
- module ImageDeltaCompressor
5
- extend self
6
-
7
- BLOCK_SIZE = 10
8
-
9
- # Compresses the target image based on the source image.
10
- #
11
- # +target+:: +ChunkyPNG::Canvas+ The image to compress based on the source image.
12
- # +target_encoded+:: +Array+ The uncompressed image as binary string.
13
- # +source+:: +ChunkyPNG::Canvas+ The source image used as a base for compressing the target image.
14
- # +block_size+:: +Integer+ The width/height of each block.
15
- #
16
- # Returns +String+ The binary result (either the compressed image, or the uncompressed image if the compression
17
- # is greater in length).
18
- def compress_by_raw_blocks(target, target_encoded, source, block_size = BLOCK_SIZE)
19
- # If we can't compress for any reason, return the target image as is.
20
- if source.nil? || (source.height != target.height) || (source.width != target.width)
21
- # Returning a COPY of the target binary string.
22
- return String.new(target_encoded)
23
- end
24
-
25
- # Preparing the variables we need.
26
- target_pixels = target.to_rgb_stream.unpack('C*')
27
- source_pixels = source.to_rgb_stream.unpack('C*')
28
- image_size = Dimension.new(target.width, target.height)
29
- block_columns_count = (target.width / block_size) + ((target.width % block_size).zero? ? 0 : 1)
30
- block_rows_count = (target.height / block_size) + ((target.height % block_size).zero? ? 0 : 1)
31
-
32
- # IMPORTANT: The "-Zlib::MAX_WBITS" tells ZLib to create raw deflate compression, without the
33
- # "Zlib headers" (this isn't documented in the Zlib page, I found this in some internet forum).
34
- compressor = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS)
35
-
36
- compression_result = ''
37
-
38
- # Writing the data header.
39
- compression_result += PREAMBLE.encode('UTF-8')
40
- compression_result += [FORMAT_RAW_BLOCKS].pack('C')
41
- compression_result += [0].pack('S>') # Source id, Big Endian
42
- compression_result += [block_size].pack('S>') # Big Endian
43
-
44
- # We perform the compression for each channel.
45
- 3.times do |channel|
46
- block_number = 0
47
- block_rows_count.times do |block_row|
48
- block_columns_count.times do |block_column|
49
- actual_channel_index = 2 - channel # Since the image bytes are BGR and the server expects RGB...
50
- compare_result = compare_and_copy_block_channel_data(source_pixels, target_pixels, image_size, 3,
51
- block_size, block_column, block_row, actual_channel_index)
52
-
53
- unless compare_result.identical
54
- channel_bytes = compare_result.channel_bytes
55
- string_to_compress = [channel].pack('C')
56
- string_to_compress += [block_number].pack('L>')
57
- string_to_compress += channel_bytes.pack('C*')
58
-
59
- compression_result += compressor.deflate(string_to_compress)
60
-
61
- # If the compressed data so far is greater than the uncompressed representation of the target, just return
62
- # the target.
63
- if compression_result.length > target_encoded.length
64
- compressor.finish
65
- compressor.close
66
- # Returning a copy of the target bytes.
67
- return String.new(target_encoded)
68
- end
69
- end
70
-
71
- block_number += 1
72
- end
73
- end
74
- end
75
-
76
- # Compress and flush any remaining uncompressed data in the input buffer.
77
- compression_result += compressor.finish
78
- compressor.close
79
-
80
- # Returning the compressed result as a byte array.
81
- compression_result
82
- end
83
-
84
- private
85
-
86
- PREAMBLE = 'applitools'.freeze
87
- FORMAT_RAW_BLOCKS = 3
88
-
89
- Dimension = Struct.new(:width, :height)
90
- CompareAndCopyBlockChannelDataResult = Struct.new(:identical, :channel_bytes)
91
-
92
- # Computes the width and height of the image data contained in the block at the input column and row.
93
- # +image_size+:: +Dimension+ The image size in pixels.
94
- # +block_size+:: The block size for which we would like to compute the image data width and height.
95
- # +block_column+:: The block column index.
96
- # +block_row+:: The block row index.
97
- # ++
98
- # Returns the width and height of the image data contained in the block are returned as a +Dimension+.
99
- def get_actual_block_size(image_size, block_size, block_column, block_row)
100
- actual_width = [image_size.width - (block_column * block_size), block_size].min
101
- actual_height = [image_size.height - (block_row * block_size), block_size].min
102
- Dimension.new(actual_width, actual_height)
103
- end
104
-
105
- # Compares a block of pixels between the source and target and copies the target's block bytes to the result.
106
- # +source_pixels+:: +Array+ of bytes, representing the pixels of the source image.
107
- # +target_pixels+:: +Array+ of bytes, representing the pixels of the target image.
108
- # +image_size+:: +Dimension+ The size of the source/target image (remember they must be the same size).
109
- # +pixel_length+:: +Integer+ The number of bytes composing a pixel
110
- # +block_size+:: +Integer+ The width/height of the block (block is a square, theoretically).
111
- # +block_column+:: +Integer+ The block column index (when looking at the images as a grid of blocks).
112
- # +block_row+:: +Integer+ The block row index (when looking at the images as a grid of blocks).
113
- # +channel+:: +Integer+ The index of the channel we're comparing.
114
- # ++
115
- # Returns +CompareAndCopyBlockChannelDataResult+ object containing a flag saying whether the blocks are identical
116
- # and a copy of the target block's bytes.
117
- def compare_and_copy_block_channel_data(source_pixels, target_pixels, image_size, pixel_length, block_size,
118
- block_column, block_row, channel)
119
- identical = true
120
-
121
- actual_block_size = get_actual_block_size(image_size, block_size, block_column, block_row)
122
-
123
- # Getting the actual amount of data in the block we wish to copy.
124
- actual_block_height = actual_block_size.height
125
- actual_block_width = actual_block_size.width
126
-
127
- stride = image_size.width * pixel_length
128
-
129
- # Iterating the block's pixels and comparing the source and target.
130
- channel_bytes = []
131
- actual_block_height.times do |h|
132
- offset = (((block_size * block_row) + h) * stride) + (block_size * block_column * pixel_length) + channel
133
- actual_block_width.times do |_w|
134
- source_byte = source_pixels[offset]
135
- target_byte = target_pixels[offset]
136
- identical = false if source_byte != target_byte
137
- channel_bytes << target_byte
138
- offset += pixel_length
139
- end
140
- end
141
-
142
- # Returning the compare-and-copy result.
143
- CompareAndCopyBlockChannelDataResult.new(identical, channel_bytes)
144
- end
145
-
146
- include Applitools::MethodTracer
147
- end
148
- end
@@ -1,143 +0,0 @@
1
- require 'oily_png'
2
- require 'base64'
3
- require 'tempfile'
4
-
5
- module Applitools::Utils
6
- QUADRANTS_COUNT = 4
7
-
8
- module ImageUtils
9
- extend self
10
-
11
- # Creates an image object from the PNG bytes.
12
- # +png_bytes+:: +String+ A binary string of the PNG bytes of the image.
13
- #
14
- # Returns:
15
- # +ChunkyPNG::Canvas+ An image object.
16
- def png_image_from_bytes(png_bytes)
17
- ChunkyPNG::Image.from_blob(png_bytes)
18
- end
19
-
20
- # Creates an image instance from a base64 representation of its PNG encoding.
21
- #
22
- # +png_bytes64+:: +String+ The Base64 representation of a PNG image.
23
- #
24
- # Returns:
25
- # +ChunkyPNG::Canvas+ An image object.
26
- def png_image_from_base64(png_bytes)
27
- png_image_from_bytes(Base64.decode64(png_bytes))
28
- end
29
-
30
- # Get the raw PNG bytes of an image.
31
- #
32
- # +ChunkyPNG::Canvas+ The image object for which to get the PNG bytes.
33
- #
34
- # Returns:
35
- # +String+ The PNG bytes of the image.
36
- def bytes_from_png_image(image)
37
- image.to_blob(:fast_rgb)
38
- end
39
-
40
- # Get the Base64 representation of the raw PNG bytes of an image.
41
- #
42
- # +ChunkyPNG::Canvas+ The image object for which to get the PNG bytes.
43
- #
44
- # Returns:
45
- # +String+ the Base64 representation of the raw PNG bytes of an image.
46
- def base64_from_png_image(image)
47
- Base64.encode64(bytes_from_png_image(image))
48
- end
49
-
50
- # Rotates a matrix 90 deg clockwise or counter clockwise (depending whether num_quadrants is positive or negative,
51
- # respectively).
52
- #
53
- # +image+:: +ChunkyPNG::Canvas+ The image to rotate.
54
- # +num_quadrants+:: +Integer+ The number of rotations to perform. Positive values are used for clockwise rotation
55
- # and negative values are used for counter-clockwise rotation.
56
- #
57
- def quadrant_rotate!(image, num_quadrants)
58
- num_quadrants %= QUADRANTS_COUNT
59
-
60
- case num_quadrants
61
- when 0
62
- image
63
- when 1
64
- image.rotate_right!
65
- when 2
66
- image.rotate_180!
67
- when 3
68
- image.rotate_left!
69
- end
70
- end
71
-
72
- def scale!(image, factor)
73
- image.resample_nearest_neighbor!(image.width.to_f * factor, image.height.to_f * factor)
74
- end
75
-
76
- def stitch_images(size, images_data)
77
- stitched_screenshot = ChunkyPNG::Image.new(size.width, size.height, ChunkyPNG::Color::TRANSPARENT).tap do |res|
78
- images_data.each do |image_data|
79
- # Crop out of bounds images.
80
- image = image_data.image
81
- position = image_data.position
82
-
83
- new_width = position.left + image.width > size.width ? size.width - position.left : image.width
84
- new_height = position.top + image.height > size.height ? size.height - position.top : image.height
85
-
86
- if new_width != image.width || new_height != image.height
87
- image = image.crop!(0, 0, new_width, new_height)
88
- end
89
-
90
- res.replace!(image.restore, position.left, position.top)
91
- GC.start
92
- end
93
- end
94
- result = Applitools::Utils::ImageUtils::Screenshot.new stitched_screenshot.to_blob.dup
95
- GC.start
96
- result
97
- end
98
-
99
- class Screenshot < Delegator
100
- extend Forwardable
101
- def_delegators :header, :width, :height
102
-
103
- def initialize(image)
104
- @datastream = ChunkyPNG::Datastream.from_string image
105
- end
106
-
107
- def to_blob
108
- @datastream.to_blob
109
- end
110
-
111
- def __getobj__
112
- restore
113
- end
114
-
115
- def header
116
- @datastream.header_chunk
117
- end
118
-
119
- def __setobj__(obj)
120
- @datastream = obj.to_datastream
121
- self
122
- end
123
-
124
- def method_missing(method, *args, &block)
125
- if method =~ /^.+!$/
126
- __setobj__ super
127
- else
128
- super
129
- end
130
- end
131
-
132
- def respond_to_missing?(method_name, include_private = false)
133
- super
134
- end
135
-
136
- def restore
137
- ChunkyPNG::Image.from_datastream @datastream
138
- end
139
- end
140
-
141
- include Applitools::MethodTracer
142
- end
143
- end
@@ -1,49 +0,0 @@
1
- require 'bigdecimal'
2
-
3
- module Applitools::Utils
4
- extend self
5
-
6
- def underscore(str)
7
- str.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase
8
- end
9
-
10
- def uncapitalize(str)
11
- str[0, 1].downcase + str[1..-1]
12
- end
13
-
14
- def camelcase(str)
15
- tokens = str.split('_')
16
- uncapitalize(tokens.shift) + tokens.map(&:capitalize).join
17
- end
18
-
19
- def wrap(object)
20
- if object.nil?
21
- []
22
- elsif object.respond_to?(:to_ary)
23
- object.to_ary || [object]
24
- else
25
- [object]
26
- end
27
- end
28
-
29
- def underscore_hash_keys(hash)
30
- convert_hash_keys(hash, :underscore)
31
- end
32
-
33
- def camelcase_hash_keys(hash)
34
- convert_hash_keys(hash, :camelcase)
35
- end
36
-
37
- private
38
-
39
- def convert_hash_keys(value, method)
40
- case value
41
- when Array
42
- value.map { |v| convert_hash_keys(v, method) }
43
- when Hash
44
- Hash[value.map { |k, v| [send(method, k.to_s).to_sym, convert_hash_keys(v, method)] }]
45
- else
46
- value
47
- end
48
- end
49
- end