eyes_core 3.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/eyes_core/extconf.rb +3 -0
- data/ext/eyes_core/eyes_core.c +80 -0
- data/ext/eyes_core/eyes_core.h +24 -0
- data/lib/applitools/capybara.rb +8 -0
- data/lib/applitools/chunky_png/resampling.rb +148 -0
- data/lib/applitools/chunky_png_patch.rb +8 -0
- data/lib/applitools/connectivity/proxy.rb +3 -0
- data/lib/applitools/connectivity/server_connector.rb +118 -0
- data/lib/applitools/core/app_environment.rb +29 -0
- data/lib/applitools/core/app_output.rb +17 -0
- data/lib/applitools/core/app_output_with_screenshot.rb +22 -0
- data/lib/applitools/core/argument_guard.rb +35 -0
- data/lib/applitools/core/batch_info.rb +18 -0
- data/lib/applitools/core/eyes_base.rb +463 -0
- data/lib/applitools/core/eyes_screenshot.rb +35 -0
- data/lib/applitools/core/fixed_cut_provider.rb +61 -0
- data/lib/applitools/core/fixed_scale_provider.rb +14 -0
- data/lib/applitools/core/helpers.rb +18 -0
- data/lib/applitools/core/location.rb +84 -0
- data/lib/applitools/core/match_result.rb +16 -0
- data/lib/applitools/core/match_results.rb +9 -0
- data/lib/applitools/core/match_window_data.rb +34 -0
- data/lib/applitools/core/match_window_task.rb +86 -0
- data/lib/applitools/core/mouse_trigger.rb +39 -0
- data/lib/applitools/core/rectangle_size.rb +46 -0
- data/lib/applitools/core/region.rb +180 -0
- data/lib/applitools/core/screenshot.rb +49 -0
- data/lib/applitools/core/session.rb +15 -0
- data/lib/applitools/core/session_start_info.rb +33 -0
- data/lib/applitools/core/test_results.rb +55 -0
- data/lib/applitools/core/text_trigger.rb +24 -0
- data/lib/applitools/core/trigger.rb +8 -0
- data/lib/applitools/extensions.rb +18 -0
- data/lib/applitools/eyes_logger.rb +45 -0
- data/lib/applitools/images/eyes.rb +204 -0
- data/lib/applitools/images/eyes_images_screenshot.rb +102 -0
- data/lib/applitools/method_tracer.rb +23 -0
- data/lib/applitools/sauce.rb +2 -0
- data/lib/applitools/utils/eyes_selenium_utils.rb +348 -0
- data/lib/applitools/utils/image_delta_compressor.rb +146 -0
- data/lib/applitools/utils/image_utils.rb +146 -0
- data/lib/applitools/utils/utils.rb +68 -0
- data/lib/applitools/version.rb +3 -0
- data/lib/eyes_core.rb +70 -0
- metadata +273 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b1df68d352f994ee3111a658286083efd568c76
|
4
|
+
data.tar.gz: 4b7734495c6f07a2b63ee190f18cac3a064a23be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e568a25ce2eeeddb8ca7fcae76dbe037d963b30e738ce595b6a04a2681ec81aa34fc4fdbbceeddefb66c6ad429c0991fd416a63f1cc78d569c8fcae260a2738
|
7
|
+
data.tar.gz: 07adaf7c6c520f1d762d7ab42a1a3b18f14e4324e32c69fddb346ec6804dc5cde9d1e0a776c9a953b7a0ebbf4f09d2cc87152d7bc6a21e1b94c3186c527af469
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#include "eyes_core.h"
|
2
|
+
|
3
|
+
void Init_eyes_core() {
|
4
|
+
VALUE Applitools = rb_define_module("Applitools");
|
5
|
+
VALUE Resampling = rb_define_module_under(Applitools, "ResamplingFast");
|
6
|
+
rb_define_method(Resampling, "interpolate_cubic", c_interpolate_cubic, 1);
|
7
|
+
rb_define_method(Resampling, "merge_pixels", c_merge_pixels, 1);
|
8
|
+
};
|
9
|
+
|
10
|
+
|
11
|
+
VALUE c_interpolate_cubic(VALUE self, VALUE data) {
|
12
|
+
double t = NUM2DBL(rb_ary_entry(data, 1));
|
13
|
+
BYTE new_r, new_g, new_b, new_a;
|
14
|
+
VALUE p0, p1, p2, p3;
|
15
|
+
|
16
|
+
p0 = NUM2UINT(rb_ary_entry(data, 2));
|
17
|
+
p1 = NUM2UINT(rb_ary_entry(data, 3));
|
18
|
+
p2 = NUM2UINT(rb_ary_entry(data, 4));
|
19
|
+
p3 = NUM2UINT(rb_ary_entry(data, 5));
|
20
|
+
|
21
|
+
new_r = interpolate_char(t, R_BYTE(p0), R_BYTE(p1), R_BYTE(p2), R_BYTE(p3));
|
22
|
+
new_g = interpolate_char(t, G_BYTE(p0), G_BYTE(p1), G_BYTE(p2), G_BYTE(p3));
|
23
|
+
new_b = interpolate_char(t, B_BYTE(p0), B_BYTE(p1), B_BYTE(p2), B_BYTE(p3));
|
24
|
+
new_a = interpolate_char(t, A_BYTE(p0), A_BYTE(p1), A_BYTE(p2), A_BYTE(p3));
|
25
|
+
|
26
|
+
return UINT2NUM(BUILD_PIXEL(new_r, new_g, new_b, new_a));
|
27
|
+
};
|
28
|
+
|
29
|
+
BYTE interpolate_char(double t, BYTE c0, BYTE c1, BYTE c2, BYTE c3) {
|
30
|
+
double a, b, c, d, res;
|
31
|
+
a = - 0.5 * c0 + 1.5 * c1 - 1.5 * c2 + 0.5 * c3;
|
32
|
+
b = c0 - 2.5 * c1 + 2 * c2 - 0.5 * c3;
|
33
|
+
c = 0.5 * c2 - 0.5 * c0;
|
34
|
+
d = c1;
|
35
|
+
res = a * t * t * t + b * t * t + c * t + d + 0.5;
|
36
|
+
if(res < 0) {
|
37
|
+
res = 0;
|
38
|
+
} else if(res > 255) {
|
39
|
+
res = 255;
|
40
|
+
};
|
41
|
+
return (BYTE)(res);
|
42
|
+
};
|
43
|
+
|
44
|
+
VALUE c_merge_pixels(VALUE self, VALUE pixels) {
|
45
|
+
unsigned int i, size, real_colors, acum_r, acum_g, acum_b, acum_a;
|
46
|
+
BYTE new_r, new_g, new_b, new_a;
|
47
|
+
PIXEL pix;
|
48
|
+
|
49
|
+
acum_r = 0;
|
50
|
+
acum_g = 0;
|
51
|
+
acum_b = 0;
|
52
|
+
acum_a = 0;
|
53
|
+
|
54
|
+
new_r = 0;
|
55
|
+
new_g = 0;
|
56
|
+
new_b = 0;
|
57
|
+
new_a = 0;
|
58
|
+
|
59
|
+
size = NUM2UINT(rb_funcall(pixels, rb_intern("size"), 0, Qnil)) - 1;
|
60
|
+
real_colors = 0;
|
61
|
+
|
62
|
+
for(i=1; i < size; i++) {
|
63
|
+
pix = NUM2UINT(rb_ary_entry(pixels, i));
|
64
|
+
if(A_BYTE(pix) != 0) {
|
65
|
+
acum_r += R_BYTE(pix);
|
66
|
+
acum_g += G_BYTE(pix);
|
67
|
+
acum_b += B_BYTE(pix);
|
68
|
+
acum_a += A_BYTE(pix);
|
69
|
+
real_colors += 1;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
if(real_colors > 0) {
|
74
|
+
new_r = (BYTE)(acum_r/real_colors + 0.5);
|
75
|
+
new_g = (BYTE)(acum_g/real_colors + 0.5);
|
76
|
+
new_b = (BYTE)(acum_b/real_colors + 0.5);
|
77
|
+
}
|
78
|
+
new_a = (BYTE)(acum_a/(size - 1) + 0.5);
|
79
|
+
return UINT2NUM(BUILD_PIXEL(new_r, new_g, new_b, new_a));
|
80
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef APPLITOOLS_RESAMPLING_EXT
|
2
|
+
#define APPLITOOLS_RESAMPLING_EXT
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
typedef uint32_t PIXEL; // Pixels use 32 bits unsigned integers
|
6
|
+
typedef unsigned char BYTE; // Bytes use 8 bits unsigned integers
|
7
|
+
|
8
|
+
#define R_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0xff000000) >> 24))
|
9
|
+
#define G_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x00ff0000) >> 16))
|
10
|
+
#define B_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x0000ff00) >> 8))
|
11
|
+
#define A_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x000000ff)))
|
12
|
+
|
13
|
+
#define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a))
|
14
|
+
#define INT8_MULTIPLY(a, b) (((((a) * (b) + 0x80) >> 8) + ((a) * (b) + 0x80)) >> 8)
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
void Init_resampling();
|
19
|
+
|
20
|
+
VALUE c_interpolate_cubic(VALUE,VALUE);
|
21
|
+
VALUE c_merge_pixels(VALUE, VALUE);
|
22
|
+
BYTE interpolate_char(double, BYTE, BYTE, BYTE, BYTE);
|
23
|
+
|
24
|
+
#endif
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Applitools::ChunkyPNG
|
2
|
+
module Resampling
|
3
|
+
def resample_bicubic!(dst_width, dst_height)
|
4
|
+
w_m = [1, width / dst_width].max
|
5
|
+
h_m = [1, height / dst_height].max
|
6
|
+
|
7
|
+
dst_width2 = dst_width * w_m
|
8
|
+
dst_height2 = dst_height * h_m
|
9
|
+
|
10
|
+
points = bicubic_x_points(dst_width2)
|
11
|
+
pixels = Array.new(points.size)
|
12
|
+
|
13
|
+
points.each do |interpolation_data|
|
14
|
+
pixels[interpolation_data[0]] = interpolate_cubic(interpolation_data)
|
15
|
+
end
|
16
|
+
replace_canvas!(dst_width2, height, pixels)
|
17
|
+
|
18
|
+
points = bicubic_y_points(dst_height2)
|
19
|
+
pixels = Array.new(points.size)
|
20
|
+
|
21
|
+
points.each do |interpolation_data|
|
22
|
+
pixels[interpolation_data[0]] = interpolate_cubic(interpolation_data)
|
23
|
+
end
|
24
|
+
replace_canvas!(dst_width2, dst_height2, pixels)
|
25
|
+
|
26
|
+
return self unless w_m * h_m > 1
|
27
|
+
|
28
|
+
points = scale_points(dst_width, dst_height, w_m, h_m)
|
29
|
+
pixels = Array.new(points.size)
|
30
|
+
|
31
|
+
points.each do |merge_data|
|
32
|
+
pixels[merge_data[0]] = merge_pixels(merge_data)
|
33
|
+
end
|
34
|
+
|
35
|
+
replace_canvas!(dst_width, dst_height, pixels)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resample_bicubic(new_width, new_height)
|
39
|
+
dup.resample_bicubic!(new_width, new_height)
|
40
|
+
end
|
41
|
+
|
42
|
+
def bicubic_x_points(dst_width)
|
43
|
+
bicubic_points(width, dst_width, false)
|
44
|
+
end
|
45
|
+
|
46
|
+
def bicubic_y_points(dst_height)
|
47
|
+
bicubic_points(height, dst_height, true)
|
48
|
+
end
|
49
|
+
|
50
|
+
def bicubic_points(src_dimension, dst_dimension, direction, y_start_position = 0)
|
51
|
+
step = (src_dimension - 1).to_f / dst_dimension
|
52
|
+
y_bounds = direction ? width : height
|
53
|
+
raise ArgumentError.new 'Start position value is invalid!' unless y_start_position < y_bounds
|
54
|
+
pixels_size = (y_bounds - y_start_position) * dst_dimension
|
55
|
+
|
56
|
+
steps = Array.new(dst_dimension)
|
57
|
+
residues = Array.new(dst_dimension)
|
58
|
+
|
59
|
+
(0..dst_dimension - 1).each do |i|
|
60
|
+
steps[i] = (i * step).to_i
|
61
|
+
residues[i] = i * step - steps[i]
|
62
|
+
end
|
63
|
+
Enumerator.new(pixels_size) do |enum|
|
64
|
+
(y_start_position..y_bounds - 1).each do |y|
|
65
|
+
line = (direction ? column(y) : row(y))
|
66
|
+
|
67
|
+
line_with_bounds = [imaginable_point(line[0], line[1])] + line + [
|
68
|
+
imaginable_point(line[src_dimension - 2], line[src_dimension - 3]),
|
69
|
+
imaginable_point(line[src_dimension - 1], line[src_dimension - 2])
|
70
|
+
]
|
71
|
+
|
72
|
+
index_y = dst_dimension * y
|
73
|
+
(0..dst_dimension - 1).each do |x|
|
74
|
+
index = direction ? y_bounds * x + y : index_y + x
|
75
|
+
enum << ([index, residues[x]] + line_with_bounds.last(src_dimension + 3 - steps[x]).first(4))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def scale_points(dst_width, dst_height, w_m, h_m)
|
82
|
+
Enumerator.new(dst_width * dst_height) do |enum|
|
83
|
+
(0..dst_height - 1).each do |i|
|
84
|
+
(0..dst_width - 1).each do |j|
|
85
|
+
pixels_to_merge = []
|
86
|
+
(0..h_m - 1).each do |y|
|
87
|
+
y_pos = i * h_m + y
|
88
|
+
(0..w_m - 1).each do |x|
|
89
|
+
x_pos = j * w_m + x
|
90
|
+
pixels_to_merge << get_pixel(x_pos, y_pos)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
index = i * dst_width + j
|
94
|
+
enum << ([index] + pixels_to_merge)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def merge_pixels(merge_data)
|
101
|
+
pixels = merge_data[1..merge_data.size]
|
102
|
+
merged_data = pixels.each_with_object(r: 0, g: 0, b: 0, a: 0, real_colors: 0) do |pixel, result|
|
103
|
+
unless ChunkyPNG::Color.fully_transparent?(pixel)
|
104
|
+
result[:real_colors] += 1
|
105
|
+
[:r, :g, :b].each do |ch|
|
106
|
+
result[ch] += ChunkyPNG::Color.send(ch, pixel)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
result[:a] += ChunkyPNG::Color.a(pixel)
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
113
|
+
r = merged_data[:real_colors] > 0 ? merged_data[:r] / merged_data[:real_colors] : 0
|
114
|
+
g = merged_data[:real_colors] > 0 ? merged_data[:g] / merged_data[:real_colors] : 0
|
115
|
+
b = merged_data[:real_colors] > 0 ? merged_data[:b] / merged_data[:real_colors] : 0
|
116
|
+
a = merged_data[:a] / pixels.size
|
117
|
+
|
118
|
+
ChunkyPNG::Color.rgba(r, g, b, a)
|
119
|
+
end
|
120
|
+
|
121
|
+
def interpolate_cubic(data)
|
122
|
+
result = {}
|
123
|
+
t = data[1]
|
124
|
+
[:r, :g, :b, :a].each do |chan|
|
125
|
+
c0 = ChunkyPNG::Color.send(chan, data[2])
|
126
|
+
c1 = ChunkyPNG::Color.send(chan, data[3])
|
127
|
+
c2 = ChunkyPNG::Color.send(chan, data[4])
|
128
|
+
c3 = ChunkyPNG::Color.send(chan, data[5])
|
129
|
+
|
130
|
+
a = -0.5 * c0 + 1.5 * c1 - 1.5 * c2 + 0.5 * c3
|
131
|
+
b = c0 - 2.5 * c1 + 2 * c2 - 0.5 * c3
|
132
|
+
c = 0.5 * c2 - 0.5 * c0
|
133
|
+
d = c1
|
134
|
+
|
135
|
+
result[chan] = [0, [255, (a * t**3 + b * t**2 + c * t + d).to_i].min].max
|
136
|
+
end
|
137
|
+
ChunkyPNG::Color.rgba(result[:r], result[:g], result[:b], result[:a])
|
138
|
+
end
|
139
|
+
|
140
|
+
def imaginable_point(point1, point2)
|
141
|
+
r = [0, [255, ChunkyPNG::Color.r(point1) << 1].min - ChunkyPNG::Color.r(point2)].max
|
142
|
+
g = [0, [255, ChunkyPNG::Color.g(point1) << 1].min - ChunkyPNG::Color.g(point2)].max
|
143
|
+
b = [0, [255, ChunkyPNG::Color.b(point1) << 1].min - ChunkyPNG::Color.b(point2)].max
|
144
|
+
a = [0, [255, ChunkyPNG::Color.a(point1) << 1].min - ChunkyPNG::Color.a(point2)].max
|
145
|
+
ChunkyPNG::Color.rgba(r, g, b, a)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'oj'
|
3
|
+
Oj.default_options = { :mode => :compat }
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Applitools::Connectivity
|
8
|
+
module ServerConnector
|
9
|
+
extend self
|
10
|
+
|
11
|
+
DEFAULT_SERVER_URL = 'https://eyessdk.applitools.com'.freeze
|
12
|
+
|
13
|
+
SSL_CERT = File.join(File.dirname(File.expand_path(__FILE__)), '../../../certs/cacert.pem').to_s.freeze
|
14
|
+
DEFAULT_TIMEOUT = 300
|
15
|
+
|
16
|
+
API_SESSIONS_RUNNING = '/api/sessions/running/'.freeze
|
17
|
+
|
18
|
+
HTTP_STATUS_CODES = {
|
19
|
+
created: 201,
|
20
|
+
accepted: 202
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
attr_accessor :server_url, :api_key
|
24
|
+
attr_reader :endpoint_url
|
25
|
+
attr_accessor :proxy
|
26
|
+
|
27
|
+
def server_url=(url)
|
28
|
+
@server_url = url.nil? ? DEFAULT_SERVER_URL : url
|
29
|
+
unless @server_url.is_a? String
|
30
|
+
raise Applitools::EyesIllegalArgument.new 'You should pass server url as a String!' \
|
31
|
+
" (#{@server_url.class} is passed)"
|
32
|
+
end
|
33
|
+
@endpoint_url = URI.join(@server_url, API_SESSIONS_RUNNING).to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_proxy(uri, user = nil, password = nil)
|
37
|
+
self.proxy = Proxy.new uri, user, password
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_window(session, data)
|
41
|
+
# Notice that this does not include the screenshot.
|
42
|
+
json_data = Oj.dump(Applitools::Utils.camelcase_hash_keys(data.to_hash)).force_encoding('BINARY')
|
43
|
+
body = [json_data.length].pack('L>') + json_data + data.screenshot
|
44
|
+
Applitools::EyesLogger.debug 'Sending match data...'
|
45
|
+
res = post(URI.join(endpoint_url, session.id.to_s), content_type: 'application/octet-stream', body: body)
|
46
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
47
|
+
Applitools::MatchResult.new Oj.load(res.body)
|
48
|
+
end
|
49
|
+
|
50
|
+
def start_session(session_start_info)
|
51
|
+
res = post(endpoint_url, body: Oj.dump(startInfo:
|
52
|
+
Applitools::Utils.camelcase_hash_keys(session_start_info.to_hash)))
|
53
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
54
|
+
|
55
|
+
response = Oj.load(res.body)
|
56
|
+
Applitools::Session.new(response['id'], response['url'], res.status == HTTP_STATUS_CODES[:created])
|
57
|
+
end
|
58
|
+
|
59
|
+
def stop_session(session, aborted = nil, save = false)
|
60
|
+
res = long_delete(URI.join(endpoint_url, session.id.to_s), query: { aborted: aborted, updateBaseline: save })
|
61
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
62
|
+
|
63
|
+
response = Oj.load(res.body)
|
64
|
+
Applitools::TestResults.new(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
DEFAULT_HEADERS = {
|
70
|
+
'Accept' => 'application/json',
|
71
|
+
'Content-Type' => 'application/json'
|
72
|
+
}.freeze
|
73
|
+
|
74
|
+
LONG_REQUEST_DELAY = 2 # seconds
|
75
|
+
MAX_LONG_REQUEST_DELAY = 10 # seconds
|
76
|
+
LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR = 1.5
|
77
|
+
|
78
|
+
[:get, :post, :delete].each do |method|
|
79
|
+
define_method method do |url, options = {}|
|
80
|
+
request(url, method, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
define_method "long_#{method}" do |url, options = {}|
|
84
|
+
long_request(url, method, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def request(url, method, options = {})
|
89
|
+
Faraday::Connection.new(url, ssl: { ca_file: SSL_CERT }, proxy: @proxy || nil).send(method) do |req|
|
90
|
+
req.options.timeout = DEFAULT_TIMEOUT
|
91
|
+
req.headers = DEFAULT_HEADERS.merge(options[:headers] || {})
|
92
|
+
req.headers['Content-Type'] = options[:content_type] if options.key?(:content_type)
|
93
|
+
req.params = { apiKey: api_key }.merge(options[:query] || {})
|
94
|
+
req.body = options[:body]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def long_request(url, method, options = {})
|
99
|
+
delay = LONG_REQUEST_DELAY
|
100
|
+
(options[:headers] ||= {})['Eyes-Expect'] = '202-accepted'
|
101
|
+
|
102
|
+
loop do
|
103
|
+
# Date should be in RFC 1123 format.
|
104
|
+
options[:headers]['Eyes-Date'] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
105
|
+
|
106
|
+
res = request(url, method, options)
|
107
|
+
return res unless res.status == HTTP_STATUS_CODES[:accepted]
|
108
|
+
|
109
|
+
Applitools::EyesLogger.debug "Still running... retrying in #{delay}s"
|
110
|
+
sleep delay
|
111
|
+
|
112
|
+
delay = [MAX_LONG_REQUEST_DELAY, (delay * LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR).round].min
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
include Applitools::MethodTracer
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Applitools
|
2
|
+
class AppEnvironment
|
3
|
+
attr_accessor :os, :hosting_app, :display_size, :inferred_environment
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@os = options[:os]
|
7
|
+
@hosting_app = options[:hosting_app]
|
8
|
+
@display_size = options[:display_size]
|
9
|
+
@inferred = options[:inferred]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
{
|
14
|
+
os: @os,
|
15
|
+
hosting_app: @hosting_app,
|
16
|
+
display_size: @display_size.to_hash,
|
17
|
+
inferred: @inferred
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
result = ''
|
23
|
+
to_hash.each_pair do |k, v|
|
24
|
+
result << "#{k}: #{v}; "
|
25
|
+
end
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Applitools
|
2
|
+
class AppOutput
|
3
|
+
attr_reader :title, :screenshot64
|
4
|
+
|
5
|
+
def initialize(title, screenshot64)
|
6
|
+
@title = title
|
7
|
+
@screenshot64 = screenshot64
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
{
|
12
|
+
title: title,
|
13
|
+
screenshot64: ''
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|