david-capybara 0.3.8

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 (88) hide show
  1. data/History.txt +87 -0
  2. data/README.rdoc +389 -0
  3. data/lib/capybara.rb +52 -0
  4. data/lib/capybara/cucumber.rb +32 -0
  5. data/lib/capybara/driver/base.rb +48 -0
  6. data/lib/capybara/driver/celerity_driver.rb +143 -0
  7. data/lib/capybara/driver/culerity_driver.rb +25 -0
  8. data/lib/capybara/driver/rack_test_driver.rb +272 -0
  9. data/lib/capybara/driver/selenium_driver.rb +156 -0
  10. data/lib/capybara/dsl.rb +60 -0
  11. data/lib/capybara/node.rb +60 -0
  12. data/lib/capybara/rails.rb +17 -0
  13. data/lib/capybara/save_and_open_page.rb +33 -0
  14. data/lib/capybara/searchable.rb +54 -0
  15. data/lib/capybara/server.rb +114 -0
  16. data/lib/capybara/session.rb +262 -0
  17. data/lib/capybara/spec/driver.rb +162 -0
  18. data/lib/capybara/spec/fixtures/capybara.jpg +0 -0
  19. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  20. data/lib/capybara/spec/public/canvas.jpg +0 -0
  21. data/lib/capybara/spec/public/jquery-ui.js +35 -0
  22. data/lib/capybara/spec/public/jquery.js +19 -0
  23. data/lib/capybara/spec/public/test.js +33 -0
  24. data/lib/capybara/spec/session.rb +81 -0
  25. data/lib/capybara/spec/session/all_spec.rb +69 -0
  26. data/lib/capybara/spec/session/attach_file_spec.rb +64 -0
  27. data/lib/capybara/spec/session/check_spec.rb +67 -0
  28. data/lib/capybara/spec/session/choose_spec.rb +26 -0
  29. data/lib/capybara/spec/session/click_button_spec.rb +236 -0
  30. data/lib/capybara/spec/session/click_link_spec.rb +118 -0
  31. data/lib/capybara/spec/session/click_spec.rb +24 -0
  32. data/lib/capybara/spec/session/current_url_spec.rb +8 -0
  33. data/lib/capybara/spec/session/fill_in_spec.rb +108 -0
  34. data/lib/capybara/spec/session/find_button_spec.rb +16 -0
  35. data/lib/capybara/spec/session/find_by_id_spec.rb +16 -0
  36. data/lib/capybara/spec/session/find_field_spec.rb +22 -0
  37. data/lib/capybara/spec/session/find_link_spec.rb +17 -0
  38. data/lib/capybara/spec/session/find_spec.rb +57 -0
  39. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  40. data/lib/capybara/spec/session/has_content_spec.rb +106 -0
  41. data/lib/capybara/spec/session/has_css_spec.rb +107 -0
  42. data/lib/capybara/spec/session/has_field_spec.rb +96 -0
  43. data/lib/capybara/spec/session/has_link_spec.rb +33 -0
  44. data/lib/capybara/spec/session/has_select_spec.rb +89 -0
  45. data/lib/capybara/spec/session/has_table_spec.rb +96 -0
  46. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  47. data/lib/capybara/spec/session/headers.rb +19 -0
  48. data/lib/capybara/spec/session/javascript.rb +204 -0
  49. data/lib/capybara/spec/session/locate_spec.rb +59 -0
  50. data/lib/capybara/spec/session/select_spec.rb +83 -0
  51. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  52. data/lib/capybara/spec/session/unselect_spec.rb +54 -0
  53. data/lib/capybara/spec/session/within_spec.rb +153 -0
  54. data/lib/capybara/spec/test_app.rb +75 -0
  55. data/lib/capybara/spec/views/buttons.erb +4 -0
  56. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  57. data/lib/capybara/spec/views/form.erb +235 -0
  58. data/lib/capybara/spec/views/frame_one.erb +8 -0
  59. data/lib/capybara/spec/views/frame_two.erb +8 -0
  60. data/lib/capybara/spec/views/postback.erb +13 -0
  61. data/lib/capybara/spec/views/tables.erb +122 -0
  62. data/lib/capybara/spec/views/with_html.erb +46 -0
  63. data/lib/capybara/spec/views/with_js.erb +39 -0
  64. data/lib/capybara/spec/views/with_scope.erb +36 -0
  65. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  66. data/lib/capybara/spec/views/within_frames.erb +10 -0
  67. data/lib/capybara/version.rb +3 -0
  68. data/lib/capybara/wait_until.rb +28 -0
  69. data/lib/capybara/xpath.rb +180 -0
  70. data/spec/capybara_spec.rb +18 -0
  71. data/spec/driver/celerity_driver_spec.rb +16 -0
  72. data/spec/driver/culerity_driver_spec.rb +12 -0
  73. data/spec/driver/rack_test_driver_spec.rb +17 -0
  74. data/spec/driver/remote_culerity_driver_spec.rb +23 -0
  75. data/spec/driver/remote_selenium_driver_spec.rb +18 -0
  76. data/spec/driver/selenium_driver_spec.rb +11 -0
  77. data/spec/dsl_spec.rb +140 -0
  78. data/spec/save_and_open_page_spec.rb +43 -0
  79. data/spec/searchable_spec.rb +66 -0
  80. data/spec/server_spec.rb +53 -0
  81. data/spec/session/celerity_session_spec.rb +27 -0
  82. data/spec/session/culerity_session_spec.rb +25 -0
  83. data/spec/session/rack_test_session_spec.rb +33 -0
  84. data/spec/session/selenium_session_spec.rb +25 -0
  85. data/spec/spec_helper.rb +19 -0
  86. data/spec/wait_until_spec.rb +28 -0
  87. data/spec/xpath_spec.rb +180 -0
  88. metadata +260 -0
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title>This is the title of frame one</title>
4
+ </head>
5
+ <body>
6
+ <div id="divInFrameOne">This is the text of divInFrameOne</div>
7
+ </body>
8
+ </html>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title>This is the title of frame two</title>
4
+ </head>
5
+ <body>
6
+ <div id="divInFrameTwo">This is the text of divInFrameTwo</div>
7
+ </body>
8
+ </html>
@@ -0,0 +1,13 @@
1
+ <h1>Postback</h1>
2
+
3
+ <form method="get">
4
+ <p>
5
+ <input type="submit" value="With no action">
6
+ </p>
7
+ </form>
8
+
9
+ <form action="" method="get">
10
+ <p>
11
+ <input type="submit" value="With blank action">
12
+ </p>
13
+ </form>
@@ -0,0 +1,122 @@
1
+ <form action="/form" method="post">
2
+ <table id="agent_table">
3
+ <caption>Agent</caption>
4
+
5
+ <tr>
6
+ <td>
7
+ <label for="form_agent_name">Name</label>
8
+ </td>
9
+ <td>
10
+ <input type="text" name="form[agent_name]" value="James" id="form_agent_name"/>
11
+ </td>
12
+ </tr>
13
+
14
+ <tr>
15
+ <td colspan="2">
16
+ <input type="submit" value="Create"/>
17
+ </td>
18
+ </tr>
19
+ </table>
20
+ </form>
21
+
22
+ <form action="/form" method="post">
23
+ <table id="girl_table">
24
+ <caption>Girl</caption>
25
+
26
+ <tr>
27
+ <td>
28
+ <label for="form_girl_name">Name</label>
29
+ </td>
30
+ <td>
31
+ <input type="text" name="form[girl_name]" value="Vesper" id="form_girl_name"/>
32
+ </td>
33
+ </tr>
34
+
35
+ <tr>
36
+ <td colspan="2">
37
+ <input type="submit" value="Create"/>
38
+ </td>
39
+ </tr>
40
+ </table>
41
+ </form>
42
+
43
+ <form action="/form" method="post">
44
+ <table id="villain_table">
45
+ <caption>Villain</caption>
46
+
47
+ <tr>
48
+ <td>
49
+ <label for="form_villain_name">Name</label>
50
+ </td>
51
+ <td>
52
+ <input type="text" name="form[villain_name]" value="Ernst" id="form_villain_name"/>
53
+ </td>
54
+ </tr>
55
+
56
+ <tr>
57
+ <td colspan="2">
58
+ <input type="submit" value="Create"/>
59
+ </td>
60
+ </tr>
61
+ </table>
62
+ </form>
63
+
64
+ <table>
65
+ <caption>Ransom</caption>
66
+
67
+ <thead>
68
+ <tr>
69
+ <th>Year</th>
70
+ <th>Governmental</th>
71
+ <th>Private</th>
72
+ </tr>
73
+ </thead>
74
+
75
+ <tbody>
76
+ <tr>
77
+ <th scope="row">2007</th>
78
+ <td>$300</td>
79
+ <td>$100</td>
80
+ </tr>
81
+ <tr>
82
+ <th scope="row">2008</th>
83
+ <td>$123</td>
84
+ <td>$897</td>
85
+ </tr>
86
+ <tr>
87
+ <th scope="row">2009</th>
88
+ <td>$543</td>
89
+ <td>$99</td>
90
+ </tr>
91
+ </tbody>
92
+ </table>
93
+
94
+ <table>
95
+ <caption>Deaths</caption>
96
+
97
+ <thead>
98
+ <tr>
99
+ <th>Year</th>
100
+ <th>Sharks with lasers</th>
101
+ <th>Flaming volcano</th>
102
+ </tr>
103
+ </thead>
104
+
105
+ <tbody>
106
+ <tr>
107
+ <th scope="row">2007</th>
108
+ <td>66</td>
109
+ <td>7</td>
110
+ </tr>
111
+ <tr>
112
+ <th scope="row">2008</th>
113
+ <td>123</td>
114
+ <td>12</td>
115
+ </tr>
116
+ <tr>
117
+ <th scope="row">2009</th>
118
+ <td>913</td>
119
+ <td>13</td>
120
+ </tr>
121
+ </tbody>
122
+ </table>
@@ -0,0 +1,46 @@
1
+ <h1>This is a test</h1>
2
+
3
+ <p id="first">
4
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
5
+ tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
6
+ et dolore magna aliqua. Ut enim ad minim veniam,
7
+ quis nostrud exercitation <a href="/foo" id="foo">ullamco</a> laboris nisi
8
+ ut aliquip ex ea commodo consequat.
9
+ <a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="awesome image" /></a>
10
+ </p>
11
+
12
+ <p id="second">
13
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
14
+ dolore eu fugiat <a href="/redirect" id="red">Redirect</a> pariatur. Excepteur sint occaecat cupidatat non proident,
15
+ sunt in culpa qui officia
16
+ text with
17
+ whitespace
18
+ id est laborum.
19
+ </p>
20
+
21
+ <p>
22
+ <input type="text" id="test_field" value="monkey"/>
23
+ <a href="/redirect_back">BackToMyself</a>
24
+ <a title="twas a fine link" href="/redirect">A link came first</a>
25
+ <a title="a fine link" href="/with_simple_html">A link</a>
26
+ <a title="a fine link with data method" data-method="delete" href="/delete">A link with data-method</a>
27
+ <a>No Href</a>
28
+ <a href="">Blank Href</a>
29
+ <a href="#">Blank Anchor</a>
30
+ <a href="#anchor">Anchor</a>
31
+ <a href="/with_simple_html#anchor">Anchor on different page</a>
32
+ <a href="/with_html#anchor">Anchor on same page</a>
33
+ <input type="text" value="" id="test_field">
34
+ <input type="text" checked="checked" id="checked_field">
35
+ <a href="/redirect"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="very fine image" /></a>
36
+ <a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="fine image" /></a>
37
+ <img src="canvas.jpg" usemap="#testmap" height="523" witdh="501">
38
+ <map name="testmap">
39
+ <area href="/with_simple_html" shape="rect" coords="109,240,129,259" alt="Image Map"/>
40
+ </map>
41
+ </p>
42
+
43
+ <div id="hidden" style="display: none;">
44
+ <div id="hidden_via_ancestor">Inside element with hidden ancestor</div>
45
+ <a href="/with_simple_html" title="awesome title" class="simple">hidden link</a>
46
+ </div>
@@ -0,0 +1,39 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
2
+ <head>
3
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
4
+ <title>with_js</title>
5
+ <script src="/jquery.js" type="text/javascript" charset="utf-8"></script>
6
+ <script src="/jquery-ui.js" type="text/javascript" charset="utf-8"></script>
7
+ <script src="/test.js" type="text/javascript" charset="utf-8"></script>
8
+ </head>
9
+
10
+ <body id="with_js">
11
+ <h1>FooBar</h1>
12
+
13
+ <p id="change">This is text</p>
14
+ <div id="drag">
15
+ <p>This is a draggable element.</p>
16
+ </div>
17
+ <div id="drop">
18
+ <p>It should be dropped here.</p>
19
+ </div>
20
+
21
+ <p><a href="#" id="clickable">Click me</a></p>
22
+
23
+ <p>
24
+ <select id="waiter">
25
+ <option>Foo</option>
26
+ <option>My Waiting Option</option>
27
+ </select>
28
+ </p>
29
+
30
+ <p>
31
+ <input type="text" name="with_focus_event" value="" id="with_focus_event"/>
32
+ </p>
33
+
34
+ <p>
35
+ <input type="checkbox" id="checkbox_with_event"/>
36
+ </p>
37
+ </body>
38
+ </html>
39
+
@@ -0,0 +1,36 @@
1
+ <h1>This page is used for testing various scopes</h1>
2
+
3
+ <p id="for_foo">
4
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
5
+ incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia.
6
+ <a href="/redirect">Go</a>
7
+ </p>
8
+
9
+ <div id="for_bar">
10
+ <ul>
11
+ <li>With Simple HTML: <a href="/with_simple_html">Go</a>
12
+ <form action="/redirect" method="post" accept-charset="utf-8">
13
+ <p>
14
+ <label for="simple_first_name">First Name</label>
15
+ <input type="text" name="first_name" value="John" id="simple_first_name"/>
16
+ </p>
17
+ <p><input type="submit" value="Go"/></p>
18
+ </form>
19
+ </li>
20
+ <li>Bar: <a href="/foo">Go</a>
21
+ <form action="/form" method="post" accept-charset="utf-8">
22
+ <p>
23
+ <label for="bar_first_name">First Name</label>
24
+ <input type="text" name="form[first_name]" value="Peter" id="bar_first_name"/>
25
+ </p>
26
+ <p><input type="submit" value="Go"/></p>
27
+ </form>
28
+ </li>
29
+ </ul>
30
+ </div>
31
+
32
+ <div id="another_foo">
33
+ <ul>
34
+ <li>With Simple HTML: <a href="/">Go</a>
35
+ </ul>
36
+ </div>
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <title>With Frames</title>
4
+ </head>
5
+ <body>
6
+ <div id="divInMainWindow">This is the text for divInMainWindow</div>
7
+ <iframe src="/frame_one" id="frameOne"></iframe>
8
+ <iframe src="/frame_two" id="frameTwo"></iframe>
9
+ </body>
10
+ </html>
@@ -0,0 +1,3 @@
1
+ module Capybara
2
+ VERSION = '0.3.8'
3
+ end
@@ -0,0 +1,28 @@
1
+ module Capybara
2
+ #Provides timeout similar to standard library Timeout, but avoids threads
3
+ class WaitUntil
4
+
5
+ class << self
6
+
7
+ def timeout(seconds = 1, driver = nil, &block)
8
+ start_time = Time.now
9
+
10
+ result = nil
11
+
12
+ until result
13
+ return result if result = yield
14
+
15
+ delay = seconds - (Time.now - start_time)
16
+ if delay <= 0
17
+ raise TimeoutError
18
+ end
19
+
20
+ driver && driver.wait_until(delay)
21
+
22
+ sleep(0.05)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,180 @@
1
+ module Capybara
2
+ # this is a class for generating XPath queries, use it like this:
3
+ # Xpath.text_field('foo').link('blah').to_s
4
+ # this will generate an XPath that matches either a text field or a link
5
+ class XPath
6
+
7
+ class << self
8
+ def escape(string)
9
+ if string.include?("'")
10
+ string = string.split("'", -1).map do |substr|
11
+ "'#{substr}'"
12
+ end.join(%q{,"'",})
13
+ "concat(#{string})"
14
+ else
15
+ "'#{string}'"
16
+ end
17
+ end
18
+
19
+ def wrap(path)
20
+ if path.is_a?(self)
21
+ path
22
+ else
23
+ new(path.to_s)
24
+ end
25
+ end
26
+
27
+ def respond_to?(method)
28
+ new.respond_to?(method)
29
+ end
30
+
31
+ def method_missing(*args)
32
+ new.send(*args)
33
+ end
34
+ end
35
+
36
+ attr_reader :paths
37
+
38
+ def initialize(*paths)
39
+ @paths = paths
40
+ end
41
+
42
+ def scope(scope)
43
+ XPath.new(*paths.map { |p| scope + p })
44
+ end
45
+
46
+ def to_s
47
+ @paths.join(' | ')
48
+ end
49
+
50
+ def append(path)
51
+ XPath.new(*[@paths, XPath.wrap(path).paths].flatten)
52
+ end
53
+
54
+ def prepend(path)
55
+ XPath.new(*[XPath.wrap(path).paths, @paths].flatten)
56
+ end
57
+
58
+ def from_css(css)
59
+ append(Nokogiri::CSS.xpath_for(css).first)
60
+ end
61
+ alias_method :for_css, :from_css
62
+
63
+ def field(locator, options={})
64
+ if options[:with]
65
+ fillable_field(locator, options)
66
+ else
67
+ xpath = fillable_field(locator)
68
+ xpath = xpath.input_field(:file, locator, options)
69
+ xpath = xpath.checkbox(locator, options)
70
+ xpath = xpath.radio_button(locator, options)
71
+ xpath.select(locator, options)
72
+ end
73
+ end
74
+
75
+ def fillable_field(locator, options={})
76
+ text_area(locator, options).text_field(locator, options)
77
+ end
78
+
79
+ def content(locator)
80
+ append("/descendant-or-self::*[contains(normalize-space(.),#{s(locator)})]")
81
+ end
82
+
83
+ def table(locator, options={})
84
+ conditions = ""
85
+ if options[:rows]
86
+ row_conditions = options[:rows].map do |row|
87
+ row = row.map { |column| "*[self::td or self::th][text()=#{s(column)}]" }.join(sibling)
88
+ "tr[./#{row}]"
89
+ end.join(sibling)
90
+ conditions << "[.//#{row_conditions}]"
91
+ end
92
+ append("//table[@id=#{s(locator)} or contains(caption,#{s(locator)})]#{conditions}")
93
+ end
94
+
95
+ def fieldset(locator)
96
+ append("//fieldset[@id=#{s(locator)} or contains(legend,#{s(locator)})]")
97
+ end
98
+
99
+ def link(locator)
100
+ xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)}) or img[contains(@alt,#{s(locator)})]]")
101
+ xpath = xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)} or img[@alt=#{s(locator)}]]")
102
+ xpath.prepend("//area[@href][@alt=#{s(locator)} or @coords=#{s(locator)}]")
103
+ end
104
+
105
+ def button(locator)
106
+ xpath = append("//input[@type='submit' or @type='image' or @type='button'][@id=#{s(locator)} or contains(@value,#{s(locator)})]")
107
+ xpath = xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]")
108
+ xpath = xpath.prepend("//input[@type='submit' or @type='image' or @type='button'][@value=#{s(locator)}]")
109
+ xpath = xpath.prepend("//input[@type='image'][@alt=#{s(locator)} or contains(@alt,#{s(locator)})]")
110
+ xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]")
111
+ end
112
+
113
+ def text_field(locator, options={})
114
+ options = options.merge(:value => options[:with]) if options.has_key?(:with)
115
+ add_field(locator, "//input[@type!='radio' and @type!='checkbox' and @type!='hidden']", options)
116
+ end
117
+
118
+ def text_area(locator, options={})
119
+ options = options.merge(:text => options[:with]) if options.has_key?(:with)
120
+ add_field(locator, "//textarea", options)
121
+ end
122
+
123
+ def select(locator, options={})
124
+ add_field(locator, "//select", options)
125
+ end
126
+
127
+ def checkbox(locator, options={})
128
+ input_field(:checkbox, locator, options)
129
+ end
130
+
131
+ def radio_button(locator, options={})
132
+ input_field(:radio, locator, options)
133
+ end
134
+
135
+ def file_field(locator, options={})
136
+ input_field(:file, locator, options)
137
+ end
138
+
139
+ protected
140
+
141
+ def input_field(type, locator, options={})
142
+ options = options.merge(:value => options[:with]) if options.has_key?(:with)
143
+ add_field(locator, "//input[@type='#{type}']", options)
144
+ end
145
+
146
+ # place this between to nodes to indicate that they should be siblings
147
+ def sibling
148
+ '/following-sibling::*[1]/self::'
149
+ end
150
+
151
+ def add_field(locator, field, options={})
152
+ postfix = extract_postfix(options)
153
+ xpath = append("#{field}[@id=#{s(locator)}]#{postfix}")
154
+ xpath = xpath.append("#{field}[@name=#{s(locator)}]#{postfix}")
155
+ xpath = xpath.append("#{field}[@id=//label[contains(.,#{s(locator)})]/@for]#{postfix}")
156
+ xpath = xpath.append("//label[contains(.,#{s(locator)})]#{field}#{postfix}")
157
+ xpath.prepend("#{field}[@id=//label[text()=#{s(locator)}]/@for]#{postfix}")
158
+ end
159
+
160
+ def extract_postfix(options)
161
+ options.inject("") do |postfix, (key, value)|
162
+ case key
163
+ when :value then postfix += "[@value=#{s(value)}]"
164
+ when :text then postfix += "[text()=#{s(value)}]"
165
+ when :checked then postfix += "[@checked]"
166
+ when :unchecked then postfix += "[not(@checked)]"
167
+ when :options then postfix += value.map { |o| "[.//option/text()=#{s(o)}]" }.join
168
+ when :selected then postfix += [value].flatten.map { |o| "[.//option[@selected]/text()=#{s(o)}]" }.join
169
+ end
170
+ postfix
171
+ end
172
+ end
173
+
174
+ # Sanitize a String for putting it into an xpath query
175
+ def s(string)
176
+ XPath.escape(string)
177
+ end
178
+
179
+ end
180
+ end