eyes_core 3.0.4

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/ext/eyes_core/extconf.rb +3 -0
  3. data/ext/eyes_core/eyes_core.c +80 -0
  4. data/ext/eyes_core/eyes_core.h +24 -0
  5. data/lib/applitools/capybara.rb +8 -0
  6. data/lib/applitools/chunky_png/resampling.rb +148 -0
  7. data/lib/applitools/chunky_png_patch.rb +8 -0
  8. data/lib/applitools/connectivity/proxy.rb +3 -0
  9. data/lib/applitools/connectivity/server_connector.rb +118 -0
  10. data/lib/applitools/core/app_environment.rb +29 -0
  11. data/lib/applitools/core/app_output.rb +17 -0
  12. data/lib/applitools/core/app_output_with_screenshot.rb +22 -0
  13. data/lib/applitools/core/argument_guard.rb +35 -0
  14. data/lib/applitools/core/batch_info.rb +18 -0
  15. data/lib/applitools/core/eyes_base.rb +463 -0
  16. data/lib/applitools/core/eyes_screenshot.rb +35 -0
  17. data/lib/applitools/core/fixed_cut_provider.rb +61 -0
  18. data/lib/applitools/core/fixed_scale_provider.rb +14 -0
  19. data/lib/applitools/core/helpers.rb +18 -0
  20. data/lib/applitools/core/location.rb +84 -0
  21. data/lib/applitools/core/match_result.rb +16 -0
  22. data/lib/applitools/core/match_results.rb +9 -0
  23. data/lib/applitools/core/match_window_data.rb +34 -0
  24. data/lib/applitools/core/match_window_task.rb +86 -0
  25. data/lib/applitools/core/mouse_trigger.rb +39 -0
  26. data/lib/applitools/core/rectangle_size.rb +46 -0
  27. data/lib/applitools/core/region.rb +180 -0
  28. data/lib/applitools/core/screenshot.rb +49 -0
  29. data/lib/applitools/core/session.rb +15 -0
  30. data/lib/applitools/core/session_start_info.rb +33 -0
  31. data/lib/applitools/core/test_results.rb +55 -0
  32. data/lib/applitools/core/text_trigger.rb +24 -0
  33. data/lib/applitools/core/trigger.rb +8 -0
  34. data/lib/applitools/extensions.rb +18 -0
  35. data/lib/applitools/eyes_logger.rb +45 -0
  36. data/lib/applitools/images/eyes.rb +204 -0
  37. data/lib/applitools/images/eyes_images_screenshot.rb +102 -0
  38. data/lib/applitools/method_tracer.rb +23 -0
  39. data/lib/applitools/sauce.rb +2 -0
  40. data/lib/applitools/utils/eyes_selenium_utils.rb +348 -0
  41. data/lib/applitools/utils/image_delta_compressor.rb +146 -0
  42. data/lib/applitools/utils/image_utils.rb +146 -0
  43. data/lib/applitools/utils/utils.rb +68 -0
  44. data/lib/applitools/version.rb +3 -0
  45. data/lib/eyes_core.rb +70 -0
  46. metadata +273 -0
@@ -0,0 +1,35 @@
1
+ module Applitools
2
+ class EyesScreenshot
3
+ extend Forwardable
4
+ extend Applitools::Helpers
5
+
6
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
7
+ attr_accessor :image
8
+
9
+ COORDINATE_TYPES = {
10
+ screenshot_as_is: 'SCREENSHOT_AS_IS',
11
+ context_relative: 'CONTEXT_RELATIVE'
12
+ }.freeze
13
+
14
+ def initialize(screenshot)
15
+ Applitools::ArgumentGuard.is_a? screenshot, 'screenshot', Applitools::Screenshot
16
+ self.image = screenshot
17
+ end
18
+
19
+ abstract_method :sub_screenshot, false
20
+ abstract_method :convert_location, false
21
+ abstract_method :location_in_screenshot, false
22
+ abstract_method :intersected_region, false
23
+
24
+ def convert_region_location(region, from, to)
25
+ Applitools::ArgumentGuard.not_nil region, 'region'
26
+ Applitools::ArgumentGuard.is_a? region, 'region', Applitools::Region
27
+ return Region.new(0, 0, 0, 0) if region.empty?
28
+ Applitools::ArgumentGuard.not_nil from, 'from'
29
+ Applitools::ArgumentGuard.not_nil to, 'to'
30
+
31
+ updated_location = convert_location(region.location, from, to)
32
+ Region.new updated_location.x, updated_location.y, region.width, region.height
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ module Applitools
2
+ # Provides 'cut' method which is used to cut screen shots
3
+ class FixedCutProvider
4
+ # Creates a FixedCutProvider instance
5
+ # @param [Applitools::Region] crop_region Outside space of the region will be cropped
6
+ # @param [Integer] header A top field to crop
7
+ # @param [Integer] left A left field to crop
8
+ # @param [Integer] right A right field to crop
9
+ # @param [Integer] footer A bottom field to crop
10
+ # @example Creates cut provider by a Region
11
+ # Applitools::FixedCutProvider.new Applitools::Region.new(20,20, 300, 300)
12
+ # @example Creates cut provider by a set of fields
13
+ # Applitools::FixedCutProvider.new 20, 20, 300, 300
14
+ def initialize(*args)
15
+ self.region = nil
16
+ self.left = 0
17
+ self.header = 0
18
+ self.right = 0
19
+ self.footer = 0
20
+ case args.length
21
+ when 1
22
+ initialize_by_rectangle(args[0])
23
+ when 4
24
+ initialize_by_fields(*args)
25
+ end
26
+ end
27
+
28
+ def cut(image)
29
+ Applitools::Utils::ImageUtils.cut! image, crop_region(image)
30
+ end
31
+
32
+ private
33
+
34
+ attr_accessor :header, :footer, :left, :right, :region
35
+
36
+ def initialize_by_rectangle(region)
37
+ unless region.is_a? Applitools::Region
38
+ raise Applitools::EyesIllegalArgument.new 'Applitools::Region expected as argument ' /
39
+ " (#{region.class} is passed)."
40
+ end
41
+ self.region = region
42
+ end
43
+
44
+ def initialize_by_fields(header, footer, left, right)
45
+ self.header = header
46
+ self.footer = footer
47
+ self.left = left
48
+ self.right = right
49
+ end
50
+
51
+ def crop_region(image)
52
+ current_height = image.height
53
+ current_width = image.width
54
+ if region.nil?
55
+ Applitools::Region.new(left, header, current_width - left - right, current_height - header - footer)
56
+ else
57
+ region.dup
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,14 @@
1
+ module Applitools
2
+ # @!visibility private
3
+ class FixedScaleProvider
4
+ attr_reader :scale_ratio, :scale_method
5
+ def initialize(scale_ratio, method = :speed)
6
+ @scale_ratio = scale_ratio
7
+ @scale_method = method
8
+ end
9
+
10
+ def scale_image(image)
11
+ Applitools::Utils::ImageUtils.scale!(image, scale_method, scale_ratio)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Applitools
2
+ module Helpers
3
+ def abstract_attr_accessor(*names)
4
+ names.each do |method_name|
5
+ instance_variable_set "@#{method_name}", nil
6
+ abstract_method method_name, true
7
+ abstract_method "#{method_name}=", true
8
+ end
9
+ end
10
+
11
+ def abstract_method(method_name, is_private = true)
12
+ define_method method_name do |*_args|
13
+ raise Applitools::AbstractMethodCalled.new method_name, self
14
+ end
15
+ private method_name if is_private
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,84 @@
1
+ require 'applitools/core/region'
2
+ module Applitools
3
+ class Location
4
+ class << self
5
+ def from_any_attribute(*args)
6
+ if args.size == 2
7
+ new args[0], args[1]
8
+ elsif args.size == 1
9
+ value = args.shift
10
+ from_hash(value) if value.is_a? Hash
11
+ from_array(value) if value.is_a? Array
12
+ from_string(value) if value.is_a? String
13
+ from_struct(value) if value.respond_to?(:x) & value.respond_to?(:y)
14
+ end
15
+ end
16
+
17
+ alias for from_any_attribute
18
+
19
+ def from_hash(value)
20
+ new value[:x], value[:y]
21
+ end
22
+
23
+ def from_array(value)
24
+ new value.shift, value.shift
25
+ end
26
+
27
+ def from_string(value)
28
+ x, y = value.split(/x/)
29
+ new x, y
30
+ end
31
+
32
+ def from_struct(value)
33
+ new value.x, value.y
34
+ end
35
+ end
36
+
37
+ attr_reader :x, :y
38
+
39
+ alias left x
40
+ alias top y
41
+
42
+ def initialize(x, y)
43
+ @x = x
44
+ @y = y
45
+ end
46
+
47
+ TOP_LEFT = Location.new(0, 0)
48
+
49
+ def to_s
50
+ "#{x} x #{y}"
51
+ end
52
+
53
+ def ==(other)
54
+ return super.==(other) unless other.is_a?(Location)
55
+ @x == other.x && @y == other.y
56
+ end
57
+
58
+ alias eql? ==
59
+
60
+ def hash
61
+ @x.hash & @y.hash
62
+ end
63
+
64
+ def to_hash(options = {})
65
+ options[:region] ? { left: left, top: top } : { x: x, y: y }
66
+ end
67
+
68
+ def values
69
+ [x, y]
70
+ end
71
+
72
+ def offset(other)
73
+ @x += other.x
74
+ @y += other.y
75
+ self
76
+ end
77
+
78
+ def offset_negative(other)
79
+ @x -= other.x
80
+ @y -= other.y
81
+ self
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,16 @@
1
+ module Applitools
2
+ class MatchResult
3
+ attr_reader :response_hash
4
+ attr_accessor :screenshot
5
+
6
+ def initialize(response_hash)
7
+ Applitools::ArgumentGuard.hash response_hash, 'response hash', ['asExpected']
8
+ @response_hash = response_hash
9
+ end
10
+
11
+ def as_expected?
12
+ return response_hash['asExpected'] if [TrueClass, FalseClass].include? response_hash['asExpected'].class
13
+ false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Applitools
2
+ class MatchResults
3
+ attr_accessor :screenshot, :window_id, :as_expected
4
+
5
+ def as_expected?
6
+ as_expected
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ module Applitools
2
+ class MatchWindowData
3
+ attr_accessor :app_output, :user_inputs, :tag, :options, :ignore_mismatch
4
+
5
+ def initialize(user_inputs, app_output, tag, ignore_mismatch, options = {})
6
+ self.user_inputs = user_inputs
7
+ self.app_output = app_output
8
+ self.tag = tag
9
+ self.ignore_mismatch = ignore_mismatch
10
+ self.options = options
11
+ end
12
+
13
+ def screenshot
14
+ app_output.screenshot.image.to_blob
15
+ end
16
+
17
+ alias appOutput app_output
18
+ alias userInputs user_inputs
19
+ alias ignoreMismatch ignore_mismatch
20
+
21
+ def to_hash
22
+ ary = [:userInputs, :appOutput, :tag, :ignoreMismatch].map do |field|
23
+ result = send(field)
24
+ result = result.to_hash if result.respond_to? :to_hash
25
+ [field, result] if [String, Symbol, Hash, Array, FalseClass, TrueClass].include? result.class
26
+ end
27
+ Hash[ary]
28
+ end
29
+
30
+ def to_s
31
+ to_hash
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,86 @@
1
+ require 'base64'
2
+ module Applitools
3
+ class MatchWindowTask
4
+ MATCH_INTERVAL = 0.5
5
+ AppOuptut = Struct.new(:title, :screenshot64)
6
+
7
+ attr_reader :logger, :running_session, :default_retry_timeout, :app_output_provider
8
+
9
+ def initialize(logger, running_session, retry_timeout, app_output_provider)
10
+ @logger = logger
11
+ @running_session = running_session
12
+ # @driver = driver
13
+ @default_retry_timeout = retry_timeout
14
+ @app_output_provider = app_output_provider
15
+
16
+ ArgumentGuard.not_nil logger, 'logger'
17
+ ArgumentGuard.not_nil running_session, 'running_session'
18
+ ArgumentGuard.not_nil app_output_provider, 'app_output_provider'
19
+ ArgumentGuard.greater_than_or_equal_to_zero retry_timeout, 'retry_timeout'
20
+
21
+ return if app_output_provider.respond_to? :app_output
22
+ raise Applitools::EyesIllegalArgument.new 'MatchWindowTask.new(): app_output_provider doesn\'t' /
23
+ ' respond to :app_output'
24
+ end
25
+
26
+ def match_window(options = {})
27
+ user_inputs = options[:user_inputs]
28
+ last_screenshot = options[:last_screenshot]
29
+ region_provider = options[:region_provider]
30
+ tag = options[:tag]
31
+ should_match_window_run_once_on_timeout = options[:should_match_window_run_once_on_timeout]
32
+ ignore_mismatch = options[:ignore_mismatch]
33
+ retry_timeout = options[:retry_timeout]
34
+
35
+ retry_timeout = default_retry_timeout if retry_timeout < 0
36
+
37
+ logger.info "retry_timeout = #{retry_timeout}"
38
+ elapsed_time_start = Time.now
39
+
40
+ if retry_timeout.zero? || should_match_window_run_once_on_timeout
41
+ sleep retry_timeout if should_match_window_run_once_on_timeout
42
+ app_output = app_output_provider.app_output region_provider, last_screenshot
43
+ match_result = perform_match user_inputs: user_inputs, app_output: app_output, tag: tag,
44
+ ignore_mismatch: ignore_mismatch
45
+ else
46
+ app_output = app_output_provider.app_output region_provider, last_screenshot
47
+ start = Time.now
48
+ match_result = perform_match user_inputs: user_inputs, app_output: app_output, tag: tag, ignore_mismatch: true
49
+ retry_time = Time.now - start
50
+
51
+ while retry_time < retry_timeout && !match_result.as_expected?
52
+ sleep MATCH_INTERVAL
53
+ app_output = app_output_provider.app_output region_provider, last_screenshot
54
+ match_result = perform_match user_inputs: user_inputs, app_output: app_output, tag: tag, ignore_mismatch: true
55
+ retry_time = Time.now - start
56
+ end
57
+
58
+ unless match_result.as_expected?
59
+ app_output = app_output_provider.app_output region_provider, last_screenshot
60
+ match_result = perform_match user_inputs: user_inputs, app_output: app_output, tag: tag,
61
+ ignore_mismatch: ignore_mismatch
62
+ end
63
+ end
64
+
65
+ elapsed_time = (Time.now - elapsed_time_start) / 1000
66
+
67
+ logger.info "Completed in #{format('%.2f', elapsed_time)} seconds"
68
+
69
+ match_result.screenshot = app_output.screenshot
70
+ match_result
71
+ end
72
+
73
+ private
74
+
75
+ def perform_match(options = {})
76
+ user_inputs = options[:user_inputs]
77
+ app_output = options[:app_output]
78
+ tag = options[:tag]
79
+ ignore_mismatch = options[:ignore_mismatch]
80
+ data = Applitools::MatchWindowData.new user_inputs, app_output, tag, ignore_mismatch,
81
+ tag: tag, user_inputs: user_inputs, ignore_mismatch: ignore_mismatch, ignore_match: false,
82
+ force_mistmatch: false, force_match: false
83
+ Applitools::Connectivity::ServerConnector.match_window running_session, data
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,39 @@
1
+ require 'applitools/core/trigger'
2
+ module Applitools
3
+ class MouseTrigger < Trigger
4
+ TRIGGER_TYPE = :Mouse
5
+
6
+ ##
7
+ # A hash contains available mouse actions: +:click+, +:right_click+, +:double_click+, +:move+, +:down+, +:up+
8
+
9
+ MOUSE_ACTION = {
10
+ click: 'Click',
11
+ right_click: 'RightClick',
12
+ double_click: 'DoubleClick',
13
+ move: 'Move',
14
+ down: 'Down',
15
+ up: 'Up'
16
+ }.freeze
17
+
18
+ attr_reader :mouse_action, :control, :location
19
+
20
+ def initialize(mouse_action, control, location)
21
+ @mouse_action = mouse_action
22
+ @control = control
23
+ @location = location
24
+ end
25
+
26
+ def to_hash
27
+ {
28
+ triggerType: trigger_type,
29
+ mouseAction: MOUSE_ACTION[mouse_action],
30
+ control: control.to_hash,
31
+ location: location.to_hash
32
+ }
33
+ end
34
+
35
+ def to_s
36
+ "#{mouse_action} [#{control}] #{location.x}, #{location.y}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ module Applitools
2
+ RectangleSize = Struct.new(:width, :height) do
3
+ class << self
4
+ def from_any_argument(value)
5
+ return from_string(value) if value.is_a? String
6
+ return from_hash(value) if value.is_a? Hash
7
+ return from_struct(value) if value.respond_to?(:width) & value.respond_to?(:height)
8
+ return value if value.is_a? self
9
+ nil
10
+ end
11
+
12
+ alias_method :for, :from_any_argument
13
+
14
+ def from_string(value)
15
+ width, height = value.split(/x/)
16
+ new width, height
17
+ end
18
+
19
+ def from_hash(value)
20
+ new value[:width], value[:height]
21
+ end
22
+
23
+ def from_struct(value)
24
+ new value.width, value.height
25
+ end
26
+ end
27
+
28
+ def to_s
29
+ "#{width}x#{height}"
30
+ end
31
+
32
+ def -(other)
33
+ self.width = width - other.width
34
+ self.height = height - other.height
35
+ self
36
+ end
37
+
38
+ def +(other)
39
+ self.width = width + other.width
40
+ self.height = height + other.height
41
+ self
42
+ end
43
+
44
+ alias_method :to_hash, :to_h
45
+ end
46
+ end