bbc-capybara 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/History.txt +325 -0
  2. data/License.txt +22 -0
  3. data/README.md +815 -0
  4. data/lib/capybara.rb +368 -0
  5. data/lib/capybara/cucumber.rb +25 -0
  6. data/lib/capybara/driver/base.rb +64 -0
  7. data/lib/capybara/driver/node.rb +74 -0
  8. data/lib/capybara/dsl.rb +50 -0
  9. data/lib/capybara/node/actions.rb +146 -0
  10. data/lib/capybara/node/base.rb +63 -0
  11. data/lib/capybara/node/document.rb +25 -0
  12. data/lib/capybara/node/element.rb +201 -0
  13. data/lib/capybara/node/finders.rb +154 -0
  14. data/lib/capybara/node/matchers.rb +442 -0
  15. data/lib/capybara/node/simple.rb +132 -0
  16. data/lib/capybara/query.rb +63 -0
  17. data/lib/capybara/rack_test/browser.rb +126 -0
  18. data/lib/capybara/rack_test/driver.rb +80 -0
  19. data/lib/capybara/rack_test/form.rb +80 -0
  20. data/lib/capybara/rack_test/node.rb +121 -0
  21. data/lib/capybara/rails.rb +17 -0
  22. data/lib/capybara/rspec.rb +26 -0
  23. data/lib/capybara/rspec/features.rb +22 -0
  24. data/lib/capybara/rspec/matchers.rb +152 -0
  25. data/lib/capybara/selector.rb +156 -0
  26. data/lib/capybara/selenium/driver.rb +169 -0
  27. data/lib/capybara/selenium/node.rb +91 -0
  28. data/lib/capybara/server.rb +87 -0
  29. data/lib/capybara/session.rb +322 -0
  30. data/lib/capybara/spec/driver.rb +329 -0
  31. data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
  32. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  33. data/lib/capybara/spec/public/jquery-ui.js +791 -0
  34. data/lib/capybara/spec/public/jquery.js +9046 -0
  35. data/lib/capybara/spec/public/test.js +43 -0
  36. data/lib/capybara/spec/session.rb +148 -0
  37. data/lib/capybara/spec/session/all_spec.rb +78 -0
  38. data/lib/capybara/spec/session/attach_file_spec.rb +76 -0
  39. data/lib/capybara/spec/session/check_spec.rb +68 -0
  40. data/lib/capybara/spec/session/choose_spec.rb +29 -0
  41. data/lib/capybara/spec/session/click_button_spec.rb +305 -0
  42. data/lib/capybara/spec/session/click_link_or_button_spec.rb +37 -0
  43. data/lib/capybara/spec/session/click_link_spec.rb +120 -0
  44. data/lib/capybara/spec/session/current_url_spec.rb +83 -0
  45. data/lib/capybara/spec/session/fill_in_spec.rb +127 -0
  46. data/lib/capybara/spec/session/find_button_spec.rb +18 -0
  47. data/lib/capybara/spec/session/find_by_id_spec.rb +18 -0
  48. data/lib/capybara/spec/session/find_field_spec.rb +26 -0
  49. data/lib/capybara/spec/session/find_link_spec.rb +19 -0
  50. data/lib/capybara/spec/session/find_spec.rb +168 -0
  51. data/lib/capybara/spec/session/first_spec.rb +105 -0
  52. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  53. data/lib/capybara/spec/session/has_css_spec.rb +243 -0
  54. data/lib/capybara/spec/session/has_field_spec.rb +192 -0
  55. data/lib/capybara/spec/session/has_link_spec.rb +37 -0
  56. data/lib/capybara/spec/session/has_select_spec.rb +129 -0
  57. data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
  58. data/lib/capybara/spec/session/has_table_spec.rb +34 -0
  59. data/lib/capybara/spec/session/has_text_spec.rb +138 -0
  60. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  61. data/lib/capybara/spec/session/headers.rb +19 -0
  62. data/lib/capybara/spec/session/javascript.rb +312 -0
  63. data/lib/capybara/spec/session/response_code.rb +19 -0
  64. data/lib/capybara/spec/session/select_spec.rb +119 -0
  65. data/lib/capybara/spec/session/text_spec.rb +24 -0
  66. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  67. data/lib/capybara/spec/session/unselect_spec.rb +67 -0
  68. data/lib/capybara/spec/session/within_spec.rb +178 -0
  69. data/lib/capybara/spec/test_app.rb +156 -0
  70. data/lib/capybara/spec/views/buttons.erb +4 -0
  71. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  72. data/lib/capybara/spec/views/form.erb +366 -0
  73. data/lib/capybara/spec/views/frame_one.erb +8 -0
  74. data/lib/capybara/spec/views/frame_two.erb +8 -0
  75. data/lib/capybara/spec/views/header_links.erb +7 -0
  76. data/lib/capybara/spec/views/host_links.erb +12 -0
  77. data/lib/capybara/spec/views/popup_one.erb +8 -0
  78. data/lib/capybara/spec/views/popup_two.erb +8 -0
  79. data/lib/capybara/spec/views/postback.erb +13 -0
  80. data/lib/capybara/spec/views/tables.erb +62 -0
  81. data/lib/capybara/spec/views/with_html.erb +75 -0
  82. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  83. data/lib/capybara/spec/views/with_js.erb +53 -0
  84. data/lib/capybara/spec/views/with_scope.erb +36 -0
  85. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  86. data/lib/capybara/spec/views/within_frames.erb +10 -0
  87. data/lib/capybara/spec/views/within_popups.erb +25 -0
  88. data/lib/capybara/util/save_and_open_page.rb +45 -0
  89. data/lib/capybara/util/timeout.rb +27 -0
  90. data/lib/capybara/version.rb +3 -0
  91. data/spec/basic_node_spec.rb +89 -0
  92. data/spec/capybara_spec.rb +46 -0
  93. data/spec/driver/rack_test_driver_spec.rb +90 -0
  94. data/spec/driver/selenium_driver_spec.rb +55 -0
  95. data/spec/dsl_spec.rb +255 -0
  96. data/spec/fixtures/selenium_driver_rspec_failure.rb +8 -0
  97. data/spec/fixtures/selenium_driver_rspec_success.rb +8 -0
  98. data/spec/rspec/features_spec.rb +43 -0
  99. data/spec/rspec/matchers_spec.rb +564 -0
  100. data/spec/rspec_spec.rb +51 -0
  101. data/spec/save_and_open_page_spec.rb +155 -0
  102. data/spec/server_spec.rb +74 -0
  103. data/spec/session/rack_test_session_spec.rb +61 -0
  104. data/spec/session/selenium_session_spec.rb +26 -0
  105. data/spec/spec_helper.rb +31 -0
  106. data/spec/timeout_spec.rb +28 -0
  107. metadata +297 -0
@@ -0,0 +1,121 @@
1
+ class Capybara::RackTest::Node < Capybara::Driver::Node
2
+ def text
3
+ unnormalized_text.strip.gsub(/\s+/, ' ')
4
+ end
5
+
6
+ def [](name)
7
+ string_node[name]
8
+ end
9
+
10
+ def value
11
+ string_node.value
12
+ end
13
+
14
+ def set(value)
15
+ if tag_name == 'input' and type == 'radio'
16
+ other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
17
+ driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
18
+ native['checked'] = 'checked'
19
+ elsif tag_name == 'input' and type == 'checkbox'
20
+ if value && !native['checked']
21
+ native['checked'] = 'checked'
22
+ elsif !value && native['checked']
23
+ native.remove_attribute('checked')
24
+ end
25
+ elsif tag_name == 'input'
26
+ if (type == 'text' || type == 'password') && self[:maxlength] &&
27
+ !self[:maxlength].empty?
28
+ # Browser behavior for maxlength="0" is inconsistent, so we stick with
29
+ # Firefox, allowing no input
30
+ value = value[0...self[:maxlength].to_i]
31
+ end
32
+ native['value'] = value.to_s
33
+ elsif tag_name == "textarea"
34
+ native.content = value.to_s
35
+ end
36
+ end
37
+
38
+ def select_option
39
+ if select_node['multiple'] != 'multiple'
40
+ select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
41
+ end
42
+ native["selected"] = 'selected'
43
+ end
44
+
45
+ def unselect_option
46
+ if select_node['multiple'] != 'multiple'
47
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
48
+ end
49
+ native.remove_attribute('selected')
50
+ end
51
+
52
+ def click
53
+ if tag_name == 'a'
54
+ method = self["data-method"] if driver.options[:respect_data_method]
55
+ method ||= :get
56
+ driver.follow(method, self[:href].to_s)
57
+ elsif (tag_name == 'input' and %w(submit image).include?(type)) or
58
+ ((tag_name == 'button') and type.nil? or type == "submit")
59
+ Capybara::RackTest::Form.new(driver, form).submit(self)
60
+ end
61
+ end
62
+
63
+ def tag_name
64
+ native.node_name
65
+ end
66
+
67
+ def visible?
68
+ string_node.visible?
69
+ end
70
+
71
+ def checked?
72
+ string_node.checked?
73
+ end
74
+
75
+ def selected?
76
+ string_node.selected?
77
+ end
78
+
79
+ def path
80
+ native.path
81
+ end
82
+
83
+ def find(locator)
84
+ native.xpath(locator).map { |n| self.class.new(driver, n) }
85
+ end
86
+
87
+ protected
88
+
89
+ def unnormalized_text
90
+ if !visible?
91
+ ''
92
+ elsif native.text?
93
+ native.text
94
+ elsif native.element?
95
+ native.children.map do |child|
96
+ Capybara::RackTest::Node.new(driver, child).unnormalized_text
97
+ end.join
98
+ else
99
+ ''
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def string_node
106
+ @string_node ||= Capybara::Node::Simple.new(native)
107
+ end
108
+
109
+ # a reference to the select node if this is an option node
110
+ def select_node
111
+ find('./ancestor::select').first
112
+ end
113
+
114
+ def type
115
+ native[:type]
116
+ end
117
+
118
+ def form
119
+ native.ancestors('form').first
120
+ end
121
+ end
@@ -0,0 +1,17 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ Capybara.app = Rack::Builder.new do
5
+ map "/" do
6
+ if Rails.version.to_f >= 3.0
7
+ run Rails.application
8
+ else # Rails 2
9
+ use Rails::Rack::Static
10
+ run ActionController::Dispatcher.new
11
+ end
12
+ end
13
+ end.to_app
14
+
15
+ Capybara.asset_root = Rails.root.join('public')
16
+ Capybara.save_and_open_page_path = Rails.root.join('tmp/capybara')
17
+
@@ -0,0 +1,26 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+ require 'rspec/core'
4
+ require 'capybara/rspec/matchers'
5
+ require 'capybara/rspec/features'
6
+
7
+ RSpec.configure do |config|
8
+ config.include Capybara::DSL, :type => :request
9
+ config.include Capybara::DSL, :type => :acceptance
10
+ config.include Capybara::RSpecMatchers, :type => :request
11
+ config.include Capybara::RSpecMatchers, :type => :acceptance
12
+ # The before and after blocks must run instantaneously, because Capybara
13
+ # might not actually be used in all examples where it's included.
14
+ config.after do
15
+ if self.class.include?(Capybara::DSL)
16
+ Capybara.reset_sessions!
17
+ Capybara.use_default_driver
18
+ end
19
+ end
20
+ config.before do
21
+ if self.class.include?(Capybara::DSL)
22
+ Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
23
+ Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module Capybara
2
+ module Features
3
+ def self.included(base)
4
+ base.instance_eval do
5
+ alias :background :before
6
+ alias :scenario :it
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ def self.feature(*args, &block)
13
+ options = if args.last.is_a?(Hash) then args.pop else {} end
14
+ options[:capybara_feature] = true
15
+ options[:type] = :request
16
+ options[:caller] ||= caller
17
+ args.push(options)
18
+
19
+ describe(*args, &block)
20
+ end
21
+
22
+ RSpec.configuration.include Capybara::Features, :capybara_feature => true
@@ -0,0 +1,152 @@
1
+ module Capybara
2
+ module RSpecMatchers
3
+ class HaveSelector
4
+ def initialize(*args)
5
+ @args = args
6
+ end
7
+
8
+ def matches?(actual)
9
+ @actual = wrap(actual)
10
+ @actual.has_selector?(*@args)
11
+ end
12
+
13
+ def does_not_match?(actual)
14
+ @actual = wrap(actual)
15
+ @actual.has_no_selector?(*@args)
16
+ end
17
+
18
+ def failure_message_for_should
19
+ query.failure_message(:assert, @actual)
20
+ end
21
+
22
+ def failure_message_for_should_not
23
+ query.negative_failure_message(:assert, @actual)
24
+ end
25
+
26
+ def description
27
+ "has #{query.description}"
28
+ end
29
+
30
+ def wrap(actual)
31
+ if actual.respond_to?("has_selector?")
32
+ actual
33
+ else
34
+ Capybara.string(actual.to_s)
35
+ end
36
+ end
37
+
38
+ def query
39
+ @query ||= Capybara::Query.new(*@args)
40
+ end
41
+ end
42
+
43
+ class HaveMatcher
44
+ attr_reader :name, :locator, :options, :failure_message, :actual
45
+
46
+ def initialize(name, locator, options={}, &block)
47
+ @name = name
48
+ @locator = locator
49
+ @options = options
50
+ @failure_message = block
51
+ end
52
+
53
+ def arguments
54
+ if options.empty? then [locator] else [locator, options] end
55
+ end
56
+
57
+ def matches?(actual)
58
+ @actual = wrap(actual)
59
+ @actual.send(:"has_#{name}?", *arguments)
60
+ end
61
+
62
+ def does_not_match?(actual)
63
+ @actual = wrap(actual)
64
+ @actual.send(:"has_no_#{name}?", *arguments)
65
+ end
66
+
67
+ def failure_message_for_should
68
+ if failure_message
69
+ failure_message.call(actual, self)
70
+ elsif(@options[:count])
71
+ "expected #{selector_name} to be returned #{@options[:count]} times"
72
+ else
73
+ "expected #{selector_name} to return something"
74
+ end
75
+ end
76
+
77
+ def failure_message_for_should_not
78
+ "expected #{selector_name} not to return anything"
79
+ end
80
+
81
+ def description
82
+ "has #{selector_name}"
83
+ end
84
+
85
+ def selector_name
86
+ selector_name = "#{name} #{locator.inspect}"
87
+ selector_name << " with text #{options[:text].inspect}" if options[:text]
88
+ selector_name
89
+ end
90
+
91
+ def wrap(actual)
92
+ if actual.respond_to?("has_selector?")
93
+ actual
94
+ else
95
+ Capybara.string(actual.to_s)
96
+ end
97
+ end
98
+ end
99
+
100
+ def have_selector(*args)
101
+ HaveSelector.new(*args)
102
+ end
103
+
104
+ def have_xpath(xpath, options={})
105
+ HaveMatcher.new(:xpath, xpath, options)
106
+ end
107
+
108
+ def have_css(css, options={})
109
+ HaveMatcher.new(:css, css, options)
110
+ end
111
+
112
+ def have_content(text)
113
+ HaveMatcher.new(:content, text.to_s) do |page, matcher|
114
+ %(expected there to be content #{matcher.locator.inspect} in #{page.text.inspect})
115
+ end
116
+ end
117
+
118
+ def have_text(text)
119
+ HaveMatcher.new(:text, text.to_s) do |page, matcher|
120
+ %(expected there to be text #{matcher.locator.inspect} in #{page.text.inspect})
121
+ end
122
+ end
123
+
124
+ def have_link(locator, options={})
125
+ HaveMatcher.new(:link, locator, options)
126
+ end
127
+
128
+ def have_button(locator)
129
+ HaveMatcher.new(:button, locator)
130
+ end
131
+
132
+ def have_field(locator, options={})
133
+ HaveMatcher.new(:field, locator, options)
134
+ end
135
+
136
+ def have_checked_field(locator)
137
+ HaveMatcher.new(:checked_field, locator)
138
+ end
139
+
140
+ def have_unchecked_field(locator)
141
+ HaveMatcher.new(:unchecked_field, locator)
142
+ end
143
+
144
+ def have_select(locator, options={})
145
+ HaveMatcher.new(:select, locator, options)
146
+ end
147
+
148
+ def have_table(locator, options={})
149
+ HaveMatcher.new(:table, locator, options)
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,156 @@
1
+ module Capybara
2
+ class Selector
3
+ attr_reader :name, :custom_filters
4
+
5
+
6
+ class << self
7
+ def all
8
+ @selectors ||= {}
9
+ end
10
+
11
+ def add(name, &block)
12
+ all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block)
13
+ end
14
+
15
+ def remove(name)
16
+ all.delete(name.to_sym)
17
+ end
18
+ end
19
+
20
+ def initialize(name, &block)
21
+ @name = name
22
+ @custom_filters = {}
23
+ @match = nil
24
+ @failure_message = nil
25
+ instance_eval(&block)
26
+ end
27
+
28
+ def xpath(&block)
29
+ @xpath = block if block
30
+ @xpath
31
+ end
32
+
33
+ # Same as xpath, but wrap in XPath.css().
34
+ def css(&block)
35
+ if block
36
+ @xpath = xpath { |*args| XPath.css(block.call(*args)) }
37
+ end
38
+ @xpath
39
+ end
40
+
41
+ def match(&block)
42
+ @match = block if block
43
+ @match
44
+ end
45
+
46
+ def failure_message(&block)
47
+ @failure_message = block if block
48
+ @failure_message
49
+ end
50
+
51
+ def call(locator)
52
+ @xpath.call(locator)
53
+ end
54
+
55
+ def match?(locator)
56
+ @match and @match.call(locator)
57
+ end
58
+
59
+ def filter(name, &block)
60
+ @custom_filters[name] = block
61
+ end
62
+ end
63
+ end
64
+
65
+ Capybara.add_selector(:xpath) do
66
+ xpath { |xpath| xpath }
67
+ end
68
+
69
+ Capybara.add_selector(:css) do
70
+ css { |css| css }
71
+ end
72
+
73
+ Capybara.add_selector(:id) do
74
+ xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
75
+ match { |value| value.is_a?(Symbol) }
76
+ end
77
+
78
+ Capybara.add_selector(:field) do
79
+ xpath { |locator| XPath::HTML.field(locator) }
80
+ filter(:checked) { |node, value| not(value ^ node.checked?) }
81
+ filter(:unchecked) { |node, value| (value ^ node.checked?) }
82
+ filter(:with) { |node, with| node.value == with }
83
+ end
84
+
85
+ Capybara.add_selector(:fieldset) do
86
+ xpath { |locator| XPath::HTML.fieldset(locator) }
87
+ end
88
+
89
+ Capybara.add_selector(:link_or_button) do
90
+ xpath { |locator| XPath::HTML.link_or_button(locator) }
91
+ failure_message { |node, selector| "no link or button '#{selector.locator}' found" }
92
+ end
93
+
94
+ Capybara.add_selector(:link) do
95
+ xpath { |locator| XPath::HTML.link(locator) }
96
+ failure_message { |node, selector| "no link with title, id or text '#{selector.locator}' found" }
97
+ filter(:href) do |node, href|
98
+ node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href)])
99
+ end
100
+ end
101
+
102
+ Capybara.add_selector(:button) do
103
+ xpath { |locator| XPath::HTML.button(locator) }
104
+ failure_message { |node, selector| "no button with value or id or text '#{selector.locator}' found" }
105
+ end
106
+
107
+ Capybara.add_selector(:fillable_field) do
108
+ xpath { |locator| XPath::HTML.fillable_field(locator) }
109
+ failure_message { |node, selector| "no text field, text area or password field with id, name, or label '#{selector.locator}' found" }
110
+ end
111
+
112
+ Capybara.add_selector(:radio_button) do
113
+ xpath { |locator| XPath::HTML.radio_button(locator) }
114
+ failure_message { |node, selector| "no radio button with id, name, or label '#{selector.locator}' found" }
115
+ filter(:checked) { |node, value| not(value ^ node.checked?) }
116
+ filter(:unchecked) { |node, value| (value ^ node.checked?) }
117
+ end
118
+
119
+ Capybara.add_selector(:checkbox) do
120
+ xpath { |locator| XPath::HTML.checkbox(locator) }
121
+ failure_message { |node, selector| "no checkbox with id, name, or label '#{selector.locator}' found" }
122
+ filter(:checked) { |node, value| not(value ^ node.checked?) }
123
+ filter(:unchecked) { |node, value| (value ^ node.checked?) }
124
+ end
125
+
126
+ Capybara.add_selector(:select) do
127
+ xpath { |locator| XPath::HTML.select(locator) }
128
+ failure_message { |node, selector| "no select box with id, name, or label '#{selector.locator}' found" }
129
+ filter(:options) { |node, options| options.all? { |option| node.first(:option, option) } }
130
+ filter(:selected) do |node, selected|
131
+ actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
132
+ ([selected].flatten - actual).empty?
133
+ end
134
+ end
135
+
136
+ Capybara.add_selector(:option) do
137
+ xpath { |locator| XPath::HTML.option(locator) }
138
+ failure_message do |node, selector|
139
+ "no option with text '#{selector.locator}'".tap do |message|
140
+ message << " in the select box" if node.tag_name == 'select'
141
+ end
142
+ end
143
+ end
144
+
145
+ Capybara.add_selector(:file_field) do
146
+ xpath { |locator| XPath::HTML.file_field(locator) }
147
+ failure_message { |node, selector| "no file field with id, name, or label '#{selector.locator}' found" }
148
+ end
149
+
150
+ Capybara.add_selector(:content) do
151
+ xpath { |content| XPath::HTML.content(content) }
152
+ end
153
+
154
+ Capybara.add_selector(:table) do
155
+ xpath { |locator| XPath::HTML.table(locator) }
156
+ end