capybara 3.9.0 → 3.10.0

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