eyes_selenium 2.39.1 → 3.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/applitools/capybara.rb +1 -1
- data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +41 -0
- data/lib/applitools/selenium/browser.rb +1 -0
- data/lib/applitools/selenium/capybara/capybara_settings.rb +8 -0
- data/lib/applitools/selenium/capybara/driver.rb +1 -0
- data/lib/applitools/selenium/context_based_scale_provider.rb +36 -0
- data/lib/applitools/selenium/css_translate_position_provider.rb +66 -0
- data/lib/applitools/selenium/driver.rb +159 -44
- data/lib/applitools/selenium/element.rb +100 -8
- data/lib/applitools/selenium/element_position_provider.rb +48 -0
- data/lib/applitools/selenium/eyes.rb +922 -0
- data/lib/applitools/selenium/eyes_target_locator.rb +191 -0
- data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +274 -0
- data/lib/applitools/selenium/frame.rb +24 -0
- data/lib/applitools/selenium/frame_chain.rb +68 -0
- data/lib/applitools/selenium/full_page_capture_algorithm.rb +162 -0
- data/lib/applitools/selenium/keyboard.rb +1 -0
- data/lib/applitools/selenium/match_window_task.rb +0 -197
- data/lib/applitools/selenium/mouse.rb +1 -0
- data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +33 -0
- data/lib/applitools/selenium/nop_region_visibility_strategy.rb +16 -0
- data/lib/applitools/selenium/scroll_position_provider.rb +52 -0
- data/lib/applitools/selenium/takes_screenshot_image_provider.rb +36 -0
- data/lib/applitools/version.rb +1 -1
- data/lib/eyes_selenium.rb +19 -30
- metadata +24 -254
- data/.gitignore +0 -18
- data/.rspec +0 -2
- data/.rubocop.yml +0 -57
- data/.travis.yml +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -13
- data/README.md +0 -38
- data/Rakefile +0 -11
- data/certs/cacert.pem +0 -3557
- data/examples/appium_example_script.rb +0 -59
- data/examples/capybara_example.rb +0 -82
- data/examples/sauce_capybara_example.rb +0 -41
- data/examples/sauce_example.rb +0 -34
- data/examples/simple_test_script.rb +0 -23
- data/examples/watir_test_script.rb +0 -26
- data/eyes_selenium.gemspec +0 -89
- data/lib/applitools/appium_driver.rb +0 -7
- data/lib/applitools/base/batch_info.rb +0 -21
- data/lib/applitools/base/dimension.rb +0 -36
- data/lib/applitools/base/environment.rb +0 -19
- data/lib/applitools/base/image_position.rb +0 -10
- data/lib/applitools/base/mouse_trigger.rb +0 -33
- data/lib/applitools/base/point.rb +0 -34
- data/lib/applitools/base/region.rb +0 -112
- data/lib/applitools/base/server_connector.rb +0 -120
- data/lib/applitools/base/session.rb +0 -15
- data/lib/applitools/base/start_info.rb +0 -34
- data/lib/applitools/base/test_results.rb +0 -28
- data/lib/applitools/base/text_trigger.rb +0 -22
- data/lib/applitools/extensions.rb +0 -17
- data/lib/applitools/eyes.rb +0 -460
- data/lib/applitools/eyes_logger.rb +0 -38
- data/lib/applitools/method_tracer.rb +0 -22
- data/lib/applitools/poltergeist/applitools_compatible.rb +0 -28
- data/lib/applitools/poltergeist/driver.rb +0 -11
- data/lib/applitools/sauce.rb +0 -2
- data/lib/applitools/selenium/match_window_data.rb +0 -28
- data/lib/applitools/selenium_webdriver.rb +0 -8
- data/lib/applitools/utils/image_delta_compressor.rb +0 -148
- data/lib/applitools/utils/image_utils.rb +0 -143
- data/lib/applitools/utils/utils.rb +0 -49
- data/lib/applitools/watir_browser.rb +0 -8
- data/spec/driver_passthrough_spec.rb +0 -68
- data/spec/fixtures/static_test_file.html +0 -15
- 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
|
data/lib/applitools/sauce.rb
DELETED
@@ -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
|