capybara 2.13.0 → 2.18.0

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