capybara 3.9.0 → 3.10.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +15 -0
  3. data/License.txt +1 -1
  4. data/README.md +1 -1
  5. data/lib/capybara.rb +1 -2
  6. data/lib/capybara/helpers.rb +3 -2
  7. data/lib/capybara/minitest.rb +1 -1
  8. data/lib/capybara/minitest/spec.rb +1 -0
  9. data/lib/capybara/node/finders.rb +20 -15
  10. data/lib/capybara/node/matchers.rb +43 -10
  11. data/lib/capybara/queries/current_path_query.rb +2 -2
  12. data/lib/capybara/queries/selector_query.rb +9 -2
  13. data/lib/capybara/rack_test/browser.rb +14 -13
  14. data/lib/capybara/rack_test/form.rb +32 -27
  15. data/lib/capybara/result.rb +8 -11
  16. data/lib/capybara/rspec/compound.rb +16 -26
  17. data/lib/capybara/rspec/matcher_proxies.rb +28 -11
  18. data/lib/capybara/rspec/matchers.rb +67 -45
  19. data/lib/capybara/selector.rb +22 -25
  20. data/lib/capybara/selector/builders/css_builder.rb +3 -4
  21. data/lib/capybara/selector/builders/xpath_builder.rb +4 -6
  22. data/lib/capybara/selector/regexp_disassembler.rb +43 -44
  23. data/lib/capybara/selector/selector.rb +16 -11
  24. data/lib/capybara/selenium/driver.rb +28 -21
  25. data/lib/capybara/selenium/nodes/marionette_node.rb +6 -12
  26. data/lib/capybara/server.rb +1 -3
  27. data/lib/capybara/server/animation_disabler.rb +2 -2
  28. data/lib/capybara/session.rb +1 -1
  29. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +29 -0
  30. data/lib/capybara/spec/session/click_button_spec.rb +6 -0
  31. data/lib/capybara/spec/session/click_link_spec.rb +1 -1
  32. data/lib/capybara/spec/session/has_all_selectors_spec.rb +1 -1
  33. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  34. data/lib/capybara/spec/session/html_spec.rb +7 -0
  35. data/lib/capybara/spec/session/node_spec.rb +5 -1
  36. data/lib/capybara/spec/session/refresh_spec.rb +4 -0
  37. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +4 -0
  38. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -0
  39. data/lib/capybara/spec/session/window/window_spec.rb +4 -0
  40. data/lib/capybara/spec/session/window/windows_spec.rb +4 -0
  41. data/lib/capybara/spec/views/form.erb +2 -2
  42. data/lib/capybara/version.rb +1 -1
  43. data/spec/minitest_spec.rb +5 -1
  44. data/spec/minitest_spec_spec.rb +5 -1
  45. data/spec/regexp_dissassembler_spec.rb +5 -3
  46. data/spec/rspec_spec.rb +20 -0
  47. data/spec/selector_spec.rb +89 -3
  48. data/spec/selenium_spec_chrome.rb +3 -7
  49. data/spec/selenium_spec_marionette.rb +1 -4
  50. metadata +20 -6
  51. data/lib/capybara/xpath_patches.rb +0 -27
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec 'node' do
4
8
  before do
5
9
  @session.visit('/with_html')
@@ -478,7 +482,7 @@ Capybara::SpecHelper.spec 'node' do
478
482
  expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
479
483
  end
480
484
 
481
- it 'should hold modifers at top level' do
485
+ it 'should hold modifiers at top level' do
482
486
  @session.visit('/form')
483
487
  @session.find(:css, '#address1_city').send_keys('ocean', :shift, 'side')
484
488
  expect(@session.find(:css, '#address1_city').value).to eq 'oceanSIDE'
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec '#refresh' do
4
8
  it 'reload the page' do
5
9
  @session.visit('/form')
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do
4
8
  before do
5
9
  @window = @session.current_window
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
4
8
  before do
5
9
  @window = @session.current_window
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
4
8
  before do
5
9
  @window = @session.current_window
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
3
7
  Capybara::SpecHelper.spec '#windows', requires: [:windows] do
4
8
  before do
5
9
  @window = @session.current_window
@@ -470,6 +470,8 @@ New line after and before textarea tag
470
470
  <input type="submit" name="form[submit_form1]" value="submit_form1" id="submit_form1"/>
471
471
  </form>
472
472
 
473
+ <button type="submit" name="form[outside_button]" value="outside_button" form="form2">Outside!</button>
474
+
473
475
  <form id="form2" action="/form" method="post">
474
476
  <input type="text" name="form[which_form]" value="form2" id="form_which_form2"/>
475
477
  <input type="submit" name="form[unused]" value="unused"/>
@@ -486,8 +488,6 @@ New line after and before textarea tag
486
488
  </select>
487
489
 
488
490
  <input type="submit" name="form[outside_submit]" value="outside_submit" form="form1"/>
489
- <button type="submit" name="form[outside_button]" value="outside_button" form="form2">Outside!</button>
490
-
491
491
 
492
492
  <form id="get-form" action="/form/get?foo=bar" method="get">
493
493
  <p>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capybara
4
- VERSION = '3.9.0'
4
+ VERSION = '3.10.0'
5
5
  end
@@ -105,6 +105,10 @@ class MinitestTest < Minitest::Test
105
105
  assert_none_of_selectors(:css, 'input#not_on_page', 'input#also_not_on_page')
106
106
  end
107
107
 
108
+ def test_assert_any_of_selectors
109
+ assert_any_of_selectors(:css, 'input#not_on_page', 'select#form_other_title')
110
+ end
111
+
108
112
  def test_assert_matches_selector
109
113
  assert_matches_selector(find(:field, 'customer_email'), :field, 'customer_email')
110
114
  assert_not_matches_selector(find(:select, 'form_title'), :field, 'customer_email')
@@ -144,6 +148,6 @@ RSpec.describe 'capybara/minitest' do
144
148
  reporter.start
145
149
  MinitestTest.run reporter, {}
146
150
  reporter.report
147
- expect(output.string).to include('19 runs, 49 assertions, 0 failures, 0 errors, 1 skips')
151
+ expect(output.string).to include('20 runs, 50 assertions, 0 failures, 0 errors, 1 skips')
148
152
  end
149
153
  end
@@ -98,6 +98,10 @@ class MinitestSpecTest < Minitest::Spec
98
98
  page.must_have_none_of_selectors(:css, 'input#not_on_page', 'input#also_not_on_page')
99
99
  end
100
100
 
101
+ it 'supports any_of_selectors expectations' do
102
+ page.must_have_any_of_selectors(:css, 'select#form_other_title', 'input#not_on_page')
103
+ end
104
+
101
105
  it 'supports match_selector expectations' do
102
106
  find(:field, 'customer_email').must_match_selector(:field, 'customer_email')
103
107
  find(:select, 'form_title').wont_match_selector(:field, 'customer_email')
@@ -140,7 +144,7 @@ RSpec.describe 'capybara/minitest/spec' do
140
144
  reporter.start
141
145
  MinitestSpecTest.run reporter, {}
142
146
  reporter.report
143
- expect(output.string).to include('19 runs, 41 assertions, 1 failures, 0 errors, 1 skips')
147
+ expect(output.string).to include('20 runs, 42 assertions, 1 failures, 0 errors, 1 skips')
144
148
  # Make sure error messages are displayed
145
149
  expect(output.string).to include('expected to find select box "non_existing_form_title" but there were no matches')
146
150
  end
@@ -12,9 +12,11 @@ RSpec.describe Capybara::Selector::RegexpDisassembler do
12
12
 
13
13
  it 'handles escaped characters' do
14
14
  verify_strings(
15
- /abc\\def/ => %w[abc def],
16
- /\nabc/ => %w[abc],
17
- %r{abc/} => %w[abc/]
15
+ /abc\\def/ => %w[abc\def],
16
+ /abc\.def/ => %w[abc.def],
17
+ /\nabc/ => ["\nabc"],
18
+ %r{abc/} => %w[abc/],
19
+ /ab\++cd/ => %w[ab+ cd]
18
20
  )
19
21
  end
20
22
 
@@ -111,6 +111,26 @@ RSpec.describe 'capybara/rspec' do
111
111
  expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41)
112
112
  end
113
113
  end
114
+
115
+ context 'when `match_when_negated` is not defined in a matcher' do
116
+ before do
117
+ RSpec::Matchers.define :only_match_matcher do |expected|
118
+ match do |actual|
119
+ !(actual ^ expected)
120
+ end
121
+ end
122
+ end
123
+
124
+ it 'can be called with `not_to`' do
125
+ # This test is for a bug in jruby where `super` isn't defined correctly - https://github.com/jruby/jruby/issues/4678
126
+ # Reported in https://github.com/teamcapybara/capybara/issues/2115
127
+ @test_class_instance.instance_eval do
128
+ expect do
129
+ expect(true).not_to only_match_matcher(false) # rubocop:disable RSpec/ExpectActual
130
+ end.not_to raise_error
131
+ end
132
+ end
133
+ end
114
134
  end
115
135
 
116
136
  it 'should not include Capybara' do
@@ -17,7 +17,7 @@ RSpec.describe Capybara do
17
17
  <p>Yes it is</p>
18
18
  </div>
19
19
  <p class="bb cc">Some Content</p>
20
- <p class="bb dd"></p>
20
+ <p class="bb dd !mine"></p>
21
21
  </div>
22
22
  <div id="#special">
23
23
  </div>
@@ -54,11 +54,27 @@ RSpec.describe Capybara do
54
54
  end
55
55
 
56
56
  Capybara.add_selector :custom_css_selector do
57
- css { |selector| selector }
57
+ css(:name, :other_name) do |selector, name: nil, **|
58
+ selector ||= ''
59
+ selector += "[name='#{name}']" if name
60
+ selector
61
+ end
62
+
63
+ expression_filter(:placeholder) do |expr, val|
64
+ expr + "[placeholder='#{val}']"
65
+ end
66
+
67
+ expression_filter(:value) do |expr, val|
68
+ expr + "[value='#{val}']"
69
+ end
70
+
71
+ expression_filter(:title) do |expr, val|
72
+ expr + "[title='#{val}']"
73
+ end
58
74
  end
59
75
 
60
76
  Capybara.add_selector :custom_xpath_selector do
61
- xpath { |selector| selector }
77
+ xpath(:valid1, :valid2) { |selector| selector }
62
78
  end
63
79
  end
64
80
 
@@ -104,6 +120,71 @@ RSpec.describe Capybara do
104
120
  end
105
121
  end
106
122
 
123
+ describe 'xpath' do
124
+ it 'uses filter names passed in' do
125
+ selector = Capybara::Selector.new :test do
126
+ xpath(:something, :other) { |_locator| XPath.descendant }
127
+ end
128
+
129
+ expect(selector.expression_filters.keys).to include(:something, :other)
130
+ end
131
+
132
+ it 'gets filter names from block if none passed to xpath method' do
133
+ selector = Capybara::Selector.new :test do
134
+ xpath { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
135
+ end
136
+
137
+ expect(selector.expression_filters.keys).to include(:valid3, :valid4)
138
+ end
139
+
140
+ it 'ignores block parameters if names passed in' do
141
+ selector = Capybara::Selector.new :test do
142
+ xpath(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
143
+ end
144
+
145
+ expect(selector.expression_filters.keys).to include(:valid1)
146
+ expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
147
+ end
148
+ end
149
+
150
+ describe 'css' do
151
+ it "supports filters specified in 'css' definition" do
152
+ expect(string).to have_selector(:custom_css_selector, 'input', name: 'form[my_text_input]')
153
+ expect(string).to have_no_selector(:custom_css_selector, 'input', name: 'form[not_my_text_input]')
154
+ end
155
+
156
+ it 'supports explicitly defined expression filters' do
157
+ expect(string).to have_selector(:custom_css_selector, placeholder: 'my text')
158
+ expect(string).to have_no_selector(:custom_css_selector, placeholder: 'not my text')
159
+ expect(string).to have_selector(:custom_css_selector, value: 'click me', title: 'submit button')
160
+ end
161
+
162
+ it 'uses filter names passed in' do
163
+ selector = Capybara::Selector.new :text do
164
+ css(:name, :other_name) { |_locator| '' }
165
+ end
166
+
167
+ expect(selector.expression_filters.keys).to include(:name, :other_name)
168
+ end
169
+
170
+ it 'gets filter names from block if none passed to css method' do
171
+ selector = Capybara::Selector.new :test do
172
+ css { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
173
+ end
174
+
175
+ expect(selector.expression_filters.keys).to include(:valid3, :valid4)
176
+ end
177
+
178
+ it 'ignores block parameters if names passed in' do
179
+ selector = Capybara::Selector.new :test do
180
+ css(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
181
+ end
182
+
183
+ expect(selector.expression_filters.keys).to include(:valid1)
184
+ expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
185
+ end
186
+ end
187
+
107
188
  describe 'builtin selectors' do
108
189
  context 'when locator is nil' do
109
190
  it 'devolves to just finding element types' do
@@ -172,6 +253,11 @@ RSpec.describe Capybara do
172
253
  expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!cc', '!dd', 'bb']).size).to eq 1
173
254
  end
174
255
 
256
+ it 'handles classes starting with ! by requiring negated negated first' do
257
+ expect(string.all(:custom_css_selector, 'div, p', class: ['!!!mine']).size).to eq 1
258
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!!!mine']).size).to eq 1
259
+ end
260
+
175
261
  it "works with 'special' characters" do
176
262
  expect(string.find(:custom_css_selector, 'input', class: '.special')[:id]).to eq 'file'
177
263
  expect(string.find(:custom_css_selector, 'input', class: '2checkbox')[:id]).to eq '2checkbox'
@@ -7,10 +7,6 @@ require 'rspec/shared_spec_matchers'
7
7
 
8
8
  CHROME_DRIVER = ENV['HEADLESS'] ? :selenium_chrome_headless : :selenium_chrome
9
9
 
10
- # if ENV['HEADLESS'] && ENV['TRAVIS']
11
- # Selenium::WebDriver::Chrome.path='/usr/bin/google-chrome-beta'
12
- # end
13
-
14
10
  Capybara.register_driver :selenium_chrome do |app|
15
11
  driver = Capybara::Selenium::Driver.new(app, browser: :chrome)
16
12
  driver.browser.download_path = Capybara.save_path
@@ -19,8 +15,8 @@ end
19
15
 
20
16
  Capybara.register_driver :selenium_chrome_headless do |app|
21
17
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
22
- browser_options.args << '--headless'
23
- browser_options.args << '--disable-gpu' if Gem.win_platform?
18
+ browser_options.headless!
19
+ browser_options.add_option(:w3c, !!ENV['W3C'])
24
20
  driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
25
21
  driver.browser.download_path = Capybara.save_path
26
22
  driver
@@ -31,7 +27,7 @@ Capybara.register_driver :selenium_chrome_clear_storage do |app|
31
27
  browser: :chrome,
32
28
  options: ::Selenium::WebDriver::Chrome::Options.new
33
29
  }
34
- chrome_options[:options].args << 'headless' if ENV['HEADLESS']
30
+ chrome_options[:options].headless! if ENV['HEADLESS']
35
31
  Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: true, clear_session_storage: true))
36
32
  end
37
33
 
@@ -6,8 +6,7 @@ require 'shared_selenium_session'
6
6
  require 'rspec/shared_spec_matchers'
7
7
 
8
8
  browser_options = ::Selenium::WebDriver::Firefox::Options.new
9
- browser_options.args << '--headless' if ENV['HEADLESS']
10
- browser_options.add_preference 'dom.file.createInChild', true
9
+ browser_options.headless! if ENV['HEADLESS']
11
10
  # browser_options.add_option("log", {"level": "trace"})
12
11
 
13
12
  browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |profile|
@@ -21,7 +20,6 @@ Capybara.register_driver :selenium_marionette do |app|
21
20
  Capybara::Selenium::Driver.new(
22
21
  app,
23
22
  browser: :firefox,
24
- desired_capabilities: { marionette: true, 'moz:webdriverClick': true },
25
23
  options: browser_options,
26
24
  # Get a trace level log from geckodriver
27
25
  # :driver_opts => { args: ['-vv'] }
@@ -32,7 +30,6 @@ Capybara.register_driver :selenium_marionette_clear_storage do |app|
32
30
  Capybara::Selenium::Driver.new(
33
31
  app,
34
32
  browser: :firefox,
35
- desired_capabilities: { marionette: true },
36
33
  clear_local_storage: true,
37
34
  clear_session_storage: true,
38
35
  options: browser_options
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Walpole
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
12
12
  - gem-public_cert.pem
13
- date: 2018-10-03 00:00:00.000000000 Z
13
+ date: 2018-10-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: addressable
@@ -82,20 +82,34 @@ dependencies:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: 0.6.3
85
+ - !ruby/object:Gem::Dependency
86
+ name: regexp_parser
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '1.2'
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '1.2'
85
99
  - !ruby/object:Gem::Dependency
86
100
  name: xpath
87
101
  requirement: !ruby/object:Gem::Requirement
88
102
  requirements:
89
103
  - - "~>"
90
104
  - !ruby/object:Gem::Version
91
- version: '3.1'
105
+ version: '3.2'
92
106
  type: :runtime
93
107
  prerelease: false
94
108
  version_requirements: !ruby/object:Gem::Requirement
95
109
  requirements:
96
110
  - - "~>"
97
111
  - !ruby/object:Gem::Version
98
- version: '3.1'
112
+ version: '3.2'
99
113
  - !ruby/object:Gem::Dependency
100
114
  name: cucumber
101
115
  requirement: !ruby/object:Gem::Requirement
@@ -376,6 +390,7 @@ files:
376
390
  - lib/capybara/spec/session/go_back_spec.rb
377
391
  - lib/capybara/spec/session/go_forward_spec.rb
378
392
  - lib/capybara/spec/session/has_all_selectors_spec.rb
393
+ - lib/capybara/spec/session/has_any_selectors_spec.rb
379
394
  - lib/capybara/spec/session/has_button_spec.rb
380
395
  - lib/capybara/spec/session/has_css_spec.rb
381
396
  - lib/capybara/spec/session/has_current_path_spec.rb
@@ -455,7 +470,6 @@ files:
455
470
  - lib/capybara/spec/views/within_frames.erb
456
471
  - lib/capybara/version.rb
457
472
  - lib/capybara/window.rb
458
- - lib/capybara/xpath_patches.rb
459
473
  - spec/basic_node_spec.rb
460
474
  - spec/capybara_spec.rb
461
475
  - spec/css_splitter_spec.rb
@@ -511,7 +525,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
511
525
  version: '0'
512
526
  requirements: []
513
527
  rubyforge_project:
514
- rubygems_version: 2.7.7
528
+ rubygems_version: 2.7.6
515
529
  signing_key:
516
530
  specification_version: 4
517
531
  summary: Capybara aims to simplify the process of integration testing Rack applications,
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module XPath
4
- module DSL
5
- def lowercase
6
- method(:translate, 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ', 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')
7
- end
8
-
9
- def uppercase
10
- method(:translate, 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ')
11
- end
12
- end
13
- end
14
-
15
- module Capybara
16
- module XPathPatches
17
- module Renderer
18
- def attribute(current, name)
19
- return super if name =~ /^[a-zA-Z_:][a-zA-Z0-9_:\.\-]*$/
20
-
21
- "#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
22
- end
23
- end
24
- end
25
- end
26
-
27
- XPath::Renderer.prepend(Capybara::XPathPatches::Renderer)