eyes_core 3.0.4

Sign up to get free protection for your applications and to get access to all the features.
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