capybara 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +42 -0
  3. data/README.rdoc +208 -0
  4. data/Rakefile +33 -0
  5. data/examples/webcat.rb +36 -0
  6. data/lib/capybara.rb +30 -0
  7. data/lib/capybara/core_ext/tcp_socket.rb +26 -0
  8. data/lib/capybara/cucumber.rb +32 -0
  9. data/lib/capybara/driver/culerity_driver.rb +83 -0
  10. data/lib/capybara/driver/firewatir_driver.rb +66 -0
  11. data/lib/capybara/driver/rack_test_driver.rb +151 -0
  12. data/lib/capybara/driver/safariwatir_driver.rb +67 -0
  13. data/lib/capybara/driver/selenium_driver.rb +93 -0
  14. data/lib/capybara/dsl.rb +58 -0
  15. data/lib/capybara/node.rb +36 -0
  16. data/lib/capybara/rails.rb +11 -0
  17. data/lib/capybara/save_and_open_page.rb +25 -0
  18. data/lib/capybara/server.rb +53 -0
  19. data/lib/capybara/session.rb +190 -0
  20. data/script/console +10 -0
  21. data/script/destroy +14 -0
  22. data/script/generate +14 -0
  23. data/spec/driver/culerity_driver_spec.rb +10 -0
  24. data/spec/driver/firewatir_driver_spec.rb +10 -0
  25. data/spec/driver/rack_test_driver_spec.rb +9 -0
  26. data/spec/driver/safariwarit_driver_spec.rb +10 -0
  27. data/spec/driver/selenium_driver_spec.rb +10 -0
  28. data/spec/drivers_spec.rb +72 -0
  29. data/spec/dsl_spec.rb +139 -0
  30. data/spec/fixtures/test_file.txt +1 -0
  31. data/spec/public/jquery.js +19 -0
  32. data/spec/session/culerity_session_spec.rb +23 -0
  33. data/spec/session/rack_test_session_spec.rb +23 -0
  34. data/spec/session/selenium_session_spec.rb +23 -0
  35. data/spec/session_spec.rb +611 -0
  36. data/spec/spec_helper.rb +10 -0
  37. data/spec/test_app.rb +67 -0
  38. data/spec/views/form.erb +117 -0
  39. data/spec/views/with_html.erb +19 -0
  40. data/spec/views/with_js.erb +20 -0
  41. data/spec/views/with_scope.erb +36 -0
  42. data/spec/views/with_simple_html.erb +1 -0
  43. metadata +164 -0
@@ -0,0 +1,66 @@
1
+ require 'watir'
2
+ Watir::Browser.default = "firefox"
3
+
4
+ class Capybara::Driver::FireWatir
5
+ class Node < Struct.new(:node)
6
+ def text
7
+ node.text
8
+ end
9
+
10
+ def attribute(name)
11
+ value = if name.to_sym == :class
12
+ node.class_name
13
+ else
14
+ node.send(name.to_sym)
15
+ end
16
+ return value if value and not value.empty?
17
+ end
18
+
19
+ def click
20
+ node.click
21
+ end
22
+
23
+ def tag_name
24
+ # FIXME: this might be the dumbest way ever of getting the tag name
25
+ # there has to be something better...
26
+ node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
27
+ end
28
+ end
29
+
30
+ attr_reader :app, :rack_server
31
+
32
+ def initialize(app)
33
+ @app = app
34
+ @rack_server = Capybara::Server.new(@app)
35
+ @rack_server.boot
36
+ end
37
+
38
+ def visit(path)
39
+ browser.goto(url(path))
40
+ end
41
+
42
+ def body
43
+ browser.html
44
+ end
45
+
46
+ def find(selector)
47
+ browser.elements_by_xpath(selector).map { |node| Node.new(node) }
48
+ end
49
+
50
+ private
51
+
52
+ def url(path)
53
+ rack_server.url(path)
54
+ end
55
+
56
+ def browser
57
+ unless @_browser
58
+ @_browser = Watir::Browser.new
59
+ at_exit do
60
+ @_browser.exit
61
+ end
62
+ end
63
+ @_browser
64
+ end
65
+
66
+ end
@@ -0,0 +1,151 @@
1
+ require 'rack/test'
2
+ require 'nokogiri'
3
+
4
+ class Capybara::Driver::RackTest
5
+ class Node < Capybara::Node
6
+ def text
7
+ node.text
8
+ end
9
+
10
+ def [](name)
11
+ value = node[name.to_s]
12
+ return value.to_s if value
13
+ end
14
+
15
+ def set(value)
16
+ if tag_name == 'input' and %w(text password hidden file).include?(type)
17
+ node['value'] = value.to_s
18
+ elsif tag_name == 'input' and type == 'radio'
19
+ driver.html.xpath("//input[@name='#{self[:name]}']").each { |node| node.remove_attribute("checked") }
20
+ node['checked'] = 'checked'
21
+ elsif tag_name == 'input' and type == 'checkbox'
22
+ if value
23
+ node['checked'] = 'checked'
24
+ else
25
+ node.remove_attribute('checked')
26
+ end
27
+ elsif tag_name == "textarea"
28
+ node.content = value.to_s
29
+ end
30
+ end
31
+
32
+ def select(option)
33
+ node.xpath(".//option").each { |node| node.remove_attribute("selected") }
34
+ node.xpath(".//option[contains(.,'#{option}')]").first["selected"] = 'selected'
35
+ end
36
+
37
+ def click
38
+ if tag_name == 'a'
39
+ driver.visit(self[:href])
40
+ elsif tag_name == 'input' and %w(submit image).include?(type)
41
+ Form.new(driver, form).submit(self)
42
+ end
43
+ end
44
+
45
+ def tag_name
46
+ node.node_name
47
+ end
48
+
49
+ private
50
+
51
+ def type
52
+ self[:type]
53
+ end
54
+
55
+ def form
56
+ node.ancestors('form').first
57
+ end
58
+ end
59
+
60
+ class Form < Node
61
+ def params(button)
62
+ params = []
63
+ params += node.xpath(".//input[@type='text']", ".//input[@type='hidden']", ".//input[@type='password']").inject([]) do |agg, input|
64
+ agg << [input['name'].to_s, input['value'].to_s]
65
+ agg
66
+ end
67
+ params += node.xpath(".//textarea").inject([]) do |agg, textarea|
68
+ agg << [textarea['name'].to_s, textarea.text.to_s]
69
+ agg
70
+ end
71
+ params += node.xpath(".//input[@type='radio']").inject([]) do |agg, input|
72
+ agg << [input['name'].to_s, input['value'].to_s] if input['checked']
73
+ agg
74
+ end
75
+ params += node.xpath(".//input[@type='checkbox']").inject([]) do |agg, input|
76
+ agg << [input['name'].to_s, input['value'].to_s] if input['checked']
77
+ agg
78
+ end
79
+ params += node.xpath(".//select").inject([]) do |agg, select|
80
+ option = select.xpath(".//option[@selected]").first
81
+ option ||= select.xpath('.//option').first
82
+ agg << [select['name'].to_s, (option['value'] || option.text).to_s] if option
83
+ agg
84
+ end
85
+ params += node.xpath(".//input[@type='file']").inject([]) do |agg, input|
86
+ if multipart?
87
+ agg << [input['name'].to_s, Rack::Test::UploadedFile.new(input['value'].to_s)]
88
+ else
89
+ agg << [input['name'].to_s, File.basename(input['value'].to_s)]
90
+ end
91
+ agg
92
+ end
93
+ params.push [button[:name], button[:value]] if button[:name]
94
+ if multipart?
95
+ params.inject({}) { |agg, (key, value)| agg[key] = value; agg }
96
+ else
97
+ params.map { |key, value| "#{key}=#{value}" }.join('&')
98
+ end
99
+ end
100
+
101
+ def submit(button)
102
+ if post?
103
+ driver.submit(node['action'].to_s, params(button))
104
+ else
105
+ driver.visit(node['action'].to_s.split('?').first + '?' + params(button))
106
+ end
107
+ end
108
+
109
+ def multipart?
110
+ self[:enctype] == "multipart/form-data"
111
+ end
112
+
113
+ def post?
114
+ self[:method] =~ /post/i
115
+ end
116
+ end
117
+
118
+ include ::Rack::Test::Methods
119
+ attr_reader :app, :html, :body
120
+
121
+ alias_method :response, :last_response
122
+ alias_method :request, :last_request
123
+
124
+ def initialize(app)
125
+ @app = app
126
+ end
127
+
128
+ def visit(path)
129
+ get(path)
130
+ follow_redirect! while response.redirect?
131
+ cache_body
132
+ end
133
+
134
+ def submit(path, attributes)
135
+ post(path, attributes)
136
+ follow_redirect! while response.redirect?
137
+ cache_body
138
+ end
139
+
140
+ def find(selector)
141
+ html.xpath(selector).map { |node| Node.new(self, node) }
142
+ end
143
+
144
+ private
145
+
146
+ def cache_body
147
+ @body = response.body
148
+ @html = Nokogiri::HTML(body)
149
+ end
150
+
151
+ end
@@ -0,0 +1,67 @@
1
+ require 'safariwatir'
2
+
3
+ class Capybara::Driver::SafariWatir
4
+ class Node < Struct.new(:node)
5
+ def text
6
+ node.text
7
+ end
8
+
9
+ def attribute(name)
10
+ value = if name.to_sym == :class
11
+ node.class_name
12
+ else
13
+ node.send(name.to_sym)
14
+ end
15
+ return value if value and not value.empty?
16
+ end
17
+
18
+ def click
19
+ node.click
20
+ end
21
+
22
+ def tag_name
23
+ # FIXME: this might be the dumbest way ever of getting the tag name
24
+ # there has to be something better...
25
+ node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
26
+ end
27
+ end
28
+
29
+ attr_reader :app, :rack_server
30
+
31
+ def initialize(app)
32
+ @app = app
33
+ @rack_server = Capybara::Server.new(@app)
34
+ @rack_server.boot
35
+ end
36
+
37
+ def visit(path)
38
+ browser.goto(url(path))
39
+ end
40
+
41
+ def body
42
+ browser.html
43
+ end
44
+
45
+ def find(selector)
46
+ foo = Struct.new(:what).new
47
+ foo.what = selector
48
+ browser.send(:scripter).operate_by_xpath(foo){}.map { |node| Node.new(node) }
49
+ end
50
+
51
+ private
52
+
53
+ def url(path)
54
+ rack_server.url(path)
55
+ end
56
+
57
+ def browser
58
+ unless @_browser
59
+ @_browser = Watir::Safari.new
60
+ at_exit do
61
+ @_browser.exit
62
+ end
63
+ end
64
+ @_browser
65
+ end
66
+
67
+ end
@@ -0,0 +1,93 @@
1
+ require 'selenium-webdriver'
2
+
3
+ class Capybara::Driver::Selenium
4
+ class Node < Capybara::Node
5
+ def text
6
+ node.text
7
+ end
8
+
9
+ def [](name)
10
+ if name == :value
11
+ node.value
12
+ else
13
+ node.attribute(name)
14
+ end
15
+ rescue Selenium::WebDriver::Error::WebDriverError
16
+ nil
17
+ end
18
+
19
+ def set(value)
20
+ if tag_name == 'input' and %w(text password hidden file).include?(type)
21
+ node.clear
22
+ node.send_keys(value.to_s)
23
+ elsif tag_name == 'input' and type == 'radio'
24
+ node.select
25
+ elsif tag_name == 'input' and type == 'checkbox'
26
+ node.toggle
27
+ elsif tag_name == "textarea"
28
+ node.clear
29
+ node.send_keys(value.to_s)
30
+ end
31
+ end
32
+
33
+ def select(option)
34
+ node.find_element(:xpath, ".//option[contains(.,'#{option}')]").select
35
+ end
36
+
37
+ def click
38
+ node.click
39
+ end
40
+
41
+ def tag_name
42
+ node.tag_name
43
+ end
44
+
45
+ private
46
+
47
+ def type
48
+ self[:type]
49
+ end
50
+
51
+ end
52
+
53
+ attr_reader :app, :rack_server
54
+
55
+ def self.driver
56
+ unless @driver
57
+ @driver = Selenium::WebDriver.for :firefox
58
+ at_exit do
59
+ @driver.quit
60
+ end
61
+ end
62
+ @driver
63
+ end
64
+
65
+ def initialize(app)
66
+ @app = app
67
+ @rack_server = Capybara::Server.new(@app)
68
+ @rack_server.boot
69
+ end
70
+
71
+ def visit(path)
72
+ driver.navigate.to(url(path))
73
+ end
74
+
75
+ def body
76
+ driver.page_source
77
+ end
78
+
79
+ def find(selector)
80
+ driver.find_elements(:xpath, selector).map { |node| Node.new(self, node) }
81
+ end
82
+
83
+ private
84
+
85
+ def url(path)
86
+ rack_server.url(path)
87
+ end
88
+
89
+ def driver
90
+ self.class.driver
91
+ end
92
+
93
+ end
@@ -0,0 +1,58 @@
1
+ module Capybara
2
+ class << self
3
+ attr_writer :default_driver, :current_driver, :javascript_driver
4
+
5
+ attr_accessor :app
6
+
7
+ def default_driver
8
+ @default_driver || :rack_test
9
+ end
10
+
11
+ def current_driver
12
+ @current_driver || default_driver
13
+ end
14
+ alias_method :mode, :current_driver
15
+
16
+ def javascript_driver
17
+ @javascript_driver || :selenium
18
+ end
19
+
20
+ def use_default_driver
21
+ @current_driver = nil
22
+ end
23
+
24
+ def current_session
25
+ session_pool["#{current_driver}#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
26
+ end
27
+
28
+ def reset_sessions!
29
+ @session_pool = nil
30
+ end
31
+
32
+ private
33
+
34
+ def session_pool
35
+ @session_pool ||= {}
36
+ end
37
+ end
38
+
39
+ extend(self)
40
+
41
+ def page
42
+ Capybara.current_session
43
+ end
44
+
45
+ SESSION_METHODS = [
46
+ :visit, :body, :click_link, :click_button, :fill_in, :choose, :has_xpath?, :has_css?,
47
+ :check, :uncheck, :attach_file, :select, :has_content?, :within, :save_and_open_page,
48
+ :find_field, :find_link, :find_button, :field_labeled
49
+ ]
50
+ SESSION_METHODS.each do |method|
51
+ class_eval <<-RUBY, __FILE__, __LINE__+1
52
+ def #{method}(*args, &block)
53
+ page.#{method}(*args, &block)
54
+ end
55
+ RUBY
56
+ end
57
+
58
+ end
@@ -0,0 +1,36 @@
1
+ class Capybara::Node
2
+ attr_reader :driver, :node
3
+
4
+ def initialize(driver, node)
5
+ @driver = driver
6
+ @node = node
7
+ end
8
+
9
+ def text
10
+ raise "Not implemented"
11
+ end
12
+
13
+ def [](name)
14
+ raise "Not implemented"
15
+ end
16
+
17
+ def value
18
+ self[:value]
19
+ end
20
+
21
+ def set(value)
22
+ raise "Not implemented"
23
+ end
24
+
25
+ def select(option)
26
+ raise "Not implemented"
27
+ end
28
+
29
+ def click
30
+ raise "Not implemented"
31
+ end
32
+
33
+ def tag_name
34
+ raise "Not implemented"
35
+ end
36
+ end