eyes_selenium 1.6.0

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 (42) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +13 -0
  5. data/README.md +82 -0
  6. data/Rakefile +1 -0
  7. data/eyes_selenium.gemspec +30 -0
  8. data/lib/eyes_selenium.rb +42 -0
  9. data/lib/eyes_selenium/capybara.rb +21 -0
  10. data/lib/eyes_selenium/eyes/agent_connecter.rb +39 -0
  11. data/lib/eyes_selenium/eyes/batch_info.rb +14 -0
  12. data/lib/eyes_selenium/eyes/dimension.rb +15 -0
  13. data/lib/eyes_selenium/eyes/driver.rb +146 -0
  14. data/lib/eyes_selenium/eyes/element.rb +78 -0
  15. data/lib/eyes_selenium/eyes/environment.rb +14 -0
  16. data/lib/eyes_selenium/eyes/eyes.rb +182 -0
  17. data/lib/eyes_selenium/eyes/eyes_keyboard.rb +25 -0
  18. data/lib/eyes_selenium/eyes/eyes_mouse.rb +60 -0
  19. data/lib/eyes_selenium/eyes/failure_reports.rb +4 -0
  20. data/lib/eyes_selenium/eyes/match_level.rb +7 -0
  21. data/lib/eyes_selenium/eyes/match_window_data.rb +18 -0
  22. data/lib/eyes_selenium/eyes/match_window_task.rb +71 -0
  23. data/lib/eyes_selenium/eyes/mouse_trigger.rb +19 -0
  24. data/lib/eyes_selenium/eyes/region.rb +22 -0
  25. data/lib/eyes_selenium/eyes/screenshot_taker.rb +18 -0
  26. data/lib/eyes_selenium/eyes/session.rb +14 -0
  27. data/lib/eyes_selenium/eyes/start_info.rb +34 -0
  28. data/lib/eyes_selenium/eyes/target_app.rb +17 -0
  29. data/lib/eyes_selenium/eyes/test_results.rb +21 -0
  30. data/lib/eyes_selenium/eyes/text_trigger.rb +15 -0
  31. data/lib/eyes_selenium/eyes/viewport_size.rb +104 -0
  32. data/lib/eyes_selenium/eyes_logger.rb +18 -0
  33. data/lib/eyes_selenium/utils.rb +5 -0
  34. data/lib/eyes_selenium/utils/image_delta_compressor.rb +147 -0
  35. data/lib/eyes_selenium/version.rb +3 -0
  36. data/spec/capybara_spec.rb +34 -0
  37. data/spec/driver_spec.rb +10 -0
  38. data/spec/eyes_spec.rb +157 -0
  39. data/spec/spec_helper.rb +29 -0
  40. data/spec/test_app.rb +11 -0
  41. data/test_script.rb +21 -0
  42. metadata +226 -0
@@ -0,0 +1,14 @@
1
+ class Applitools::Session
2
+ attr_reader :eyes, :id, :url
3
+ attr_accessor :new_session
4
+ def initialize(session_id, session_url, new_session)
5
+ @new_session = new_session
6
+ @id = session_id
7
+ @url = session_url
8
+ end
9
+
10
+ def new_session?
11
+ self.new_session
12
+ end
13
+ end
14
+
@@ -0,0 +1,34 @@
1
+ class Applitools::StartInfo
2
+
3
+ ATTRIBUTES = %w[ agent_id app_id_or_name ver_id scenario_id_or_name batch_info
4
+ environment application match_level branch_name parent_branch_name ]
5
+
6
+ ATTRIBUTES.each do |attr|
7
+ attr_accessor attr
8
+ end
9
+
10
+ ## add a config file with this stuff, and use hash arg
11
+ def initialize(agent_id, app_id_or_name, scenario_id_or_name, batch_info, environment,
12
+ application, match_level, ver_id=nil, branch_name=nil,
13
+ parent_branch_name=nil)
14
+ @agent_id = agent_id
15
+ @app_id_or_name = app_id_or_name
16
+ @ver_id = ver_id
17
+ @scenario_id_or_name = scenario_id_or_name
18
+ @batch_info = batch_info
19
+ @environment = environment
20
+ @application = application
21
+ @match_level = match_level
22
+ @branch_name = branch_name
23
+ @parent_branch_name = parent_branch_name
24
+ end
25
+
26
+ def to_hash
27
+ {
28
+ AgentId: agent_id, AppIdOrName: app_id_or_name, VerId: ver_id, ScenarioIdOrName: scenario_id_or_name,
29
+ BatchInfo: batch_info.to_hash, Environment: environment.to_hash,
30
+ Application: application.to_hash, matchLevel: match_level, branchName: branch_name,
31
+ parentBranchName: parent_branch_name
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ class Applitools::TargetApp
2
+
3
+ attr_reader :url, :session_id, :user_agent
4
+
5
+ def initialize(url, session_id, user_agent)
6
+ @url = url
7
+ @session_id = session_id
8
+ @user_agent = user_agent
9
+ end
10
+
11
+ def to_hash
12
+ {
13
+ "$type" => "Applitools.Framework.TargetWebDriverApplication, Core",
14
+ url: URI.encode(url.to_s), sessionId: session_id, userAgent: user_agent
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ class Applitools::TestResults
2
+ attr_reader :steps, :matches, :mismatches, :missing, :exact_matches, :strict_matches,
3
+ :context_matches, :layout_matches, :none_matches
4
+ def initialize(steps=0, matches=0, mismatches=0, missing=0,
5
+ exact_matches=0, strict_matches=0, content_matches=0,
6
+ layout_matches=0, none_matches=0)
7
+ @steps = steps
8
+ @matches = matches
9
+ @mismatches = mismatches
10
+ @missing = missing
11
+ @exact_matches = exact_matches
12
+ @strict_matches = strict_matches
13
+ @content_matches = content_matches
14
+ @layout_matches = layout_matches
15
+ @none_matches = none_matches
16
+ end
17
+
18
+ def to_s
19
+ "[ steps: #{steps}, matches: #{matches}, mismatches: #{mismatches}, missing: #{missing} ]"
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ class Applitools::TextTrigger
2
+
3
+ attr_reader :text, :control
4
+
5
+ def initialize(text, control)
6
+ @text = text
7
+ @control = control
8
+ end
9
+
10
+ def to_hash
11
+ {
12
+ "$type" => "Applitools.Models.TextTrigger, Core", text: text, control: control.to_hash
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,104 @@
1
+ class Applitools::ViewportSize
2
+
3
+ GET_VIEWPORT_HEIGHT_JAVASCRIPT_FOR_NORMAL_BROWSER = "return window.innerHeight"
4
+ GET_VIEWPORT_WIDTH_JAVASCRIPT_FOR_NORMAL_BROWSER = "return window.innerWidth"
5
+
6
+ DOCUMENT_CLEAR_SCROLL_BARS_JAVASCRIPT = "var doc = document.documentElement;" +
7
+ "var previousOverflow = doc.style.overflow;"
8
+ DOCUMENT_RESET_SCROLL_BARS_JAVASCRIPT = "doc.style.overflow = previousOverflow;"
9
+ DOCUMENT_RETURN_JAVASCRIPT = "return __applitools_result;"
10
+
11
+ GET_VIEWPORT_WIDTH_JAVASCRIPT_FOR_BAD_BROWSERS =
12
+ DOCUMENT_CLEAR_SCROLL_BARS_JAVASCRIPT +
13
+ "var __applitools_result = doc.clientWidth;" +
14
+ DOCUMENT_RESET_SCROLL_BARS_JAVASCRIPT +
15
+ DOCUMENT_RETURN_JAVASCRIPT
16
+
17
+ GET_VIEWPORT_HEIGHT_JAVASCRIPT_FOR_BAD_BROWSERS =
18
+ DOCUMENT_CLEAR_SCROLL_BARS_JAVASCRIPT +
19
+ "var __applitools_result = doc.clientHeight;" +
20
+ DOCUMENT_RESET_SCROLL_BARS_JAVASCRIPT +
21
+ DOCUMENT_RETURN_JAVASCRIPT
22
+
23
+ attr_reader :driver
24
+ attr_accessor :dimension
25
+ def initialize(driver, dimension=nil)
26
+ @driver = driver
27
+ @dimension = dimension
28
+ end
29
+
30
+ def extract_viewport_width
31
+ begin
32
+ return driver.execute_script(GET_VIEWPORT_WIDTH_JAVASCRIPT_FOR_NORMAL_BROWSER)
33
+ rescue => e
34
+ EyesLogger.info "getViewportSize(): Browser does not support innerWidth (#{e.message})"
35
+ end
36
+
37
+ driver.execute_script(GET_VIEWPORT_WIDTH_JAVASCRIPT_FOR_BAD_BROWSERS)
38
+ end
39
+
40
+ def extract_viewport_height
41
+ begin
42
+ return driver.execute_script(GET_VIEWPORT_HEIGHT_JAVASCRIPT_FOR_NORMAL_BROWSER)
43
+ rescue => e
44
+ EyesLogger.info "getViewportSize(): Browser does not support innerHeight (#{e.message})"
45
+ end
46
+
47
+ driver.execute_script(GET_VIEWPORT_WIDTH_JAVASCRIPT_FOR_BAD_BROWSERS)
48
+ end
49
+
50
+ def extract_viewport_from_browser!
51
+ self.dimension = extract_viewport_from_browser
52
+ end
53
+
54
+ def extract_viewport_from_browser
55
+ width = extract_viewport_width
56
+ height = extract_viewport_height
57
+ Applitools::Dimension.new(width,height)
58
+ rescue => e
59
+ EyesLogger.info "getViewportSize(): only window size is available (#{e.message})"
60
+ width, height = *browser_size.values
61
+ Applitools::Dimension.new(width,height)
62
+ end
63
+ alias_method :viewport_size, :extract_viewport_from_browser
64
+
65
+ def set
66
+ if dimension.is_a?(Hash) && dimension.has_key?(:width) && dimension.has_key?(:height)
67
+ # If dimension is hash of width/height, we convert it to a struct with width/height properties.
68
+ self.dimension = Struct.new(:width, :height).new(dimension[:width], dimension[:height])
69
+ elsif !dimension.respond_to?(:width) || !dimension.respond_to?(:height)
70
+ raise ArgumentError, "expected #{dimension.inspect}:#{dimension.class}" +
71
+ " to respond to #width and #height, or be a hash with these keys"
72
+ end
73
+ self.browser_size = dimension
74
+ verify_size(:browser_size)
75
+ cur_viewport_size = extract_viewport_from_browser
76
+ self.browser_size = Applitools::Dimension.new(
77
+ (2 * browser_size.width) - cur_viewport_size.width,
78
+ (2 * browser_size.height) - cur_viewport_size.height
79
+ )
80
+ verify_size(:viewport_size)
81
+ end
82
+
83
+ def verify_size(to_verify, sleep_time=1, retries=3)
84
+ retries.times do
85
+ sleep(sleep_time)
86
+ cur_size = send(to_verify)
87
+ return if cur_size.values == dimension.values
88
+ end
89
+ EyesLogger.info(err_msg = "Failed setting #{to_verify} to #{dimension.values}")
90
+ raise Applitools::TestFailedError.new(err_msg)
91
+ end
92
+
93
+ def browser_size
94
+ driver.manage.window.size
95
+ end
96
+
97
+ def browser_size=(other)
98
+ self.driver.manage.window.size = other
99
+ end
100
+
101
+ def to_hash
102
+ Hash[dimension.each_pair.to_a]
103
+ end
104
+ end
@@ -0,0 +1,18 @@
1
+ require 'logger'
2
+
3
+ module EyesLogger
4
+ def self.logger(to=STDOUT, level=Logger::INFO)
5
+ return @@log if defined?(@@log)
6
+ @@log = Logger.new(to)
7
+ @@log.level = level
8
+ @@log
9
+ end
10
+
11
+ def self.info(msg)
12
+ logger.info(msg)
13
+ end
14
+
15
+ def self.debug(msg)
16
+ logger.debug(msg)
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module Applitools
2
+ module Utils
3
+ require 'eyes_selenium/utils/image_delta_compressor'
4
+ end
5
+ end
@@ -0,0 +1,147 @@
1
+ =begin
2
+ Applitools SDK class.
3
+
4
+ Provides image compression based on image sequences and deflate
5
+ =end
6
+ require 'oily_png'
7
+ require 'base64'
8
+
9
+ class Applitools::Utils::ImageDeltaCompressor
10
+
11
+ # Compresses the target image based on the source image.
12
+ # +target+:: +ChunkyPNG::Canvas+ The image to compress based on the source image.
13
+ # +target_encoded+:: +Array+ The uncompressed image as binary string.
14
+ # +source+:: +ChunkyPNG::Canvas+ The source image used as a base for compressing the target image.
15
+ # +block_size+:: +Integer+ The width/height of each block.
16
+ # ++
17
+ # Returns +String+ The binary result (either the compressed image, or the uncompressed image if the compression
18
+ # is greater in length)
19
+ def self.compress_by_raw_blocks(target, target_encoded, source, block_size = 10)
20
+ # If we can't compress for any reason, return the target image as is.
21
+ if source.nil? || (source.height != target.height) || (source.width != target.width)
22
+ # Returning a COPY of the target binary string
23
+ return String.new(target_encoded)
24
+ end
25
+
26
+ # Preparing the variables we need
27
+ target_pixels = target.to_rgb_stream.unpack('C*')
28
+ source_pixels = source.to_rgb_stream.unpack('C*')
29
+ image_size = Dimension.new(target.width, target.height)
30
+ block_columns_count = (target.width / block_size) + ((target.width % block_size) == 0 ? 0 : 1)
31
+ block_rows_count = (target.height / block_size) + ((target.height % block_size) == 0 ? 0 : 1)
32
+
33
+ # IMPORTANT: The "-Zlib::MAX_WBITS" tells ZLib to create raw deflate compression, without the
34
+ # "Zlib headers" (this isn't documented in the Zlib page, I found this in some internet forum).
35
+ compressor = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS)
36
+
37
+ compression_result = ''
38
+
39
+ # Writing the data header
40
+ compression_result += @@PREAMBLE.encode("UTF-8")
41
+ compression_result += [@@FORMAT_RAW_BLOCKS].pack("C")
42
+ compression_result += [0].pack("S>") #Source id, Big Endian
43
+ compression_result += [block_size].pack("S>") #Big Endian
44
+
45
+
46
+ # We perform the compression for each channel
47
+ 3.times do |channel|
48
+ block_number = 0
49
+ block_rows_count.times do |block_row|
50
+ block_columns_count.times do |block_column|
51
+ actual_channel_index = 2 - channel # Since the image bytes are BGR and the server expects RGB...
52
+ compare_result = compare_and_copy_block_channel_data(source_pixels, target_pixels, image_size, 3, block_size,
53
+ block_column, block_row, actual_channel_index)
54
+
55
+ if !compare_result.identical
56
+ channel_bytes = compare_result.channel_bytes
57
+ string_to_compress = [channel].pack('C')
58
+ string_to_compress += [block_number].pack('L>')
59
+ string_to_compress += channel_bytes.pack('C*')
60
+ compression_result += compressor.deflate(string_to_compress)
61
+
62
+ # If the compressed data so far is greater than the uncompressed
63
+ # representation of the target, just return the target.
64
+ if compression_result.length > target_encoded.length
65
+ compressor.close
66
+ # Returning a COPY of the target bytes
67
+ return String.new(target_encoded)
68
+ end
69
+ end
70
+ block_number += 1
71
+ end
72
+ end
73
+ end
74
+ # Compress and flush any remaining uncompressed data in the input buffer.
75
+ compression_result += compressor.finish
76
+ compressor.close
77
+ # Returning the compressed result as a byte array
78
+ return compression_result
79
+ end
80
+
81
+
82
+ ### PRIVATE
83
+ private
84
+
85
+ @@PREAMBLE = "applitools"
86
+ @@FORMAT_RAW_BLOCKS = 3
87
+
88
+ Dimension = Struct.new(:width, :height)
89
+ CompareAndCopyBlockChannelDataResult = Struct.new(:identical, :channel_bytes)
90
+
91
+
92
+ # Computes the width and height of the image data contained in the block
93
+ # at the input column and row.
94
+ # +image_size+:: +Dimension+ The image size in pixels.
95
+ # +block_size+:: The block size for which we would like to compute the image data width and height.
96
+ # +block_column+:: The block column index.
97
+ # +block_row+:: The block row index.
98
+ # ++
99
+ # Returns the width and height of the image data contained in the block are returned as a +Dimension+.
100
+ def self.get_actual_block_size(image_size, block_size, block_column, block_row)
101
+ actual_width = [image_size.width - (block_column * block_size), block_size].min
102
+ actual_height = [image_size.height - (block_row * block_size), block_size].min
103
+ Dimension.new(actual_width, actual_height)
104
+ end
105
+
106
+ # Compares a block of pixels between the source and target and copies the target's block bytes to the result.
107
+ # +source_pixels+:: +Array+ of bytes, representing the pixels of the source image.
108
+ # +target_pixels+:: +Array+ of bytes, representing the pixels of the target image.
109
+ # +image_size+:: +Dimension+ The size of the source/target image (remember they must be the same size).
110
+ # +pixel_length+:: +Integer+ The number of bytes composing a pixel
111
+ # +block_size+:: +Integer+ The width/height of the block (block is a square, theoretically).
112
+ # +block_column+:: +Integer+ The block column index (when looking at the images as a grid of blocks).
113
+ # +block_row+:: +Integer+ The block row index (when looking at the images as a grid of blocks).
114
+ # +channel+:: +Integer+ The index of the channel we're comparing.
115
+ # ++
116
+ # Returns +CompareAndCopyBlockChannelDataResult+ object containing a flag saying whether the blocks are identical
117
+ # and a copy of the target block's bytes.
118
+ def self.compare_and_copy_block_channel_data(source_pixels, target_pixels, image_size, pixel_length, block_size,
119
+ block_column, block_row, channel)
120
+ identical = true
121
+
122
+ actual_block_size = get_actual_block_size(image_size, block_size, block_column, block_row)
123
+
124
+ # Getting the actual amount of data in the block we wish to copy
125
+ actual_block_height = actual_block_size.height
126
+ actual_block_width = actual_block_size.width
127
+
128
+ stride = image_size.width * pixel_length
129
+
130
+ # Iterating the block's pixels and comparing the source and target
131
+ channel_bytes = []
132
+ actual_block_height.times do |h|
133
+ offset = (((block_size * block_row) + h) * stride) + (block_size * block_column * pixel_length) + channel
134
+ actual_block_width.times do |w|
135
+ source_byte = source_pixels[offset]
136
+ target_byte = target_pixels[offset]
137
+ if source_byte != target_byte
138
+ identical = false
139
+ end
140
+ channel_bytes << target_byte
141
+ offset += pixel_length
142
+ end
143
+ end
144
+ # Returning the compare-and-copy result
145
+ CompareAndCopyBlockChannelDataResult.new(identical, channel_bytes)
146
+ end
147
+ end
@@ -0,0 +1,3 @@
1
+ module Applitools
2
+ VERSION = "1.6.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'capybara/rspec'
3
+ require 'eyes_selenium/capybara'
4
+ require 'test_app'
5
+
6
+ Capybara.app = TestApp
7
+
8
+ describe Applitools::Eyes do
9
+ init_eyes
10
+ describe 'capybara' do
11
+ before do
12
+ eyes.agent_connector.stub(:stop_session).and_return(double("TestResults", mismatches: 0, missing: 0))
13
+ eyes.agent_connector.stub(:match_window).and_return(true)
14
+ @navigation = double("navigation")
15
+ @navigation.stub(:to)
16
+ end
17
+ it 'should invoke eyes driver when calling capybara visit inside of eyes.open' do
18
+ Capybara.current_driver.should == :rack_test
19
+
20
+ eyes.driver.should_receive(:navigate).and_return(@navigation)
21
+ eyes.test(app_name: 'test', test_name: 'test') do |eyes|
22
+ Capybara.current_driver.should == :selenium
23
+ eyes.driver.should == Capybara.current_session.driver.instance_variable_get(:@browser)
24
+ Capybara.current_session.visit '/'
25
+ eyes.check_window('test')
26
+ end
27
+ Capybara.current_driver.should == :rack_test
28
+ end
29
+ it 'should not invoke eyes driver when calling capybara visit outside of eyes.open' do
30
+ eyes.driver.should_not_receive(:navigate)
31
+ Capybara.current_session.visit '/'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Applitools::Driver do
4
+ describe "#driver" do
5
+ let(:driver) { Applitools::Driver.new }
6
+
7
+ it "has a selenium driver" do
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe Applitools::Eyes do
4
+ init_eyes
5
+
6
+ before do
7
+ eyes.open(app_name: 'test_app', test_name: 'test_app_test')
8
+ fake_session = Applitools::Session.new(1,"http://fake_url.com", false)
9
+ eyes.session = fake_session
10
+ eyes.agent_connector.stub(:stop_session).and_return(double("TestResults", mismatches: 0, missing: 0))
11
+ eyes.agent_connector.stub(:match_window).and_return(true)
12
+ end
13
+
14
+ describe "#new" do
15
+ context "doesn't have an apikey" do
16
+ before do
17
+ Applitools::Eyes.config[:apikey] = nil
18
+ end
19
+ it {expect {Applitools::Eyes.new }.to raise_error }
20
+ after do
21
+ Applitools::Eyes.config[:apikey] = 'YOUR_API_KEY_HERE'
22
+ end
23
+ end
24
+ context "has an apikey url" do
25
+ it {expect {Applitools::Eyes.new }.to_not raise_error }
26
+ end
27
+ end
28
+
29
+ describe ".open" do
30
+ context "already open" do
31
+ it "aborts session" do
32
+ eyes.should_receive(:abort_if_not_closed)
33
+ expect {eyes.open}.to raise_error
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ describe ".abort_if_not_closed" do
40
+ it "closes eyes" do
41
+ expect(eyes).to be_open
42
+ eyes.send(:abort_if_not_closed)
43
+ expect(eyes).to_not be_open
44
+ end
45
+
46
+ it "clears session" do
47
+ eyes.send(:abort_if_not_closed)
48
+ expect(eyes.session).to be_nil
49
+ end
50
+
51
+ it "stops session" do
52
+ expect(eyes.agent_connector).to receive(:stop_session)
53
+ eyes.send(:abort_if_not_closed)
54
+ end
55
+ end
56
+
57
+ describe ".close" do
58
+ it "stops the session" do
59
+ eyes.agent_connector.stub(:stop_session).and_return(double("TestResults", mismatches: 0, missing: 0))
60
+ expect(eyes.agent_connector).to receive(:stop_session)
61
+ expect(eyes.close)
62
+ end
63
+
64
+ context "new_session" do
65
+ before { eyes.session.stub(:new_session?).and_return(true) }
66
+ it "raises NewTestError" do
67
+ eyes.session_start_info = double().as_null_object
68
+ expect { eyes.close }.to raise_error(Applitools::NewTestError)
69
+ end
70
+ end
71
+
72
+ context "not new_session" do
73
+ before do
74
+ eyes.stub(:session_start_info).and_return double().as_null_object
75
+ eyes.session.new_session = false
76
+ end
77
+
78
+ it "raises TestFailedError if results have mismatches" do
79
+ eyes.agent_connector.stub(:stop_session).and_return(double("TestResults", mismatches: 2))
80
+ expect { eyes.close }.to raise_error(Applitools::TestFailedError)
81
+ end
82
+
83
+ it "raises TestFailedError if results have missing" do
84
+ eyes.agent_connector.stub(:stop_session).and_return(double("TestResults", mismatches: 0, missing: 2))
85
+ expect { eyes.close }.to raise_error(Applitools::TestFailedError)
86
+ end
87
+
88
+ it "doesn't raise if old session without mismatches" do
89
+ eyes.stub_chain(:agent_connector, :stop_session).and_return(double("TestResults", mismatches: 0, missing: 0))
90
+ expect(eyes.close).to_not raise_error
91
+ end
92
+ end
93
+ end
94
+
95
+ describe ".check_window" do
96
+ it "raises EyesError if eyes aren't open"
97
+ context "no session" do
98
+ it "creates new session if no session exists"
99
+ it "creates new MatchWindowTask"
100
+ end
101
+
102
+ context "results are as expected" do
103
+ it "doesn't raise"
104
+ end
105
+ context "results are not as expected" do
106
+ context "new session" do
107
+ specify "next check_window will run once on time out"
108
+ end
109
+ context "not new session" do
110
+ context "immediate failreports" do
111
+ it "raises TestFailedError"
112
+ end
113
+
114
+ context "not immediate failreports" do
115
+ it "doesn't raise"
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe ".test" do
122
+ it "opens and closes the eyes around the block execution" do
123
+ eyes.should_receive(:open).ordered
124
+ eyes.should_receive(:close).ordered
125
+ eyes.test { "foo" }
126
+ end
127
+
128
+ it "always closes eyes even if there were exceptions" do
129
+ eyes.stub(:open)
130
+ eyes.should_receive(:abort_if_not_closed)
131
+ eyes.test { raise Applitools::EyesError }
132
+ end
133
+
134
+ context "testing .test" do
135
+ let!(:eyes) do
136
+ Applitools::Eyes.new
137
+ end
138
+ it "catches triggers" do
139
+ eyes.send(:abort_if_not_closed)
140
+ eyes.test(app_name: 'my app1', test_name: 'my test') do |eyes, driver|
141
+ driver.get "http://www.applitools.com"
142
+ eyes.check_window("initial")
143
+ driver.find_element(:css, "li.pricing a").click
144
+ # return false unless user_inputs.map(&:to_hash) == [{"$type"=>"Applitools.Models.MouseTrigger, Core", :mouseAction=>1, :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>555, :top=>0, :height=>69, :width=>50}, :location=>{:latitude=>25, :longitude=>34, :altitude=>nil}}]
145
+ eyes.check_window("pricing page")
146
+ el = driver.find_elements(:css, ".footer-row a").first
147
+ driver.action.double_click(el).perform
148
+ # return false unless user_inputs.map(&:to_hash) == [{"$type"=>"Applitools.Models.MouseTrigger, Core", :mouseAction=>3, :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>0, :top=>0, :height=>115, :width=>376}, :location=>{:x=>115, :y=>376}}]
149
+ eyes.check_window("in forms")
150
+ other_el = driver.find_elements(:css, ".s2member-pro-paypal-registration-email").first
151
+ driver.action.move_to(other_el).click(other_el).send_keys("text1",:space,"text2").perform
152
+ # return false unless user_inputs.map(&:to_hash) == [{"$type"=>"Applitools.Models.MouseTrigger, Core", :mouseAction=>4, :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>0, :top=>0, :height=>230, :width=>331}, :location=>{:x=>230, :y=>331}}, {"$type"=>"Applitools.Models.MouseTrigger, Core", :mouseAction=>1, :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>0, :top=>0, :height=>230, :width=>331}, :location=>{:x=>230, :y=>331}}, {"$type"=>"Applitools.Models.TextTrigger, Core", :text=>"text1", :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>230, :top=>331, :height=>367, :width=>35}}, {"$type"=>"Applitools.Models.TextTrigger, Core", :text=>"", :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>230, :top=>331, :height=>367, :width=>35}}, {"$type"=>"Applitools.Models.TextTrigger, Core", :text=>"text2", :control=>{"$type"=>"Applitools.Utils.Geometry.MutableRegion, Core", :left=>230, :top=>331, :height=>367, :width=>35}}]
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end