bbc-capybara 1.1.2

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 (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,132 @@
1
+ module Capybara
2
+ module Node
3
+
4
+ ##
5
+ #
6
+ # A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
7
+ # includes only {Capybara::Node::Finders} and {Capybara::Node::Matchers} and does
8
+ # not include {Capybara::Node::Actions}. This type of node is returned when
9
+ # using {Capybara.string}.
10
+ #
11
+ # It is useful in that it does not require a session, an application or a driver,
12
+ # but can still use Capybara's finders and matchers on any string that contains HTML.
13
+ #
14
+ class Simple
15
+ include Capybara::Node::Finders
16
+ include Capybara::Node::Matchers
17
+
18
+ attr_reader :native
19
+
20
+ def initialize(native)
21
+ native = Nokogiri::HTML(native) if native.is_a?(String)
22
+ @native = native
23
+ end
24
+
25
+ ##
26
+ #
27
+ # @return [String] The text of the element
28
+ #
29
+ def text
30
+ native.text
31
+ end
32
+
33
+ ##
34
+ #
35
+ # Retrieve the given attribute
36
+ #
37
+ # element[:title] # => HTML title attribute
38
+ #
39
+ # @param [Symbol] attribute The attribute to retrieve
40
+ # @return [String] The value of the attribute
41
+ #
42
+ def [](name)
43
+ attr_name = name.to_s
44
+ if attr_name == 'value'
45
+ value
46
+ elsif 'input' == tag_name and 'checkbox' == native[:type] and 'checked' == attr_name
47
+ native['checked'] == 'checked'
48
+ else
49
+ native[attr_name]
50
+ end
51
+ end
52
+
53
+ ##
54
+ #
55
+ # @return [String] The tag name of the element
56
+ #
57
+ def tag_name
58
+ native.node_name
59
+ end
60
+
61
+ ##
62
+ #
63
+ # An XPath expression describing where on the page the element can be found
64
+ #
65
+ # @return [String] An XPath expression
66
+ #
67
+ def path
68
+ native.path
69
+ end
70
+
71
+ ##
72
+ #
73
+ # @return [String] The value of the form element
74
+ #
75
+ def value
76
+ if tag_name == 'textarea'
77
+ native.content
78
+ elsif tag_name == 'select'
79
+ if native['multiple'] == 'multiple'
80
+ native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
81
+ else
82
+ option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
83
+ option[:value] || option.content if option
84
+ end
85
+ else
86
+ native[:value]
87
+ end
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Whether or not the element is visible. Does not support CSS, so
93
+ # the result may be inaccurate.
94
+ #
95
+ # @return [Boolean] Whether the element is visible
96
+ #
97
+ def visible?
98
+ native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or name()='script' or name()='head']").size == 0
99
+ end
100
+
101
+ ##
102
+ #
103
+ # Whether or not the element is checked.
104
+ #
105
+ # @return [Boolean] Whether the element is checked
106
+ #
107
+ def checked?
108
+ native[:checked]
109
+ end
110
+
111
+ ##
112
+ #
113
+ # Whether or not the element is selected.
114
+ #
115
+ # @return [Boolean] Whether the element is selected
116
+ #
117
+ def selected?
118
+ native[:selected]
119
+ end
120
+
121
+ protected
122
+
123
+ def find_in_base(selector, xpath)
124
+ native.xpath(xpath).map { |node| self.class.new(node) }
125
+ end
126
+
127
+ def wait_until
128
+ yield # simple nodes don't need to wait
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,63 @@
1
+ module Capybara
2
+ class Query
3
+ attr_accessor :selector, :locator, :options, :xpaths
4
+
5
+ def initialize(*args)
6
+ @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
7
+ unless options.has_key?(:visible)
8
+ @options[:visible] = Capybara.ignore_hidden_elements
9
+ end
10
+
11
+ if args[1]
12
+ @selector = Selector.all[args[0]]
13
+ @locator = args[1]
14
+ else
15
+ @selector = Selector.all.values.find { |s| s.match?(args[0]) }
16
+ @locator = args[0]
17
+ end
18
+ @selector ||= Selector.all[Capybara.default_selector]
19
+
20
+ xpath = @selector.call(@locator)
21
+ if xpath.respond_to?(:to_xpaths)
22
+ @xpaths = xpath.to_xpaths
23
+ else
24
+ @xpaths = [xpath.to_s].flatten
25
+ end
26
+ end
27
+
28
+ def failure_message(type, node)
29
+ message = selector.failure_message.call(node, self) if selector.failure_message
30
+ message ||= options[:message]
31
+ if type == :assert
32
+ message ||= "expected #{description} to return something"
33
+ else
34
+ message ||= "Unable to find #{description}"
35
+ end
36
+ message
37
+ end
38
+
39
+ def negative_failure_message(type, node)
40
+ "expected #{description} not to return anything"
41
+ end
42
+
43
+ def name; selector.name; end
44
+
45
+ def description
46
+ @description = "#{name} #{locator.inspect}"
47
+ @description << " with text #{options[:text].inspect}" if options[:text]
48
+ @description
49
+ end
50
+
51
+ def matches_filters?(node)
52
+ if options[:text]
53
+ regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text])
54
+ return false if not node.text.match(regexp)
55
+ end
56
+ return false if options[:visible] and not node.visible?
57
+ selector.custom_filters.each do |name, block|
58
+ return false if options.has_key?(name) and not block.call(node, options[name])
59
+ end
60
+ true
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,126 @@
1
+ class Capybara::RackTest::Browser
2
+ include ::Rack::Test::Methods
3
+
4
+ attr_reader :driver
5
+ attr_accessor :current_host
6
+
7
+ def initialize(driver)
8
+ @driver = driver
9
+ end
10
+
11
+ def app
12
+ driver.app
13
+ end
14
+
15
+ def options
16
+ driver.options
17
+ end
18
+
19
+ def visit(path, attributes = {})
20
+ reset_host!
21
+ process(:get, path, attributes)
22
+ follow_redirects!
23
+ end
24
+
25
+ def submit(method, path, attributes)
26
+ path = request_path if not path or path.empty?
27
+ process(method, path, attributes)
28
+ follow_redirects!
29
+ end
30
+
31
+ def follow(method, path, attributes = {})
32
+ return if path.gsub(/^#{request_path}/, '').start_with?('#')
33
+ process(method, path, attributes)
34
+ follow_redirects!
35
+ end
36
+
37
+ def follow_redirects!
38
+ 5.times do
39
+ process(:get, last_response["Location"]) if last_response.redirect?
40
+ end
41
+ raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if last_response.redirect?
42
+ end
43
+
44
+ def process(method, path, attributes = {})
45
+ new_uri = URI.parse(path)
46
+ method.downcase! unless method.is_a? Symbol
47
+
48
+ if new_uri.host
49
+ @current_host = "#{new_uri.scheme}://#{new_uri.host}"
50
+ @current_host << ":#{new_uri.port}" if new_uri.port != new_uri.default_port
51
+ end
52
+
53
+ if new_uri.relative?
54
+ if path.start_with?('?')
55
+ path = request_path + path
56
+ elsif not path.start_with?('/')
57
+ path = request_path.sub(%r(/[^/]*$), '/') + path
58
+ end
59
+ path = current_host + path
60
+ end
61
+
62
+ reset_cache!
63
+ send(method, path, attributes, env)
64
+ end
65
+
66
+ def current_url
67
+ last_request.url
68
+ rescue Rack::Test::Error
69
+ ""
70
+ end
71
+
72
+ def reset_host!
73
+ @current_host = (Capybara.app_host || Capybara.default_host)
74
+ end
75
+
76
+ def reset_cache!
77
+ @dom = nil
78
+ end
79
+
80
+ def body
81
+ dom.to_xml
82
+ end
83
+
84
+ def dom
85
+ @dom ||= Nokogiri::HTML(source)
86
+ end
87
+
88
+ def find(selector)
89
+ dom.xpath(selector).map { |node| Capybara::RackTest::Node.new(self, node) }
90
+ end
91
+
92
+ def source
93
+ last_response.body
94
+ rescue Rack::Test::Error
95
+ ""
96
+ end
97
+
98
+ protected
99
+
100
+ def build_rack_mock_session
101
+ reset_host! unless current_host
102
+ Rack::MockSession.new(app, URI.parse(current_host).host)
103
+ end
104
+
105
+ def request_path
106
+ last_request.path
107
+ rescue Rack::Test::Error
108
+ ""
109
+ end
110
+
111
+ def env
112
+ env = {}
113
+ begin
114
+ if last_response.redirect?
115
+ env["HTTP_REFERER"] = last_request.env["HTTP_REFERER"]
116
+ else
117
+ env["HTTP_REFERER"] = last_request.url
118
+ end
119
+ rescue Rack::Test::Error
120
+ # no request yet
121
+ end
122
+ env.merge!(options[:headers]) if options[:headers]
123
+ env
124
+ end
125
+
126
+ end
@@ -0,0 +1,80 @@
1
+ require 'rack/test'
2
+ require 'rack/utils'
3
+ require 'mime/types'
4
+ require 'nokogiri'
5
+ require 'cgi'
6
+
7
+ class Capybara::RackTest::Driver < Capybara::Driver::Base
8
+ DEFAULT_OPTIONS = {
9
+ :respect_data_method => true
10
+ }
11
+ attr_reader :app, :options
12
+
13
+ def initialize(app, options={})
14
+ raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
15
+ @app = app
16
+ @options = DEFAULT_OPTIONS.merge(options)
17
+ end
18
+
19
+ def browser
20
+ @browser ||= Capybara::RackTest::Browser.new(self)
21
+ end
22
+
23
+ def response
24
+ browser.last_response
25
+ end
26
+
27
+ def request
28
+ browser.last_request
29
+ end
30
+
31
+ def visit(path, attributes = {})
32
+ browser.visit(path, attributes)
33
+ end
34
+
35
+ def submit(method, path, attributes)
36
+ browser.submit(method, path, attributes)
37
+ end
38
+
39
+ def follow(method, path, attributes = {})
40
+ browser.follow(method, path, attributes)
41
+ end
42
+
43
+ def current_url
44
+ browser.current_url
45
+ end
46
+
47
+ def response_headers
48
+ response.headers
49
+ end
50
+
51
+ def status_code
52
+ response.status
53
+ end
54
+
55
+ def find(selector)
56
+ browser.find(selector)
57
+ end
58
+
59
+ def body
60
+ browser.body
61
+ end
62
+
63
+ def source
64
+ browser.source
65
+ end
66
+
67
+ def dom
68
+ browser.dom
69
+ end
70
+
71
+ def reset!
72
+ @browser = nil
73
+ end
74
+
75
+ def get(*args, &block); browser.get(*args, &block); end
76
+ def post(*args, &block); browser.post(*args, &block); end
77
+ def put(*args, &block); browser.put(*args, &block); end
78
+ def delete(*args, &block); browser.delete(*args, &block); end
79
+ def header(key, value); browser.header(key, value); end
80
+ end
@@ -0,0 +1,80 @@
1
+ class Capybara::RackTest::Form < Capybara::RackTest::Node
2
+ # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
3
+ # the class specifically when determing whether to consturct the request as multipart.
4
+ # That check should be based solely on the form element's 'enctype' attribute value,
5
+ # which should probably be provided to Rack::Test in its non-GET request methods.
6
+ class NilUploadedFile < Rack::Test::UploadedFile
7
+ def initialize
8
+ @empty_file = Tempfile.new("nil_uploaded_file")
9
+ @empty_file.close
10
+ end
11
+
12
+ def original_filename; ""; end
13
+ def content_type; "application/octet-stream"; end
14
+ def path; @empty_file.path; end
15
+ end
16
+
17
+ def params(button)
18
+ params = {}
19
+
20
+ native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
21
+ case field.name
22
+ when 'input'
23
+ if %w(radio checkbox).include? field['type']
24
+ merge_param!(params, field['name'].to_s, field['value'].to_s) if field['checked']
25
+ elsif %w(submit image).include? field['type']
26
+ # TO DO identify the click button here (in document order, rather
27
+ # than leaving until the end of the params)
28
+ elsif field['type'] =='file'
29
+ if multipart?
30
+ file = \
31
+ if (value = field['value']).to_s.empty?
32
+ NilUploadedFile.new
33
+ else
34
+ content_type = MIME::Types.type_for(value).first.to_s
35
+ Rack::Test::UploadedFile.new(value, content_type)
36
+ end
37
+ merge_param!(params, field['name'].to_s, file)
38
+ else
39
+ merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
40
+ end
41
+ else
42
+ merge_param!(params, field['name'].to_s, field['value'].to_s)
43
+ end
44
+ when 'select'
45
+ if field['multiple'] == 'multiple'
46
+ options = field.xpath(".//option[@selected]")
47
+ options.each do |option|
48
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
49
+ end
50
+ else
51
+ option = field.xpath(".//option[@selected]").first
52
+ option ||= field.xpath('.//option').first
53
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
54
+ end
55
+ when 'textarea'
56
+ merge_param!(params, field['name'].to_s, field.text.to_s)
57
+ end
58
+ end
59
+ merge_param!(params, button[:name], button[:value] || "") if button[:name]
60
+ params
61
+ end
62
+
63
+ def submit(button)
64
+ driver.submit(method, native['action'].to_s, params(button))
65
+ end
66
+
67
+ def multipart?
68
+ self[:enctype] == "multipart/form-data"
69
+ end
70
+
71
+ private
72
+
73
+ def method
74
+ self[:method] =~ /post/i ? :post : :get
75
+ end
76
+
77
+ def merge_param!(params, key, value)
78
+ Rack::Utils.normalize_params(params, key, value)
79
+ end
80
+ end