capybara 2.13.0 → 2.18.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 (122) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +218 -18
  3. data/README.md +54 -23
  4. data/lib/capybara/config.rb +132 -0
  5. data/lib/capybara/cucumber.rb +1 -0
  6. data/lib/capybara/driver/base.rb +14 -0
  7. data/lib/capybara/dsl.rb +1 -3
  8. data/lib/capybara/helpers.rb +3 -3
  9. data/lib/capybara/minitest/spec.rb +14 -37
  10. data/lib/capybara/minitest.rb +95 -114
  11. data/lib/capybara/node/actions.rb +10 -10
  12. data/lib/capybara/node/base.rb +7 -2
  13. data/lib/capybara/node/element.rb +9 -3
  14. data/lib/capybara/node/finders.rb +92 -18
  15. data/lib/capybara/node/matchers.rb +21 -9
  16. data/lib/capybara/node/simple.rb +5 -0
  17. data/lib/capybara/queries/ancestor_query.rb +25 -0
  18. data/lib/capybara/queries/base_query.rb +12 -3
  19. data/lib/capybara/queries/current_path_query.rb +13 -9
  20. data/lib/capybara/queries/selector_query.rb +62 -23
  21. data/lib/capybara/queries/sibling_query.rb +25 -0
  22. data/lib/capybara/queries/text_query.rb +10 -5
  23. data/lib/capybara/queries/title_query.rb +1 -0
  24. data/lib/capybara/rack_test/browser.rb +13 -5
  25. data/lib/capybara/rack_test/driver.rb +6 -1
  26. data/lib/capybara/rack_test/form.rb +4 -3
  27. data/lib/capybara/rack_test/node.rb +1 -1
  28. data/lib/capybara/rspec/compound.rb +95 -0
  29. data/lib/capybara/rspec/matcher_proxies.rb +45 -0
  30. data/lib/capybara/rspec/matchers.rb +108 -7
  31. data/lib/capybara/rspec.rb +3 -1
  32. data/lib/capybara/selector/filter.rb +13 -41
  33. data/lib/capybara/selector/filter_set.rb +30 -4
  34. data/lib/capybara/selector/filters/base.rb +33 -0
  35. data/lib/capybara/selector/filters/expression_filter.rb +40 -0
  36. data/lib/capybara/selector/filters/node_filter.rb +27 -0
  37. data/lib/capybara/selector/selector.rb +36 -15
  38. data/lib/capybara/selector.rb +63 -42
  39. data/lib/capybara/selenium/driver.rb +177 -33
  40. data/lib/capybara/selenium/node.rb +106 -55
  41. data/lib/capybara/server.rb +6 -5
  42. data/lib/capybara/session/config.rb +114 -0
  43. data/lib/capybara/session/matchers.rb +15 -4
  44. data/lib/capybara/session.rb +178 -65
  45. data/lib/capybara/spec/fixtures/no_extension +1 -0
  46. data/lib/capybara/spec/public/test.js +18 -3
  47. data/lib/capybara/spec/session/accept_alert_spec.rb +9 -1
  48. data/lib/capybara/spec/session/accept_prompt_spec.rb +29 -1
  49. data/lib/capybara/spec/session/all_spec.rb +13 -1
  50. data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
  51. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
  52. data/lib/capybara/spec/session/assert_selector.rb +1 -1
  53. data/lib/capybara/spec/session/assert_text.rb +8 -0
  54. data/lib/capybara/spec/session/assert_title.rb +22 -9
  55. data/lib/capybara/spec/session/attach_file_spec.rb +8 -1
  56. data/lib/capybara/spec/session/check_spec.rb +4 -4
  57. data/lib/capybara/spec/session/choose_spec.rb +2 -2
  58. data/lib/capybara/spec/session/click_button_spec.rb +1 -1
  59. data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
  60. data/lib/capybara/spec/session/click_link_spec.rb +1 -1
  61. data/lib/capybara/spec/session/current_url_spec.rb +3 -3
  62. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  63. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
  64. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
  65. data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -1
  66. data/lib/capybara/spec/session/fill_in_spec.rb +8 -2
  67. data/lib/capybara/spec/session/find_field_spec.rb +1 -0
  68. data/lib/capybara/spec/session/find_spec.rb +8 -6
  69. data/lib/capybara/spec/session/first_spec.rb +10 -5
  70. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  71. data/lib/capybara/spec/session/has_css_spec.rb +11 -0
  72. data/lib/capybara/spec/session/has_current_path_spec.rb +52 -7
  73. data/lib/capybara/spec/session/has_link_spec.rb +4 -4
  74. data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
  75. data/lib/capybara/spec/session/has_select_spec.rb +64 -6
  76. data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
  77. data/lib/capybara/spec/session/has_text_spec.rb +5 -3
  78. data/lib/capybara/spec/session/has_title_spec.rb +4 -2
  79. data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
  80. data/lib/capybara/spec/session/node_spec.rb +50 -26
  81. data/lib/capybara/spec/session/refresh_spec.rb +28 -0
  82. data/lib/capybara/spec/session/reset_session_spec.rb +3 -3
  83. data/lib/capybara/spec/session/select_spec.rb +3 -2
  84. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  85. data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
  86. data/lib/capybara/spec/session/unselect_spec.rb +2 -2
  87. data/lib/capybara/spec/session/visit_spec.rb +56 -1
  88. data/lib/capybara/spec/session/window/become_closed_spec.rb +11 -11
  89. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
  90. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
  91. data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
  92. data/lib/capybara/spec/spec_helper.rb +28 -4
  93. data/lib/capybara/spec/test_app.rb +3 -1
  94. data/lib/capybara/spec/views/form.erb +27 -1
  95. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  96. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  97. data/lib/capybara/spec/views/with_hover.erb +5 -0
  98. data/lib/capybara/spec/views/with_html.erb +33 -2
  99. data/lib/capybara/spec/views/with_js.erb +12 -0
  100. data/lib/capybara/spec/views/with_windows.erb +4 -0
  101. data/lib/capybara/version.rb +1 -1
  102. data/lib/capybara/window.rb +1 -1
  103. data/lib/capybara.rb +102 -124
  104. data/spec/capybara_spec.rb +43 -21
  105. data/spec/dsl_spec.rb +1 -0
  106. data/spec/filter_set_spec.rb +28 -0
  107. data/spec/minitest_spec.rb +9 -1
  108. data/spec/minitest_spec_spec.rb +19 -5
  109. data/spec/per_session_config_spec.rb +67 -0
  110. data/spec/result_spec.rb +20 -0
  111. data/spec/rspec/shared_spec_matchers.rb +148 -44
  112. data/spec/rspec/views_spec.rb +4 -0
  113. data/spec/rspec_matchers_spec.rb +46 -0
  114. data/spec/rspec_spec.rb +77 -0
  115. data/spec/selector_spec.rb +2 -1
  116. data/spec/selenium_spec_chrome.rb +25 -17
  117. data/spec/selenium_spec_firefox.rb +2 -1
  118. data/spec/selenium_spec_marionette.rb +18 -5
  119. data/spec/session_spec.rb +44 -0
  120. data/spec/shared_selenium_session.rb +72 -8
  121. data/spec/spec_helper.rb +4 -0
  122. metadata +55 -8
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ Capybara::SpecHelper.spec '#refresh' do
3
+ it "reload the page" do
4
+ @session.visit('/form')
5
+ expect(@session).to have_select('form_locale', selected: 'English')
6
+ @session.select('Swedish', from: 'form_locale')
7
+ expect(@session).to have_select('form_locale', selected: 'Swedish')
8
+ @session.refresh
9
+ expect(@session).to have_select('form_locale', selected: 'English')
10
+ end
11
+
12
+ it "raises any errors caught inside the server", requires: [:server] do
13
+ quietly { @session.visit("/error") }
14
+ expect do
15
+ @session.refresh
16
+ end.to raise_error(TestApp::TestAppError)
17
+ end
18
+
19
+ it "it reposts" do
20
+ @session.visit('/form')
21
+ @session.select('Sweden', from: 'form_region')
22
+ @session.click_button('awesome')
23
+ expect {
24
+ @session.refresh
25
+ sleep 2
26
+ }.to change{ extract_results(@session)['post_count'] }.by(1)
27
+ end
28
+ end
@@ -39,14 +39,14 @@ Capybara::SpecHelper.spec '#reset_session!' do
39
39
  expect(@session).to have_no_selector :xpath, "/html/body/*", wait: false
40
40
  end
41
41
 
42
- it "handles modals during unload" do
42
+ it "handles modals during unload", requires: [:modals] do
43
43
  @session.visit('/with_unload_alert')
44
44
  expect(@session).to have_selector(:css, 'div')
45
45
  expect { @session.reset_session! }.not_to raise_error
46
46
  expect(@session).to have_no_selector :xpath, "/html/body/*", wait: false
47
47
  end
48
48
 
49
- it "handles already open modals" do
49
+ it "handles already open modals", requires: [:modals] do
50
50
  @session.visit('/with_unload_alert')
51
51
  @session.click_link('Go away')
52
52
  expect { @session.reset_session! }.not_to raise_error
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#reset_session!' do
88
88
  end
89
89
 
90
90
  it "raises configured errors caught inside the server", requires: [:server] do
91
- prev_errors = Capybara.server_errors
91
+ prev_errors = Capybara.server_errors.dup
92
92
 
93
93
  Capybara.server_errors = [LoadError]
94
94
  quietly { @session.visit("/error") }
@@ -24,6 +24,7 @@ Capybara::SpecHelper.spec "#select" do
24
24
  end
25
25
 
26
26
  it "should not allow selecting options where they are the only inexact match if `Capybara.exact_options = true`" do
27
+ expect_any_instance_of(Kernel).to receive(:warn).with(/^DEPRECATED:/)
27
28
  Capybara.exact_options = true
28
29
  expect do
29
30
  @session.select("Mis", from: 'Title')
@@ -85,7 +86,7 @@ Capybara::SpecHelper.spec "#select" do
85
86
 
86
87
  context "with a locator that doesn't exist" do
87
88
  it "should raise an error" do
88
- msg = "Unable to find select box \"does not exist\""
89
+ msg = "Unable to find visible select box \"does not exist\" that is not disabled"
89
90
  expect do
90
91
  @session.select('foo', from: 'does not exist')
91
92
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -94,7 +95,7 @@ Capybara::SpecHelper.spec "#select" do
94
95
 
95
96
  context "with an option that doesn't exist" do
96
97
  it "should raise an error" do
97
- msg = "Unable to find option \"Does not Exist\""
98
+ msg = /^Unable to find visible option "Does not Exist" within/
98
99
  expect do
99
100
  @session.select('Does not Exist', from: 'form_locale')
100
101
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ Capybara::SpecHelper.spec '#sibling' do
3
+ before do
4
+ @session.visit('/with_html')
5
+ end
6
+
7
+ after do
8
+ Capybara::Selector.remove(:monkey)
9
+ end
10
+
11
+ it "should find a prior sibling element using the given locator" do
12
+ el = @session.find(:css, '#mid_sibling')
13
+ expect(el.sibling('//div[@data-pre]')[:id]).to eq('pre_sibling')
14
+ end
15
+
16
+ it "should find a following sibling element using the given locator" do
17
+ el = @session.find(:css, '#mid_sibling')
18
+ expect(el.sibling('//div[@data-post]')[:id]).to eq('post_sibling')
19
+ end
20
+
21
+ it "should raise an error if there are multiple matches" do
22
+ el = @session.find(:css, '#mid_sibling')
23
+ expect { el.sibling('//div') }.to raise_error(Capybara::Ambiguous)
24
+ end
25
+
26
+ context "with css selectors" do
27
+ it "should find the first element using the given locator" do
28
+ el = @session.find(:css, '#mid_sibling')
29
+ expect(el.sibling(:css, '#pre_sibling')).to have_text('Pre Sibling')
30
+ expect(el.sibling(:css, '#post_sibling')).to have_text('Post Sibling')
31
+ end
32
+ end
33
+
34
+ context "with custom selector" do
35
+ it "should use the custom selector" do
36
+ Capybara.add_selector(:data_attribute) do
37
+ xpath { |attr| ".//*[@data-#{attr}]" }
38
+ end
39
+ el = @session.find(:css, '#mid_sibling')
40
+ expect(el.sibling(:data_attribute, 'pre').text).to eq('Pre Sibling')
41
+ expect(el.sibling(:data_attribute, 'post').text).to eq('Post Sibling')
42
+ end
43
+ end
44
+
45
+
46
+ it "should raise ElementNotFound with a useful default message if nothing was found" do
47
+ el = @session.find(:css, '#child')
48
+ expect do
49
+ el.sibling(:xpath, '//div[@id="nosuchthing"]')
50
+ end.to raise_error(Capybara::ElementNotFound, "Unable to find xpath \"//div[@id=\\\"nosuchthing\\\"]\" that is a sibling of visible css \"#child\"")
51
+ end
52
+ end
@@ -69,11 +69,11 @@ Capybara::SpecHelper.spec "#uncheck" do
69
69
  end
70
70
 
71
71
  it "should raise original error when no label available" do
72
- expect { @session.uncheck('form_cars_ariel') }.to raise_error(Capybara::ElementNotFound, 'Unable to find checkbox "form_cars_ariel"')
72
+ expect { @session.uncheck('form_cars_ariel') }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_ariel" that is not disabled')
73
73
  end
74
74
 
75
75
  it "should raise error if not allowed to click label" do
76
- expect{@session.uncheck('form_cars_jaguar', allow_label_click: false)}.to raise_error(Capybara::ElementNotFound, 'Unable to find checkbox "form_cars_jaguar"')
76
+ expect{@session.uncheck('form_cars_jaguar', allow_label_click: false)}.to raise_error(Capybara::ElementNotFound, 'Unable to find visible checkbox "form_cars_jaguar" that is not disabled')
77
77
  end
78
78
  end
79
79
  end
@@ -55,7 +55,7 @@ Capybara::SpecHelper.spec "#unselect" do
55
55
 
56
56
  context "with a locator that doesn't exist" do
57
57
  it "should raise an error" do
58
- msg = "Unable to find select box \"does not exist\""
58
+ msg = "Unable to find visible select box \"does not exist\" that is not disabled"
59
59
  expect do
60
60
  @session.unselect('foo', from: 'does not exist')
61
61
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -64,7 +64,7 @@ Capybara::SpecHelper.spec "#unselect" do
64
64
 
65
65
  context "with an option that doesn't exist" do
66
66
  it "should raise an error" do
67
- msg = "Unable to find option \"Does not Exist\""
67
+ msg = /^Unable to find visible option "Does not Exist" within/
68
68
  expect do
69
69
  @session.unselect('Does not Exist', from: 'form_underwear')
70
70
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -63,8 +63,58 @@ Capybara::SpecHelper.spec '#visit' do
63
63
  expect(URI.parse(@session.current_url).port).to eq(root_uri.port)
64
64
  expect(@session).to have_content('Another World')
65
65
  end
66
+
67
+ it "should add the server port to a visited url if no port specified", requires: [:server] do
68
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:#{@session.server.port}")
69
+ @session.visit("http://www.example.com")
70
+ end
71
+
72
+ it "should not override the visit specified port even if default for scheme", requires: [:server] do
73
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:80")
74
+ @session.visit('http://www.example.com:80')
75
+ end
76
+
77
+ it "should give preference to app_host port if specified", requires: [:server] do
78
+ Capybara.app_host = "http://www.example.com:6666"
79
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:6666/random")
80
+ @session.visit('/random')
81
+ end
82
+
83
+ it "shouldn't override port if no server", requires: [:server] do
84
+ session = Capybara::Session.new(@session.mode, nil)
85
+ expect(session.driver).to receive(:visit).with("http://www.google.com")
86
+ session.visit("http://www.google.com")
87
+ end
88
+
89
+ it "shouldn't override port if no server but app_host is set", requires: [:server] do
90
+ session = Capybara::Session.new(@session.mode, nil)
91
+ Capybara.app_host = "http://www.example.com:6666"
92
+ expect(session.driver).to receive(:visit).with("http://www.google.com")
93
+ session.visit("http://www.google.com")
94
+ end
66
95
  end
67
96
 
97
+ context "when Capybara.always_include_port is false" do
98
+ before(:each) do
99
+ Capybara.always_include_port = false
100
+ end
101
+
102
+ it "shouldn't overwrite port if app_host is set", requires: [:server] do
103
+ session = Capybara::Session.new(@session.mode, nil)
104
+ Capybara.app_host = "http://www.example.com:6666"
105
+ expect(session.driver).to receive(:visit).with("http://www.google.com")
106
+ session.visit("http://www.google.com")
107
+ end
108
+
109
+ it "shouldn't overwrite port if port specfified", requires: [:server] do
110
+ session = Capybara::Session.new(@session.mode, nil)
111
+ Capybara.app_host = "http://www.example.com:6666"
112
+ expect(session.driver).to receive(:visit).with("http://www.google.com:99")
113
+ session.visit("http://www.google.com:99")
114
+ end
115
+ end
116
+
117
+
68
118
  context "without a server", requires: [:server] do
69
119
  it "should respect `app_host`" do
70
120
  serverless_session = Capybara::Session.new(@session.mode, nil)
@@ -96,8 +146,13 @@ Capybara::SpecHelper.spec '#visit' do
96
146
  @session.visit('/times')
97
147
  expect(@session).to have_content('redirection complete')
98
148
  end
99
- end
100
149
 
150
+ it "should work if `app_host` has a trailing /", requires: [:server] do
151
+ Capybara.app_host = "http://#{@session.server.host}:#{@session.server.port}/"
152
+ @session.visit('/')
153
+ expect(@session).to have_content('Hello world!')
154
+ end
155
+ end
101
156
 
102
157
  it "should send no referer when visiting a page" do
103
158
  @session.visit '/get_referer'
@@ -21,18 +21,18 @@ Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
21
21
  @session.execute_script('setTimeout(function(){ window.close(); }, 500);')
22
22
  end
23
23
  Capybara.using_wait_time 0.1 do
24
- expect(@other_window).to become_closed(wait: 2)
24
+ expect(@other_window).to become_closed(wait: 5)
25
25
  end
26
26
  end
27
27
 
28
28
  it 'should raise error if value of :wait is less than timeout' do
29
29
  @session.within_window @other_window do
30
- @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
30
+ @session.execute_script('setTimeout(function(){ window.close(); }, 1000);')
31
31
  end
32
32
  Capybara.using_wait_time 2 do
33
33
  expect do
34
- expect(@other_window).to become_closed(wait: 0.4)
35
- end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> to become closed after 0.4 seconds\Z/)
34
+ expect(@other_window).to become_closed(wait: 0.2)
35
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> to become closed after 0.2 seconds\Z/)
36
36
  end
37
37
  end
38
38
  end
@@ -42,7 +42,7 @@ Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
42
42
  @session.within_window @other_window do
43
43
  @session.execute_script('setTimeout(function(){ window.close(); }, 500);')
44
44
  end
45
- Capybara.using_wait_time 1.5 do
45
+ Capybara.using_wait_time 5 do
46
46
  expect(@other_window).to become_closed
47
47
  end
48
48
  end
@@ -60,25 +60,25 @@ Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
60
60
  end
61
61
 
62
62
  context 'with not_to' do
63
- it 'should raise error if default_max_wait_time is more than timeout' do
63
+ it "should not raise error if window doesn't close before default_max_wait_time" do
64
64
  @session.within_window @other_window do
65
- @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
65
+ @session.execute_script('setTimeout(function(){ window.close(); }, 1000);')
66
66
  end
67
- Capybara.using_wait_time 0.4 do
67
+ Capybara.using_wait_time 0.3 do
68
68
  expect do
69
69
  expect(@other_window).not_to become_closed
70
70
  end
71
71
  end
72
72
  end
73
73
 
74
- it 'should raise error if default_max_wait_time is more than timeout' do
74
+ it 'should raise error if window closes before default_max_wait_time' do
75
75
  @session.within_window @other_window do
76
76
  @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
77
77
  end
78
- Capybara.using_wait_time 1.1 do
78
+ Capybara.using_wait_time 3.1 do
79
79
  expect do
80
80
  expect(@other_window).not_to become_closed
81
- end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> not to become closed after 1.1 seconds\Z/)
81
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> not to become closed after 3.1 seconds\Z/)
82
82
  end
83
83
  end
84
84
  end
@@ -79,7 +79,7 @@ Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do
79
79
  @session.within(:css, '#doesNotOpenWindows') do
80
80
  @session.switch_to_window { @session.title == 'With Windows' }
81
81
  end
82
- end.to raise_error(Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from `within`'s, `within_frame`'s' or `within_window`'s' block.")
82
+ end.to raise_error(Capybara::ScopeError, /`switch_to_window` is not supposed to be invoked/)
83
83
  end
84
84
 
85
85
  it "should raise error when invoked inside `within_frame` as it's nonsense" do
@@ -87,16 +87,18 @@ Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do
87
87
  @session.within_frame('frameOne') do
88
88
  @session.switch_to_window { @session.title == 'With Windows' }
89
89
  end
90
- end.to raise_error(Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from `within`'s, `within_frame`'s' or `within_window`'s' block.")
90
+ end.to raise_error(Capybara::ScopeError, /`switch_to_window` is not supposed to be invoked from/)
91
91
  end
92
92
 
93
- it "should raise error when invoked inside `within_window` as it's nonsense" do
94
- window = (@session.windows - [@window]).first
95
- expect do
96
- @session.within_window window do
97
- @session.switch_to_window { @session.title == 'With Windows' }
98
- end
99
- end.to raise_error(Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from `within`'s, `within_frame`'s' or `within_window`'s' block.")
93
+ it "should allow to be called inside within_window and within_window will still return to original" do
94
+ other_windows = (@session.windows - [@window])
95
+ expect(@session.current_window).to eq(@window)
96
+ @session.within_window other_windows[0] do
97
+ expect(@session.current_window).to eq(other_windows[0])
98
+ @session.switch_to_window other_windows[1]
99
+ expect(@session.current_window).to eq(other_windows[1])
100
+ end
101
+ expect(@session.current_window).to eq(@window)
100
102
  end
101
103
 
102
104
  it "should raise error if window matching block wasn't found" do
@@ -22,12 +22,12 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
22
22
  Capybara.using_wait_time 2 do
23
23
  button=@session.find(:css, '#openWindowWithLongerTimeout')
24
24
  expect do
25
- @session.window_opened_by(wait: 0.8) do
25
+ @session.window_opened_by(wait: 0.3) do
26
26
  button.click
27
27
  end
28
28
  end.to raise_error(Capybara::WindowError, zero_windows_message)
29
29
  end
30
- @session.document.synchronize(2, errors: [Capybara::CapybaraError]) do
30
+ @session.document.synchronize(5, errors: [Capybara::CapybaraError]) do
31
31
  raise Capybara::CapybaraError if @session.windows.size != 2
32
32
  end
33
33
  end
@@ -46,7 +46,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
46
46
  context 'without :wait option' do
47
47
  it 'should raise error if default_max_wait_time is less than timeout' do
48
48
  button = @session.find(:css, '#openWindowWithTimeout')
49
- Capybara.using_wait_time 0.4 do
49
+ Capybara.using_wait_time 0.1 do
50
50
  expect do
51
51
  @session.window_opened_by do
52
52
  button.click
@@ -60,7 +60,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
60
60
 
61
61
  it 'should find window if default_max_wait_time is more than timeout' do
62
62
  button = @session.find(:css, '#openWindowWithTimeout')
63
- Capybara.using_wait_time 1.5 do
63
+ Capybara.using_wait_time 5 do
64
64
  window = @session.window_opened_by do
65
65
  button.click
66
66
  end
@@ -57,10 +57,10 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
57
57
  expect(@session.send(:scopes)).to eq([nil])
58
58
  end
59
59
 
60
- it "should leave correct scopes after execution in case of error" do
60
+ it "should leave correct scopes after execution in case of error", requires: [:windows, :frames] do
61
61
  window = (@session.windows - [@window]).first
62
62
  expect do
63
- @session.within 'html' do
63
+ @session.within_frame 'frameOne' do
64
64
  @session.within_window(window) {}
65
65
  end
66
66
  end.to raise_error(Capybara::ScopeError)
@@ -102,6 +102,31 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
102
102
  expect(@session.title).to eq('With Windows')
103
103
  end
104
104
 
105
+ it "should be able to nest within_window" do
106
+ @session.within_window(->{ @session.title == 'Title of popup two'}) do
107
+ expect(@session).to have_css('#divInPopupTwo')
108
+ @session.within_window(->{ @session.title == 'Title of the first popup'}) do
109
+ expect(@session).to have_css('#divInPopupOne')
110
+ end
111
+ expect(@session).to have_css('#divInPopupTwo')
112
+ expect(@session).not_to have_css('divInPopupOne')
113
+ end
114
+ expect(@session).not_to have_css('#divInPopupTwo')
115
+ expect(@session).not_to have_css('divInPopupOne')
116
+ expect(@session.title).to eq('With Windows')
117
+ end
118
+
119
+ it "should work inside a normal scope" do
120
+ expect(@session).to have_css('#openWindow')
121
+ @session.within(:css, '#scope') do
122
+ @session.within_window(->{ @session.title == 'Title of the first popup'}) do
123
+ expect(@session).to have_css('#divInPopupOne')
124
+ end
125
+ expect(@session).to have_content('My scoped content')
126
+ expect(@session).not_to have_css('#openWindow')
127
+ end
128
+ end
129
+
105
130
  it "should raise error if window wasn't found" do
106
131
  expect do
107
132
  @session.within_window(->{ @session.title == 'Invalid title'}) do
@@ -33,12 +33,15 @@ module Capybara
33
33
  Capybara.default_max_wait_time = 1
34
34
  Capybara.ignore_hidden_elements = true
35
35
  Capybara.exact = false
36
- Capybara.exact_options = false
36
+ # `exact_options` is deprecated - set instancce var directly so we
37
+ # don't generate message every reset
38
+ Capybara.send(:config).session_options.instance_variable_set('@exact_options', false)
37
39
  Capybara.raise_server_errors = true
38
40
  Capybara.visible_text_only = false
39
41
  Capybara.match = :smart
40
42
  Capybara.wait_on_first_by_default = false
41
43
  Capybara.enable_aria_label = false
44
+ reset_threadsafe
42
45
  end
43
46
 
44
47
  def filter(requires, metadata)
@@ -51,7 +54,7 @@ module Capybara
51
54
  end
52
55
  end
53
56
 
54
- def spec(name, options={}, &block)
57
+ def spec(name, *options, &block)
55
58
  @specs ||= []
56
59
  @specs << [name, options, block]
57
60
  end
@@ -64,16 +67,33 @@ module Capybara
64
67
  before do
65
68
  @session = session
66
69
  end
70
+
67
71
  after do
68
72
  @session.reset_session!
69
73
  end
74
+
75
+ before :each, psc: true do
76
+ SpecHelper.reset_threadsafe(true, @session)
77
+ end
78
+
79
+ after psc: true do
80
+ SpecHelper.reset_threadsafe(false, @session)
81
+ end
82
+
70
83
  specs.each do |spec_name, spec_options, block|
71
- describe spec_name, spec_options do
84
+ describe spec_name, *spec_options do
72
85
  class_eval(&block)
73
86
  end
74
87
  end
75
88
  end
76
89
  end
90
+
91
+ def reset_threadsafe(bool = false, session = nil)
92
+ Capybara::Session.class_variable_set(:@@instance_created, false) # Work around limit on when threadsafe can be changed
93
+ Capybara.threadsafe = bool
94
+ session = session.current_session if session.respond_to?(:current_session)
95
+ session.instance_variable_set(:@config, nil) if session
96
+ end
77
97
  end # class << self
78
98
 
79
99
  def silence_stream(stream)
@@ -99,7 +119,11 @@ module Capybara
99
119
  end
100
120
 
101
121
  def marionette?(session)
102
- session.driver.respond_to?(:marionette?) && session.driver.marionette?
122
+ session.driver.respond_to?(:marionette?, true) && session.driver.send(:marionette?)
123
+ end
124
+
125
+ def rspec2?
126
+ !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
103
127
  end
104
128
  end
105
129
  end
@@ -17,6 +17,7 @@ class TestApp < Sinatra::Base
17
17
  set :raise_errors, true
18
18
  set :show_exceptions, false
19
19
 
20
+ @@form_post_count = 0
20
21
  # Also check lib/capybara/spec/views/*.erb for pages not listed here
21
22
 
22
23
  get '/' do
@@ -147,7 +148,8 @@ class TestApp < Sinatra::Base
147
148
  end
148
149
 
149
150
  post '/form' do
150
- '<pre id="results">' + params[:form].to_yaml + '</pre>'
151
+ @@form_post_count += 1
152
+ '<pre id="results">' + params[:form].merge({"post_count" => @@form_post_count}).to_yaml + '</pre>'
151
153
  end
152
154
 
153
155
  post '/upload_empty' do
@@ -235,6 +235,17 @@ New line after and before textarea tag
235
235
  </select>
236
236
  </p>
237
237
 
238
+ <!-- invisible multiselect and options -->
239
+ <p style="display: none">
240
+ <label for="form_dessert">Dessert</label>
241
+ <select name="form[dessert]" id="form_dessert" multiple="multiple">
242
+ <option selected="selected">Pudding</option>
243
+ <option>Lava cake</option>
244
+ <option selected="selected">Tiramisu</option>
245
+ <option>Panna cotta</option>
246
+ </select>
247
+ </p>
248
+
238
249
  <!-- visible select with invisible selected option (which some browsers may treat as visible) -->
239
250
  <p>
240
251
  <label for="form_sorbet">Sorbet</label>
@@ -245,6 +256,17 @@ New line after and before textarea tag
245
256
  </select>
246
257
  </p>
247
258
 
259
+ <!-- visible multiselect with invisible selected options (which some browsers may treat as visible) -->
260
+ <p>
261
+ <label for="form_cake">Cake</label>
262
+ <select name="form[cake]" id="form_cake" multiple="multiple">
263
+ <option>Butter Cake</option>
264
+ <option selected="selected" style="display: none">Chocolate Cake</option>
265
+ <option>Strawberry Cake</option>
266
+ <option selected="selected" style="display: none">Sponge Cake</option>
267
+ </select>
268
+ </p>
269
+
248
270
  <p>
249
271
  <span>First address<span>
250
272
  <label for='address1_street'>Street</label>
@@ -586,6 +608,10 @@ New line after and before textarea tag
586
608
  </label>
587
609
 
588
610
  <label>Confusion
589
- <textarea id="confusion_textarea" class="confusion confusion-textarea"/>
611
+ <textarea id="confusion_textarea" class="confusion confusion-textarea"></textarea>
590
612
  </label>
591
613
 
614
+ <p>
615
+ <label for="asterisk_input">With Asterisk<abbr title="required">*</abbr></label>
616
+ <input id="asterisk_input" type="number"value="2016"/>
617
+ </p>
@@ -0,0 +1,10 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
2
+ <body>
3
+ <div>
4
+ Initial alert page
5
+ </div>
6
+ <script>
7
+ window.alert("Initial alert");
8
+ </script>
9
+ </body>
10
+ </html>
@@ -0,0 +1,17 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ header { height: 45px; position: fixed; top: 0; background-color: red; width: 100%;}
5
+ footer { height: 45px; position: fixed; bottom: 0; background-color: red; width: 100%;}
6
+ #main { margin: 45px;}
7
+ #tall { display: block; height: 2000px;}
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <header>My headers</header>
12
+ <div id="main">
13
+ <div id="tall">A tall block</div>
14
+ <a href="/">Go to root</a>
15
+ </div>
16
+ <footer>My footer</footer>
17
+ </body>
@@ -14,5 +14,10 @@
14
14
  Some text here so the wrapper has size
15
15
  <div class="hidden_until_hover">Here I am</div>
16
16
  </div>
17
+ <div style="display: block; height: 1000px; width: 100%"></div>
18
+ <div class="wrapper scroll_needed" >
19
+ Some text here so the wrapper has size
20
+ <div class="hidden_until_hover">Here I am</div>
21
+ </div>
17
22
  </body>
18
23
  </html>
@@ -13,13 +13,13 @@
13
13
  <span class="number">42</span>
14
14
  <span>Other span</span>
15
15
 
16
- <p class="para" id="first">
16
+ <p class="para" id="first" data-random="abc\def">
17
17
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
18
18
  tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
19
19
  et dolore magna aliqua. Ut enim ad minim veniam,
20
20
  quis nostrud exercitation <a href="/foo" id="foo">ullamco</a> laboris nisi
21
21
  ut aliquip ex ea commodo consequat.
22
- <a href="/with_simple_html" aria-label="Go to simple"><img width="20" height="20" alt="awesome image" /></a>
22
+ <a href="/with_simple_html" aria-label="Go to simple"><img id="first_image" width="20" height="20" alt="awesome image" /></a>
23
23
  </p>
24
24
 
25
25
  <p class="para" id="second">
@@ -119,4 +119,35 @@ banana</textarea>
119
119
 
120
120
  <div>
121
121
  <a id="link_placeholder">No href</a>
122
+ <a id="link_blank_href" href="">Blank href</a>
122
123
  </div>
124
+
125
+ <div id="uppercase" style="text-transform: uppercase;">
126
+ text here
127
+ </div>
128
+
129
+ <div id="ancestor3">
130
+ Ancestor
131
+ <div id="ancestor2">
132
+ Ancestor
133
+ <div id="ancestor1">
134
+ Ancestor
135
+ <div id="child">Child</div>
136
+ </div>
137
+ </div>
138
+ <button id="ancestor_button" type="submit" disabled>
139
+ <img id="button_img" width="20" height="20" alt="button img"/>
140
+ </button>
141
+ </div>
142
+
143
+ <div id="sibling_test">
144
+ <div id="sibling_wrapper" data-pre=true>
145
+ <div id="pre_sibling" data-pre=true>Pre Sibling</div>
146
+ <div id="mid_sibling">Mid Sibling</div>
147
+ <div id="post_sibling" data-post=true>Post Sibling</div>
148
+ </div>
149
+ <div id="other_sibling_wrapper" data-post=true>
150
+ <div data-pre=true>Pre Sibling</div>
151
+ <div data-post=true>Post Sibling</div>
152
+ </div>
153
+ </div>