spreewald 2.4.2 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -3
  3. data/README.md +87 -15
  4. data/Rakefile +10 -5
  5. data/examples/paths.rb +36 -0
  6. data/examples/selectors.rb +28 -0
  7. data/lib/spreewald/all_steps.rb +4 -1
  8. data/lib/spreewald/browser_tab_steps.rb +91 -0
  9. data/lib/spreewald/development_steps.rb +1 -1
  10. data/lib/spreewald/email_steps.rb +3 -1
  11. data/lib/spreewald/session_steps.rb +18 -0
  12. data/lib/spreewald/time_steps.rb +102 -0
  13. data/lib/spreewald/timecop_steps.rb +6 -76
  14. data/lib/spreewald/web_steps.rb +25 -25
  15. data/lib/spreewald_support/driver_info.rb +8 -0
  16. data/lib/spreewald_support/tolerance_for_selenium_sync_issues.rb +9 -3
  17. data/lib/spreewald_support/version.rb +1 -1
  18. data/lib/spreewald_support/web_steps_helpers.rb +17 -0
  19. data/support/step_definition.rb +1 -1
  20. data/support/step_definition_file.rb +2 -0
  21. data/tests/rails-3_capybara-1/Gemfile +2 -1
  22. data/tests/rails-3_capybara-1/Gemfile.lock +3 -1
  23. data/tests/rails-3_capybara-2/Gemfile +2 -1
  24. data/tests/rails-3_capybara-2/Gemfile.lock +3 -1
  25. data/tests/rails-4_capybara-3/Gemfile.lock +1 -1
  26. data/tests/rails-4_capybara-3/features/browser_tab_steps.feature +1 -0
  27. data/tests/rails-4_capybara-3/features/session_steps.feature +1 -0
  28. data/tests/rails-4_capybara-3/features/time_steps.feature +1 -0
  29. data/tests/rails-6_capybara-3/.ruby-version +1 -1
  30. data/tests/rails-6_capybara-3/Gemfile +1 -1
  31. data/tests/rails-6_capybara-3/Gemfile.lock +1 -1
  32. data/tests/rails-6_capybara-3/features/browser_tab_steps.feature +1 -0
  33. data/tests/rails-6_capybara-3/features/session_steps.feature +1 -0
  34. data/tests/rails-6_capybara-3/features/time_steps.feature +1 -0
  35. data/tests/shared/app/controllers/downloads_controller.rb +1 -1
  36. data/tests/shared/app/controllers/static_pages_controller.rb +9 -0
  37. data/tests/shared/app/views/forms/disabled_elements.html.haml +18 -0
  38. data/tests/shared/app/views/mailer/html_email_with_links.haml +2 -0
  39. data/tests/shared/app/views/mailer/text_email_with_links.text.erb +1 -0
  40. data/tests/shared/app/views/static_pages/session_1.haml +1 -0
  41. data/tests/shared/app/views/static_pages/session_2.haml +1 -0
  42. data/tests/shared/app/views/static_pages/session_3.haml +1 -0
  43. data/tests/shared/app/views/static_pages/tab_1.haml +3 -0
  44. data/tests/shared/app/views/static_pages/tab_2.haml +2 -0
  45. data/tests/shared/app/views/static_pages/tab_3.haml +1 -0
  46. data/tests/shared/app/views/static_pages/time.html.haml +2 -0
  47. data/tests/shared/config/routes.rb +9 -2
  48. data/tests/shared/features/shared/browser_tab_steps.feature +103 -0
  49. data/tests/shared/features/shared/session_steps.feature +29 -0
  50. data/tests/shared/features/shared/time_steps.feature +31 -0
  51. data/tests/shared/features/shared/web_steps.feature +8 -2
  52. metadata +37 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c226a047c194764d7645b43c70ab3b07d1a3e7a0d609a4e87dd8baab8e3c3a88
4
- data.tar.gz: ecdfa9f9aa7fe19da7bc3a8e2f7852a80d4d397f4342e873fe89a32276c9f9d0
3
+ metadata.gz: 59c446ad46ae093a7bb103d7e4959dbbc7e29327cbc30e5730aff221f8488e36
4
+ data.tar.gz: e23e4bd833a6234e6ce2b4e6ab104f102708bab13ab54f10aad8b97df08cd39b
5
5
  SHA512:
6
- metadata.gz: d2e322bb7fb70096255ade88045b40408f0d1b0899120f159ea5831b65a40bc5a4d7d2bfb0b9b9ede7ba3f7b8aca0a904129076c7c49cafc7c6780bcd0e329d1
7
- data.tar.gz: 190918ee162ba71606593416c1ae87f1f963a92f316f10f0c7c50f6a3217c7bc1670ccd1412372faa75504d4c0bbff98bd7dd49b88470a3ee162f477052fff5b
6
+ metadata.gz: 8d7b6e7ddd55b75433d7c800045c09f68b7175646de8a3f9ac11a5741ee83ce39c22d8bb5a8b5d9d77b4d22df01eda5de51093de4a76cabf73ae354308b12abc
7
+ data.tar.gz: bc169b566a8878be0ad26b2b1522b960b57772d80d135ffbe4a34476c673282d75b7666d9ccf14a585c229739b3d543620f578c3b59a7e6f62cf036a5df2f90c
@@ -3,8 +3,35 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## 2.8.0
7
+ - Add radio buttons to the `the "..." (field|button|checkbox|radio button) should( not)? be disabled` step.
8
+
9
+ ## 2.7.2
10
+ - Fix the step `I follow the ... link in the email` if the email contains non-HTTP(S) links
11
+
12
+ ## 2.7.1
13
+ - Support RFC-compliant encoding of filenames in `Content-Disposition` header (e.g. send_data), as provided by Rails 6.
14
+
15
+ ## 2.7.0
16
+ - Add a step modifier to control different Capybara sessions: `... in the browser session "..."`. (see issue [#66](https://github.com/makandra/spreewald/issues/66))
17
+
18
+ ## 2.6.0
19
+ - The time steps do now work with the time helpers in ActiveSupport 4.1+ if the timecop gem is not included in the bundle. Please note that the two approaches branch. While ActiveSupport will freeze the time, Timecop will keep it running.
20
+ - The steps in the file `spreewald/timecop.rb` file were moved to `spreewald/time.rb` and importing `spreewald/timecop` directly is deprecated now.
21
+
22
+ ## 2.5.0
23
+ - Add a set of steps to control browser tabs (Selenium only):
24
+ * `I open ... in a new browser tab`
25
+ * `I close the browser tab`
26
+ * `I switch to the new browser tab`
27
+ * `I switch to the previous browser tab`
28
+ * `I may open a new browser tab` (required for the following step)
29
+ * `I should( not)? have opened a new browser tab`
30
+ * `there should be (\d+) browser tabs?`
31
+ * `there should be at least (\d+) browser tabs?`
32
+
6
33
  ## 2.4.2
7
- * Fix the step `I should( not)? see a link labeled "STRING"`, it is now overridable.
34
+ - Fix the step `I should( not)? see a link labeled "STRING"`, it is now overridable.
8
35
 
9
36
  ## 2.4.1
10
37
 
@@ -21,7 +48,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
21
48
 
22
49
  ## 2.2.4
23
50
  - Fix "..." field should have the error "..." test by removing old should syntax
24
- - Add single-line mail step to READNE
51
+ - Add single-line mail step to README
25
52
 
26
53
  ## 2.2.3
27
54
  - Fix 'the window should be titled' step - closes: [#102](https://github.com/makandra/spreewald/issues/102)
@@ -41,7 +68,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
41
68
 
42
69
  ## 2.2.0
43
70
  - Add a new step `I should( not)? see a link labeled "STRING"`.
44
- - Refer to Capybara 3's new flag `Capybara.default_normalize_ws = true` in the READNE,
71
+ - Refer to Capybara 3's new flag `Capybara.default_normalize_ws = true` in the README,
45
72
 
46
73
  ## 2.1.3
47
74
  The `I should(not )? see /REGEXP/` step no longer refuses slashes as part of the regular expression. You can thus match full domains and more.
data/README.md CHANGED
@@ -38,6 +38,7 @@ single files like this:
38
38
  require 'spreewald/email_steps'
39
39
  require 'spreewald/web_steps'
40
40
 
41
+ Have a look at our [recommended Capybara defaults](#recommended-capybara-defaults) as they impact the behavior of many Spreewald steps.
41
42
 
42
43
  ## Spreewald binary
43
44
 
@@ -83,7 +84,7 @@ More info [here](https://makandracards.com/makandra/12139-waiting-for-page-load-
83
84
  Thanks to [cucumber_priority](https://github.com/makandra/cucumber_priority) you can override any step definition from Spreewald with your own pattern. Cucumber will not raise `Cucumber::Ambiguous` if your custom steps collide with a Spreewald step.
84
85
 
85
86
 
86
- ## A note on Capybara 3
87
+ ## Recommended Capybara defaults
87
88
 
88
89
  If you are upgrading from Capybara 2 to Capybara 3, you might [see failing tests](https://github.com/makandra/spreewald/issues/95) with Spreewald steps like `I should see`. This is caused by a breaking change in [Capybara's Finders](https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Node/Finders) that accept a `:text` option. To activate Capybara 2 behavior globally in your project, enable this flag:
89
90
 
@@ -99,6 +100,12 @@ Furthermore, we recommend setting [Capybara's matching strategy](https://github.
99
100
  Capybara.match = :prefer_exact
100
101
  ````
101
102
 
103
+ If you want Spreewald to match fields, links and buttons against the `aria-label` attribute, enable the following global configuration in Capybara 2.8+:
104
+
105
+ ```ruby
106
+ Capybara.enable_aria_label = true
107
+ ```
108
+
102
109
  ## Contributing
103
110
 
104
111
  ### Testing
@@ -132,6 +139,54 @@ the step definitions.
132
139
 
133
140
  ## Steps
134
141
 
142
+ ### browser_tab_steps.rb
143
+
144
+ * **When I open ... in a new browser tab**
145
+
146
+ Opens [the page](https://github.com/makandra/spreewald/blob/master/examples/paths.rb) in a new browser tab and switches to it.
147
+
148
+
149
+ * **When I close the browser tab**
150
+
151
+ Closes the current browser tab and switches back to the first tab.
152
+
153
+
154
+ * **When I switch to the new(ly opened)? browser tab**
155
+
156
+ Waits for the new browser tab to appear, then switches to it.
157
+
158
+
159
+ * **When I switch( back)? to the previous browser tab**
160
+
161
+ Changes the browser context to the second-last browser tab.
162
+
163
+
164
+ * **When I may open a new browser tab**
165
+
166
+ Required for the check whether a new browser tab was opened or not.
167
+
168
+
169
+ * **Then I should( not)? have opened a new browser tab**
170
+
171
+ Example (positive expectation):
172
+
173
+ When I may open a new browser tab
174
+ And I click on "Open link in new browser tab"
175
+ Then I should have opened a new browser tab
176
+
177
+ Example (negative expectation):
178
+
179
+ When I may open a new browser tab
180
+ And I click on "Open link in current browser tab"
181
+ Then I should not have opened a new browser tab
182
+
183
+
184
+ * **Then there should be (\d+) browser tabs?**
185
+
186
+
187
+ * **Then there should be at least (\d+) browser tabs?**
188
+
189
+
135
190
  ### development_steps.rb
136
191
 
137
192
  * **Then it should work...?**
@@ -146,14 +201,14 @@ the step definitions.
146
201
  for its job)
147
202
 
148
203
 
149
- * **AfterStep @slow**
204
+ * **AfterStep @slow-motion**
150
205
 
151
206
  Waits 2 seconds after each step
152
207
 
153
208
 
154
- * **AfterStep @single**
209
+ * **AfterStep @single-step**
155
210
 
156
- Waits for keypress after each step
211
+ Waits for a keypress after each step
157
212
 
158
213
 
159
214
  ### email_steps.rb
@@ -192,6 +247,9 @@ the step definitions.
192
247
 
193
248
  * **When I follow the (first|second|third)? link in the e?mail**
194
249
 
250
+ Please note that this step will only follow HTTP and HTTPS links.
251
+ Other links (such as mailto: or ftp:// links) are ignored.
252
+
195
253
 
196
254
  * **Then no e?mail should have been sent**
197
255
 
@@ -231,6 +289,18 @@ the step definitions.
231
289
  This step is deprecated and will be removed from spreewald. If you still want to use it, copy the code to your project's own steps.
232
290
 
233
291
 
292
+ ### session_steps.rb
293
+
294
+ * **When ... in the browser session "..."**
295
+
296
+ You can append `in the browser session "name"` to any other step to execute
297
+ the step in a different browser session.
298
+
299
+ You may need to update other steps to allow multiple sessions (e.g. your
300
+ authentication steps have to support multiple logged in users).
301
+ More details [here](https://makandracards.com/makandra/474480-how-to-make-a-cucumber-test-work-with-multiple-browser-sessions).
302
+
303
+
234
304
  ### table_steps.rb
235
305
 
236
306
  * **Then I should( not)? see a table with (exactly )?the following rows( in any order)?:?**
@@ -240,11 +310,14 @@ the step definitions.
240
310
  See [this article](https://makandracards.com/makandra/763-cucumber-step-to-match-table-rows-with-capybara) for details.
241
311
 
242
312
 
243
- ### timecop_steps.rb
313
+ ### time_steps.rb
244
314
 
245
- Steps to travel through time using [Timecop](https://github.com/jtrupiano/timecop).
315
+ Steps to travel through time
246
316
 
247
- See [this article](https://makandracards.com/makandra/1222-useful-cucumber-steps-to-travel-through-time-with-timecop) for details.
317
+ This uses [Timecop](https://github.com/jtrupiano/timecop) or Active Support 4.1+ to stub Time.now / Time.current.
318
+ The user is responsible for including one of the two gems.
319
+
320
+ Please note that the two approaches branch. While ActiveSupport will freeze the time, Timecop will keep it running.
248
321
 
249
322
 
250
323
  * **When the (date|time) is "?(\d{4}-\d{2}-\d{2}( \d{1,2}:\d{2})?)"?**
@@ -262,7 +335,7 @@ See [this article](https://makandracards.com/makandra/1222-useful-cucumber-steps
262
335
  Given the time is 13:40
263
336
 
264
337
 
265
- * **When it is (\d+|a|some|a few) (seconds?|minutes?|hours?|days?|weeks?|months?|years?) (later|earlier)**
338
+ * **When it is (\d+|an?|some|a few) (seconds?|minutes?|hours?|days?|weeks?|months?|years?) (later|earlier)**
266
339
 
267
340
  Example:
268
341
 
@@ -270,6 +343,8 @@ See [this article](https://makandracards.com/makandra/1222-useful-cucumber-steps
270
343
  When it is a few hours earlier
271
344
 
272
345
 
346
+
347
+
273
348
  ### web_steps.rb
274
349
 
275
350
  Most of cucumber-rails' original web steps plus a few of our own.
@@ -298,7 +373,7 @@ deprecation notice. Decide for yourself whether you want to use them:
298
373
 
299
374
  * **When ... within ...**
300
375
 
301
- You can append `within [selector]` to any other web step.
376
+ You can append `within [selector]` to any other web step, even multiple times.
302
377
  Be aware that within will only look at the first element that matches.
303
378
  If this is a problem for you following links, you might want to have a look
304
379
  at the 'When I follow "..." inside any "..."'-step.
@@ -608,7 +683,7 @@ deprecation notice. Decide for yourself whether you want to use them:
608
683
 
609
684
  Then "Sponsor" should link to "http://makandra.com/"
610
685
 
611
- Don't forget the trailing slash. Otherwise you'll get the error
686
+ Don't forget the trailing slash. Otherwise you'll get the error
612
687
  expected: /http:\/\/makandra.com(\?[^\/]*)?$/
613
688
  got: "http://makandra.com/" (using =~)
614
689
 
@@ -643,12 +718,9 @@ deprecation notice. Decide for yourself whether you want to use them:
643
718
  * **When I enter "..." into the browser dialog**
644
719
 
645
720
 
646
- * **When I switch to the new tab**
647
-
648
-
649
- * **Then the "..." (field|button|checkbox) should( not)? be disabled**
721
+ * **Then the "..." (field|button|checkbox|radio button) should( not)? be disabled**
650
722
 
651
- Tests that an input, button or checkbox with the given label is disabled.
723
+ Tests that an input, button, checkbox or radio button with the given label is disabled.
652
724
 
653
725
 
654
726
  * **Then the "..." field should( not)? be visible**
data/Rakefile CHANGED
@@ -44,11 +44,16 @@ task :update_readme do
44
44
  require 'support/step_manager'
45
45
  end
46
46
 
47
- readme = File.read('README.md')
48
- start_of_steps_section = readme =~ /^## Steps/
49
- length_of_steps_section = (readme[(start_of_steps_section+1)..-1] =~ /^##[^#]/) || readme.size - start_of_steps_section
50
- readme[start_of_steps_section, length_of_steps_section] = "## Steps\n\n" + StepManager.new('lib/spreewald').to_markdown
51
- File.open(readme_path, 'w') { |f| f.write(readme) }
47
+ readme = File.read(readme_path)
48
+ File.open(readme_path, 'w') do |file|
49
+ start_of_steps_section = readme =~ /^## Steps/
50
+ length_of_steps_section = readme[(start_of_steps_section+1)..-1] =~ /^##[^#]/
51
+ length_of_steps_section ||= readme.size - start_of_steps_section
52
+ step_documentation = StepManager.new('lib/spreewald').to_markdown
53
+ readme[start_of_steps_section, length_of_steps_section] = "## Steps\n\n#{step_documentation}"
54
+
55
+ file.write(readme)
56
+ end
52
57
 
53
58
  system "git diff #{readme_path}"
54
59
  puts '', '> Done (diff applied).'
@@ -13,6 +13,30 @@ module NavigationHelpers
13
13
  when /^the home\s?page$/
14
14
  root_path
15
15
 
16
+ when /^the (page|form) for the (.*?) above$/
17
+ action_prose = Regexp.last_match(1)
18
+ model_prose = Regexp.last_match(2)
19
+ route = "#{(action_prose == 'form') ? 'edit_' : ''}#{model_prose_to_route_segment(model_prose)}_path"
20
+ model = model_prose_to_class(model_prose)
21
+ send(route, model.reorder(:id).last!)
22
+
23
+ when /^the (page|form) for the (.*?) "(.*?)"$/
24
+ action_prose = Regexp.last_match(1)
25
+ model_prose = Regexp.last_match(2)
26
+ identifier = Regexp.last_match(3)
27
+ path_to_show_or_edit(action_prose, model_prose, identifier)
28
+
29
+ when /^the (.*?) (page|form) for "(.*?)"$/
30
+ model_prose = Regexp.last_match(1)
31
+ action_prose = Regexp.last_match(2)
32
+ identifier = Regexp.last_match(3)
33
+ path_to_show_or_edit(action_prose, model_prose, identifier)
34
+
35
+ when /^the (.*?) form$/
36
+ model_prose = Regexp.last_match(1)
37
+ route = "new_#{model_prose_to_route_segment(model_prose)}_path"
38
+ send(route)
39
+
16
40
  else
17
41
  begin
18
42
  page_name =~ /^the (.*) page$/
@@ -24,6 +48,18 @@ module NavigationHelpers
24
48
  end
25
49
  end
26
50
  end
51
+
52
+ private
53
+
54
+ def model_prose_to_class(model_prose)
55
+ model_prose.gsub(' ', '_').classify.constantize
56
+ end
57
+
58
+ def model_prose_to_route_segment(model_prose)
59
+ model_prose = model_prose.downcase
60
+ model_prose.gsub(/[\ \/]/, '_')
61
+ end
62
+
27
63
  end
28
64
 
29
65
  World(NavigationHelpers)
@@ -12,6 +12,27 @@ module HtmlSelectorsHelpers
12
12
  when "the page"
13
13
  "html > body"
14
14
 
15
+ # Auto-mapper for BEM classes and ARIA labels
16
+ #
17
+ # Usage examples:
18
+ # the main menu -> '.main-menu, [aria-label="main menu"]'
19
+ # the item box's header -> '.item-box--header, [aria-label="item box's header"]'
20
+ # the slider's item that is current -> '.slider--item.is-current, [aria-label="slider's item that is current"]'
21
+ when /^the (.*)$/
22
+ match = Regexp.last_match(1)
23
+ match =~ /^(.+?)(?:'s (.+?))?(?: that (.+))?$/
24
+
25
+ bem_selector = '.'
26
+ bem_selector << selectorify(Regexp.last_match(1))
27
+ bem_selector << '--' << selectorify(Regexp.last_match(2)) if Regexp.last_match(2)
28
+ bem_selector << '.' << selectorify(Regexp.last_match(3)) if Regexp.last_match(3)
29
+
30
+ aria_selector = '[aria-label="'
31
+ aria_selector << match.gsub('"', '\\"')
32
+ aria_selector << '"]'
33
+
34
+ [bem_selector, aria_selector].join(', ')
35
+
15
36
  # Add more mappings here.
16
37
  # Here is an example that pulls values out of the Regexp:
17
38
  #
@@ -35,6 +56,13 @@ module HtmlSelectorsHelpers
35
56
  "Now, go and add a mapping in #{__FILE__}"
36
57
  end
37
58
  end
59
+
60
+ private
61
+
62
+ def selectorify(string)
63
+ string.gsub(/ /, '-')
64
+ end
65
+
38
66
  end
39
67
 
40
68
  World(HtmlSelectorsHelpers)
@@ -1,9 +1,12 @@
1
1
  # coding: UTF-8
2
2
  require 'rspec/matchers'
3
3
 
4
+ ALREADY_LOADED_FILES = %w[all_steps timecop_steps]
5
+
4
6
  Dir[File.join(File.dirname(__FILE__), '*_steps.rb')].each do |f|
5
7
  name = File.basename(f, '.rb')
6
- unless name == 'all_steps'
8
+
9
+ unless ALREADY_LOADED_FILES.include?(name)
7
10
  require "spreewald/#{name}"
8
11
  end
9
12
  end
@@ -0,0 +1,91 @@
1
+ Before do
2
+ @previous_browser_tabs_count = nil
3
+ end
4
+
5
+ # Close all but the first browser tab
6
+ After do
7
+ next unless selenium_driver?
8
+
9
+ browser.switch_to.window(browser.window_handles.last)
10
+ while browser.window_handles.size > 1 do
11
+ browser.close
12
+ browser.switch_to.window(browser.window_handles.last)
13
+ end
14
+ end
15
+
16
+ # Opens [the page](https://github.com/makandra/spreewald/blob/master/examples/paths.rb) in a new browser tab and switches to it.
17
+ When /^I open (.+?) in a new browser tab$/ do |page_name|
18
+ require_selenium!
19
+ previous_handles_count = browser.window_handles.size
20
+ relative_target_path = path_to(page_name)
21
+
22
+ page.execute_script "window.open('#{relative_target_path}', '_blank')"
23
+ step "there should be #{previous_handles_count + 1} browser tabs"
24
+ step "I switch to the new browser tab"
25
+ end.overridable
26
+
27
+ # Closes the current browser tab and switches back to the first tab.
28
+ When 'I close the browser tab' do
29
+ require_selenium!
30
+ # Wait for browser tab to close
31
+ previous_handles_count = browser.window_handles.size
32
+ raise 'Cannot close the last remaining browser tab' if previous_handles_count == 1
33
+
34
+ browser.close
35
+ step "there should be #{previous_handles_count - 1} browser tabs"
36
+
37
+ # Closing the current tab causes Selenium to continue using a stale invalid tab handle
38
+ browser.switch_to.window(browser.window_handles.first)
39
+ end.overridable
40
+
41
+ # Waits for the new browser tab to appear, then switches to it.
42
+ When /^I switch to the new(?:ly opened)? browser tab$/ do
43
+ require_selenium!
44
+ step 'there should be at least 2 browser tabs'
45
+ browser.switch_to.window(browser.window_handles.last)
46
+ end.overridable
47
+
48
+ # Changes the browser context to the second-last browser tab.
49
+ When /^I switch(?: back)? to the previous browser tab$/ do
50
+ require_selenium!
51
+ previous_handle = browser.window_handles[-2] # Second last should be the previous browser tab
52
+ previous_handle ||= browser.window_handles.first # Fall back if only one tab is left
53
+ browser.switch_to.window(previous_handle)
54
+ end.overridable
55
+
56
+ # Required for the check whether a new browser tab was opened or not.
57
+ When 'I may open a new browser tab' do
58
+ @previous_browser_tabs_count = browser.window_handles.size
59
+ end.overridable
60
+
61
+ # Example (positive expectation):
62
+ #
63
+ # When I may open a new browser tab
64
+ # And I click on "Open link in new browser tab"
65
+ # Then I should have opened a new browser tab
66
+ #
67
+ # Example (negative expectation):
68
+ #
69
+ # When I may open a new browser tab
70
+ # And I click on "Open link in current browser tab"
71
+ # Then I should not have opened a new browser tab
72
+ Then /^I should( not)? have opened a new browser tab$/ do |negate|
73
+ raise "you need to use the 'I may open a new tab' step beforehand" unless @previous_browser_tabs_count
74
+ expected_browser_tab_count = negate ? @previous_browser_tabs_count : (@previous_browser_tabs_count + 1)
75
+
76
+ step "there should be #{expected_browser_tab_count} browser tabs"
77
+ end.overridable
78
+
79
+ Then /^there should be (\d+) browser tabs?$/ do |expected_browser_tabs|
80
+ patiently do
81
+ actual_browser_tabs = browser.window_handles.size
82
+ expect(actual_browser_tabs).to eq(expected_browser_tabs.to_i), "Expected #{expected_browser_tabs} browser tab to be open, but found #{actual_browser_tabs}"
83
+ end
84
+ end.overridable
85
+
86
+ Then /^there should be at least (\d+) browser tabs?$/ do |expected_browser_tabs|
87
+ patiently do
88
+ actual_browser_tabs = browser.window_handles.size
89
+ expect(actual_browser_tabs).to be >= (expected_browser_tabs.to_i), "Expected at least #{expected_browser_tabs} browser tab to be open, but found #{actual_browser_tabs}"
90
+ end
91
+ end.overridable