eyes_calabash 3.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/lib/applitools/calabash/calabash_element.rb +62 -0
  3. data/lib/applitools/calabash/calabash_screenshot_provider.rb +81 -0
  4. data/lib/applitools/calabash/environment_detector.rb +23 -0
  5. data/lib/applitools/calabash/eyes.rb +182 -0
  6. data/lib/applitools/calabash/eyes_calabash_android_screenshot.rb +56 -0
  7. data/lib/applitools/calabash/eyes_calabash_ios_screenshot.rb +28 -0
  8. data/lib/applitools/calabash/eyes_calabash_screenshot.rb +78 -0
  9. data/lib/applitools/calabash/eyes_hooks.rb +51 -0
  10. data/lib/applitools/calabash/eyes_settings.rb +43 -0
  11. data/lib/applitools/calabash/full_page_capture_algorithm.rb +24 -0
  12. data/lib/applitools/calabash/full_page_capture_algorithm/android_scroll_view.rb +85 -0
  13. data/lib/applitools/calabash/full_page_capture_algorithm/base.rb +49 -0
  14. data/lib/applitools/calabash/full_page_capture_algorithm/ios_ui_table_view.rb +148 -0
  15. data/lib/applitools/calabash/os_versions.rb +23 -0
  16. data/lib/applitools/calabash/rspec_matchers.rb +22 -0
  17. data/lib/applitools/calabash/steps/android_eyes_session.rb +35 -0
  18. data/lib/applitools/calabash/steps/android_matchers.rb +34 -0
  19. data/lib/applitools/calabash/steps/eyes_session.rb +40 -0
  20. data/lib/applitools/calabash/steps/eyes_settings.rb +57 -0
  21. data/lib/applitools/calabash/steps/ios_eyes_session.rb +13 -0
  22. data/lib/applitools/calabash/steps/ios_matchers.rb +15 -0
  23. data/lib/applitools/calabash/steps/matchers.rb +69 -0
  24. data/lib/applitools/calabash/target.rb +67 -0
  25. data/lib/applitools/calabash/utils.rb +72 -0
  26. data/lib/applitools/calabash_steps.rb +14 -0
  27. data/lib/applitools/version.rb +3 -0
  28. data/lib/eyes_calabash.rb +23 -0
  29. metadata +128 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a25fa4c2ac323fc0ab8684110da95963f9daee08
4
+ data.tar.gz: c6187489be17efc8972bccb1f4324b3f6351fad0
5
+ SHA512:
6
+ metadata.gz: 9aa7045cce478a18199592bb2fb028c84f7c11a4cbf9c8bc9fe18c6e03de8ad069d5ae48d7fb985fd59df44922ea48ed88fa1d0c5da8fabe51e68116666e759e
7
+ data.tar.gz: e129f92a3324348e1da7c0d79beae7527b193631936ff9b930a55d94fe07512d8d55bca3696a4ec985630a66f572528fd4cb0134fbd97c5d51dbcc42cad50924
@@ -0,0 +1,62 @@
1
+ module Applitools
2
+ module Calabash
3
+ class CalabashElement
4
+ extend Forwardable
5
+ attr_reader :original_element, :element_query
6
+ def_delegators :@original_element, :[], :keys, :values
7
+
8
+ def initialize(element, element_query)
9
+ raise Applitools::EyesIllegalArgument, "Invalid element passed! (#{element})" unless valid_element?(element)
10
+ @original_element = element
11
+ @element_query = element_query
12
+ end
13
+
14
+ def left
15
+ self['rect']['x']
16
+ end
17
+
18
+ alias x left
19
+
20
+ def top
21
+ self['rect']['y']
22
+ end
23
+
24
+ alias y top
25
+
26
+ def width
27
+ self['rect']['width']
28
+ end
29
+
30
+ def height
31
+ self['rect']['height']
32
+ end
33
+
34
+ def location
35
+ Applitools::Location.from_struct(self)
36
+ end
37
+
38
+ def size
39
+ Applitools::RectangleSize.from_struct(self)
40
+ end
41
+
42
+ def region
43
+ Applitools::Region.from_location_size(location, size)
44
+ end
45
+
46
+ def to_s
47
+ @original_element
48
+ end
49
+
50
+ private
51
+
52
+ def valid_element?(element)
53
+ result = true
54
+ result &&= element.is_a?(Hash)
55
+ result &&= element.key?('rect')
56
+ result &&= (rect = element['rect']).is_a?(Hash)
57
+ result &&= (%w(height width y x center_x center_y) - rect.keys).empty?
58
+ result
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ module Applitools
2
+ module Calabash
3
+ class CalabashScreenshotProvider
4
+ WAIT_BEFORE_SCREENSHOT = 1
5
+ attr_reader :density, :context, :debug_screenshot_provider
6
+
7
+ def initialize(_options = {})
8
+ @density = 1
9
+ end
10
+
11
+ def with_density(value)
12
+ @density = value
13
+ self
14
+ end
15
+
16
+ def using_context(value)
17
+ @context = value
18
+ self
19
+ end
20
+
21
+ def with_debug_screenshot_provider(value)
22
+ Applitools::ArgumentGuard.is_a?(
23
+ value,
24
+ 'debug_screenshot_provider',
25
+ Applitools::DebugScreenshotProvider
26
+ )
27
+ @debug_screenshot_provider = value
28
+ self
29
+ end
30
+
31
+ private
32
+
33
+ def save_debug_screenshot(screenshot, suffix)
34
+ suffix = suffix.join('_') if suffix.respond_to? :join
35
+ debug_screenshot_provider.save(screenshot, suffix || '') if debug_screenshot_provider
36
+ end
37
+ end
38
+
39
+ class AndroidScreenshotProvider < CalabashScreenshotProvider
40
+ include Singleton
41
+
42
+ def capture_screenshot(options = {})
43
+ sleep WAIT_BEFORE_SCREENSHOT
44
+ result = nil
45
+ Applitools::Calabash::Utils.using_screenshot(context) do |screenshot_path|
46
+ screenshot = ::ChunkyPNG::Image.from_file(screenshot_path)
47
+ save_debug_screenshot(screenshot, ['original', options[:debug_suffix]])
48
+ viewport_size = Applitools::Calabash::EyesSettings.instance.viewport_size
49
+ screenshot.crop!(0, 0, viewport_size[:width], viewport_size[:height])
50
+ save_debug_screenshot(screenshot, ['cropped', options[:debug_suffix]])
51
+ result = Applitools::Calabash::EyesCalabashAndroidScreenshot.new(
52
+ Applitools::Screenshot.from_image(
53
+ screenshot
54
+ ),
55
+ density: density
56
+ )
57
+ end
58
+ result
59
+ end
60
+ end
61
+
62
+ class IosScreenshotProvider < CalabashScreenshotProvider
63
+ include Singleton
64
+ def capture_screenshot(options = {})
65
+ sleep WAIT_BEFORE_SCREENSHOT
66
+ result = nil
67
+ Applitools::Calabash::Utils.using_screenshot(context) do |screenshot_path|
68
+ screenshot = ::ChunkyPNG::Image.from_file(screenshot_path)
69
+ save_debug_screenshot(screenshot, options[:debug_suffix])
70
+ result = Applitools::Calabash::EyesCalabashIosScreenshot.new(
71
+ Applitools::Screenshot.from_image(
72
+ screenshot
73
+ ),
74
+ scale_factor: density
75
+ )
76
+ end
77
+ result
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ module Applitools
2
+ module Calabash
3
+ module EnvironmentDetector
4
+ extend self
5
+
6
+ def android?
7
+ return true if defined?(::Calabash::Android) == 'constant'
8
+ false
9
+ end
10
+
11
+ def ios?
12
+ return true if defined?(::Calabash::Cucumber) == 'constant'
13
+ false
14
+ end
15
+
16
+ def current_environment
17
+ return :android if android?
18
+ return :ios if ios?
19
+ raise Applitools::EyesError, 'No calabash environment found!'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,182 @@
1
+ module Applitools
2
+ module Calabash
3
+ class Eyes < Applitools::EyesBase
4
+ attr_accessor :device_pixel_ratio, :full_page_capture_algorithm, :base_agent_id, :title,
5
+ :debug_screenshot, :debug_screenshot_provider, :tag_for_debug
6
+ attr_reader :context
7
+
8
+ def initialize(server_url = Applitools::Connectivity::ServerConnector::DEFAULT_SERVER_URL)
9
+ super
10
+ self.base_agent_id = "eyes.calabash.ruby/#{Applitools::VERSION}".freeze
11
+ self.debug_screenshot = true
12
+ self.debug_screenshot_provider = Applitools::DebugScreenshotProvider.new
13
+ .tag_access { tag_for_debug }
14
+ .debug_flag_access { debug_screenshot }
15
+ end
16
+
17
+ def open(options = {})
18
+ Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]
19
+ # options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size]
20
+ open_base options
21
+ end
22
+
23
+ def check(name, target)
24
+ self.tag_for_debug = get_tag_for_debug(name)
25
+ check_it(name, target, Applitools::MatchWindowData.new)
26
+ end
27
+
28
+ def inferred_environment
29
+ return @inferred_environment unless @inferred_environment.nil?
30
+ return unless device_pixel_ratio
31
+ "device pixel ratio: #{device_pixel_ratio}"
32
+ end
33
+
34
+ def add_context(value)
35
+ @context = value
36
+ end
37
+
38
+ def remove_context
39
+ @context = nil
40
+ end
41
+
42
+ def capture_screenshot
43
+ return screenshot_provider.capture_screenshot(debug_suffix: tag_for_debug) unless full_page_capture_algorithm
44
+ full_page_capture_algorithm.get_stitched_region
45
+ end
46
+
47
+ def screenshot_provider
48
+ env = Applitools::Calabash::EnvironmentDetector.current_environment
49
+ case env
50
+ when :android
51
+ Applitools::Calabash::AndroidScreenshotProvider.instance.with_density(device_pixel_ratio)
52
+ .using_context(context)
53
+ .with_debug_screenshot_provider(debug_screenshot_provider)
54
+ when :ios
55
+ Applitools::Calabash::IosScreenshotProvider.instance.with_density(device_pixel_ratio)
56
+ .using_context(context)
57
+ .with_debug_screenshot_provider(debug_screenshot_provider)
58
+ end
59
+ end
60
+
61
+ def check_it(name, target, match_window_data)
62
+ Applitools::ArgumentGuard.not_nil(name, 'name')
63
+
64
+ logger.info 'Full element requested' if target.options[:stitch_content]
65
+
66
+ self.full_page_capture_algorithm = target.options[:stitch_content] &&
67
+ get_full_page_capture_algorithm(target.region_to_check)
68
+
69
+ region_provider = if full_page_capture_algorithm
70
+ entire_screenshot_region
71
+ else
72
+ get_region_provider(target)
73
+ end
74
+
75
+ match_window_data.tag = name
76
+ update_default_settings(match_window_data)
77
+ match_window_data.read_target(target, nil)
78
+
79
+ self.viewport_size = Applitools::Calabash::EyesSettings.instance.viewport_size if viewport_size.nil?
80
+
81
+ if match_window_data.is_a? Applitools::MatchSingleCheckData
82
+ return check_single_base(
83
+ region_provider,
84
+ target.options[:timeout] || Applitools::EyesBase::USE_DEFAULT_TIMEOUT,
85
+ match_window_data
86
+ )
87
+ end
88
+
89
+ check_window_base(
90
+ region_provider,
91
+ target.options[:timeout] || Applitools::EyesBase::USE_DEFAULT_TIMEOUT,
92
+ match_window_data
93
+ )
94
+ end
95
+
96
+ def get_region_provider(target)
97
+ if (region_to_check = target.region_to_check).nil?
98
+ entire_screenshot_region
99
+ else
100
+ region_for_element(region_to_check)
101
+ end
102
+ end
103
+
104
+ def get_app_output_with_screenshot(*args)
105
+ # super do |screenshot|
106
+ # screenshot.scale_it!
107
+ # end
108
+ super(*args, &:scale_it!)
109
+ end
110
+
111
+ def entire_screenshot_region
112
+ Object.new.tap do |prov|
113
+ prov.instance_eval do
114
+ define_singleton_method :region do
115
+ Applitools::Region::EMPTY
116
+ end
117
+
118
+ define_singleton_method :coordinate_type do
119
+ nil
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def region_for_element(region_to_check)
126
+ Object.new.tap do |prov|
127
+ prov.instance_eval do
128
+ define_singleton_method :region do
129
+ case region_to_check
130
+ when Applitools::Calabash::CalabashElement
131
+ region_to_check.region
132
+ when Applitools::Region
133
+ region_to_check
134
+ else
135
+ raise Applitools::EyesError, "Incompatible region type: #{region_to_check.class}"
136
+ end
137
+ # region_to_check.respond_to?(:region) ? region_to_check.region : region_to_check
138
+ end
139
+ define_singleton_method :coordinate_type do
140
+ Applitools::Calabash::EyesCalabashScreenshot::DRIVER
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def vp_size
147
+ viewport_size
148
+ end
149
+
150
+ def vp_size=(value, skip_check_if_open = false)
151
+ unless skip_check_if_open || open?
152
+ raise Applitools::EyesNotOpenException.new 'set_viewport_size: Eyes not open!'
153
+ end
154
+ Applitools::ArgumentGuard.not_nil 'value', value
155
+ @viewport_size = Applitools::RectangleSize.for value
156
+ end
157
+
158
+ alias get_viewport_size vp_size
159
+ alias set_viewport_size vp_size=
160
+
161
+ def get_full_page_capture_algorithm(element)
162
+ logger.info "Trying to get full page capture algorithm for element #{element}..."
163
+ environment = Applitools::Calabash::EnvironmentDetector.current_environment
164
+ element_class = Applitools::Calabash::Utils.send("grub_#{environment}_class_name", context, element).first
165
+ logger.info "Trying to get FullPageCaptureAlgorithm for #{element_class}..."
166
+ algo = Applitools::Calabash::FullPageCaptureAlgorithm.get_algorithm_class(environment, element_class)
167
+ if algo
168
+ logger.info "Using #{algo}"
169
+ algo = algo.new(screenshot_provider, element, debug_screenshot_provider: debug_screenshot_provider)
170
+ else
171
+ logger.info "FullPageCaptureAlgorithm for #{element_class} not found. Continue with :check_region instead"
172
+ end
173
+ algo
174
+ end
175
+
176
+ def get_tag_for_debug(name)
177
+ return "#{current_app_name} #{test_name}" if name.empty?
178
+ "#{current_app_name} #{test_name} - #{name}"
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'eyes_calabash_screenshot'
2
+ module Applitools
3
+ module Calabash
4
+ class EyesCalabashAndroidScreenshot < ::Applitools::Calabash::EyesCalabashScreenshot
5
+ ANDROID_DENSITY = {
6
+ 120 => 0.75,
7
+ 160 => 1,
8
+ 213 => 1.33,
9
+ 240 => 1.5,
10
+ 320 => 2,
11
+ 480 => 3
12
+ }.freeze
13
+
14
+ def initialize(*args)
15
+ options = if args.last.is_a? Hash
16
+ args.pop
17
+ else
18
+ {}
19
+ end
20
+ super(*args)
21
+ @scale_factor = nil
22
+ self.density = options[:density] if options[:density]
23
+ @scale_factor ||= options[:scale_factor]
24
+ @scale_factor = 1 unless @scale_factor
25
+ end
26
+
27
+ def convert_region_location(region, from, to)
28
+ case from
29
+ when DRIVER
30
+ case to
31
+ when SCREENSHOT_AS_IS
32
+ region
33
+ else
34
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
35
+ end
36
+ when CONTEXT_RELATIVE
37
+ case to
38
+ when SCREENSHOT_AS_IS
39
+ region.scale_it!(1.to_f / scale_factor) # !!!!!!
40
+ region
41
+ else
42
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
43
+ end
44
+ else
45
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
46
+ end
47
+ region
48
+ end
49
+
50
+ def density=(value)
51
+ raise Applitools::EyesIllegalArgument, "Unknown density = #{value}" unless ANDROID_DENSITY[value.to_i]
52
+ @scale_factor = ANDROID_DENSITY[value.to_i]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'eyes_calabash_screenshot'
2
+ module Applitools
3
+ module Calabash
4
+ class EyesCalabashIosScreenshot < Applitools::Calabash::EyesCalabashScreenshot
5
+ def convert_region_location(region, from, to)
6
+ case from
7
+ when DRIVER
8
+ case to
9
+ when SCREENSHOT_AS_IS
10
+ region.scale_it!(scale_factor)
11
+ else
12
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
13
+ end
14
+ when CONTEXT_RELATIVE
15
+ case to
16
+ when SCREENSHOT_AS_IS
17
+ region
18
+ else
19
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
20
+ end
21
+ else
22
+ raise Applitools::EyesError, "from: #{from}, to: #{to}"
23
+ end
24
+ region
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,78 @@
1
+ module Applitools
2
+ module Calabash
3
+ class EyesCalabashScreenshot < Applitools::EyesScreenshot
4
+ SCREENSHOT_AS_IS = Applitools::EyesScreenshot::COORDINATE_TYPES[:screenshot_as_is].freeze
5
+ CONTEXT_RELATIVE = Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative].freeze
6
+ DRIVER = 'DRIVER'.freeze
7
+
8
+ attr_reader :scale_factor
9
+
10
+ def initialize(image, options = {})
11
+ super image
12
+ @scale_factor = options[:scale_factor] || 1
13
+ # return if (location = options[:location]).nil?
14
+ # Applitools::ArgumentGuard.is_a? location, 'options[:location]', Applitools::Location
15
+ # @bounds = Applitools::Region.new location.x, location.y, image.width, image.height
16
+ end
17
+
18
+ def sub_screenshot(region, coordinates_type, throw_if_clipped = false, force_nil_if_clipped = false)
19
+ Applitools::ArgumentGuard.not_nil region, 'region'
20
+ Applitools::ArgumentGuard.not_nil coordinates_type, 'coordinates_type'
21
+
22
+ sub_screen_region = intersected_region region, coordinates_type, SCREENSHOT_AS_IS
23
+
24
+ if sub_screen_region.empty? || (throw_if_clipped && !region.size_equals?(sub_screen_region))
25
+ return nil if force_nil_if_clipped
26
+ raise Applitools::OutOfBoundsException, "Region #{sub_screen_region} (#{coordinates_type}) is out of " \
27
+ " screenshot bounds #{bounds}"
28
+ end
29
+
30
+ sub_screenshot_image = Applitools::Screenshot.from_any_image(
31
+ image.crop(
32
+ sub_screen_region.left, sub_screen_region.top, sub_screen_region.width, sub_screen_region.height
33
+ ).to_datastream.to_blob
34
+ )
35
+
36
+ self.class.new sub_screenshot_image, scale_factor: scale_factor
37
+ end
38
+
39
+ def location_in_screenshot(_location, _coordinates_type)
40
+ raise(
41
+ Applitools::EyesError,
42
+ 'Call to :convert_location is prohibited for Applitools::Calabash::EyesCalabashScreenshot'
43
+ )
44
+ end
45
+
46
+ def intersected_region(region, from, to = CONTEXT_RELATIVE)
47
+ Applitools::ArgumentGuard.not_nil region, 'region'
48
+ Applitools::ArgumentGuard.not_nil from, 'coordinates Type (from)'
49
+ return Applitools::Region.new(0, 0, 0, 0) if region.empty?
50
+ intersected_region = convert_region_location region, from, to
51
+ intersected_region.intersect bounds
52
+ intersected_region
53
+ end
54
+
55
+ def convert_location(_location, _from, _to)
56
+ raise(
57
+ Applitools::EyesError,
58
+ 'Call to :convert_location is prohibited for Applitools::Calabash::EyesCalabashScreenshot'
59
+ )
60
+ end
61
+
62
+ abstract_method :convert_region_location, false
63
+
64
+ def scale_it!
65
+ width = (image.width.to_f / scale_factor).to_i
66
+ height = (image.height.to_f / scale_factor).to_i
67
+ image.resample_bicubic!(width, height)
68
+ self
69
+ end
70
+
71
+ private
72
+
73
+ def bounds
74
+ @bounds ||= Applitools::Region.new(0, 0, image.width, image.height)
75
+ end
76
+ end
77
+ end
78
+ end