wvanbergen-rspec_form_matcher 0.1.0
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.
- data/.gitignore +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +72 -0
- data/Rakefile +24 -0
- data/lib/rspec_form_matcher.rb +159 -0
- data/rspec_form_matcher.gemspec +23 -0
- data/spec/form_matcher_spec.rb +108 -0
- data/spec/spec_helper.rb +11 -0
- metadata +75 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Willem van Bergen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= RSpec FormMatcher
|
2
|
+
|
3
|
+
An RSpec matcher that checks views for the presence of a form, including
|
4
|
+
the presence of specified input fields that should be within the form tag.
|
5
|
+
This can be especially useful to test forms that submit to an external
|
6
|
+
website and thus cannot be included in a Cucumber story.
|
7
|
+
|
8
|
+
== Setup
|
9
|
+
|
10
|
+
Install the gem:
|
11
|
+
|
12
|
+
gem install wvanbergen-rspec_form_matcher -s http://gems.github.com
|
13
|
+
|
14
|
+
And include it in your <tt>spec_helper.rb</tt>:
|
15
|
+
|
16
|
+
require 'rspec_form_matcher'
|
17
|
+
Spec::Runner.configure do |config|
|
18
|
+
config.include Spec::Matchers::FormMatcher, :type => :views
|
19
|
+
end
|
20
|
+
|
21
|
+
The <b>libxml-ruby</b> gem is required to perform the XPath Query that is used
|
22
|
+
for checking the form.
|
23
|
+
|
24
|
+
== Example
|
25
|
+
|
26
|
+
With following HTML snippet:
|
27
|
+
|
28
|
+
<form action="http://example.com/submit" method="post">
|
29
|
+
<input type="hidden" name="foo" value="bar" />
|
30
|
+
<input type="submit" />
|
31
|
+
</form>
|
32
|
+
<input type="hidden" name="outside" value="form" />
|
33
|
+
|
34
|
+
Consider the following specs:
|
35
|
+
|
36
|
+
response.should have_form(:post => 'http://example.com/submit') # succeeds
|
37
|
+
response.should have_form(:put => 'http://example.com/submit') # fails
|
38
|
+
response.should have_form(:post => 'http://other.com/submit') # fails
|
39
|
+
|
40
|
+
response.should have_form(:post => 'http://example.com/submit').with(
|
41
|
+
:hidden, 'foo', 'bar').and_with(:submit_button) # succeeds
|
42
|
+
|
43
|
+
response.should have_form(:post => 'http://example.com/submit').with(
|
44
|
+
:hidden, 'outside', 'form').and_with(:submit_button) # fails
|
45
|
+
|
46
|
+
An alternative syntax using a block is available, which is more suitable
|
47
|
+
to check complex forms. Note the use of curly braces instead of do ... end,
|
48
|
+
to make sure that the block binds to the <tt>have_form</tt> call.
|
49
|
+
|
50
|
+
response.should have_form(:post => 'http://example.com/submit') { |form|
|
51
|
+
form.hidden 'foo', 'bar'
|
52
|
+
form.submit_button
|
53
|
+
}
|
54
|
+
|
55
|
+
See the <tt>spec/form_matcher_spec.rb</tt> file for more examples.
|
56
|
+
|
57
|
+
== Supported checks
|
58
|
+
|
59
|
+
have_form(method => action) {
|
60
|
+
form.hidden('name') # hidden field with name 'name'
|
61
|
+
form.hidden('name', 'value') # field with value
|
62
|
+
|
63
|
+
form.text('username') # named text input
|
64
|
+
form.text('username', 'default') # checks default value
|
65
|
+
form.password('password') # named password field
|
66
|
+
|
67
|
+
form.checkbox('name', 'value') # checkbox with value
|
68
|
+
form.checkbox('name[]', 'value1', 'value2') # array of checkboxes
|
69
|
+
form.radio('name', 'value1', 'value2', ...) # radiobuttons
|
70
|
+
|
71
|
+
form.submit_button # makes sure the form can be submitted
|
72
|
+
}
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
desc "Run all RSpec examples and report the result"
|
4
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
5
|
+
t.spec_files = Dir['spec/**/*_spec.rb']
|
6
|
+
t.spec_opts << '--format specdoc'
|
7
|
+
t.rcov = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def gemspec_file
|
11
|
+
Dir['*.gemspec'].first
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace(:gem) do
|
15
|
+
task(:manifest) do
|
16
|
+
# update the spec file
|
17
|
+
spec = File.read(gemspec_file)
|
18
|
+
spec.gsub!(/^(\s+\w+\.files\s*=\s*)[^\s].*$/) do
|
19
|
+
$1 + `git ls-files`.split("\n").inspect
|
20
|
+
end
|
21
|
+
|
22
|
+
File.open(gemspec_file, 'w') { |f| f << spec }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Spec
|
2
|
+
module Matchers
|
3
|
+
module FormMatcher
|
4
|
+
class HaveForm
|
5
|
+
attr_reader :action, :method, :field, :submit_button
|
6
|
+
|
7
|
+
def initialize(action = nil)
|
8
|
+
case action
|
9
|
+
when Hash then action.each { |m, a| @method, @action = m, a }
|
10
|
+
when String, Regexp then @action = action
|
11
|
+
end
|
12
|
+
|
13
|
+
@tags = []
|
14
|
+
@checks = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def with(method, *args)
|
18
|
+
self.send(method, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :and_with :with
|
22
|
+
|
23
|
+
def input(type, field_name, value = nil)
|
24
|
+
attributes = { :type => type, :name => field_name }
|
25
|
+
attributes[:value] = value if value
|
26
|
+
tag('input', attributes)
|
27
|
+
end
|
28
|
+
|
29
|
+
def tag(tag, attributes = {})
|
30
|
+
@checks << tag_xpath(tag, attributes)
|
31
|
+
return self
|
32
|
+
end
|
33
|
+
|
34
|
+
def hidden(field_name, value = nil)
|
35
|
+
input(:hidden, field_name, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def submit_button
|
39
|
+
button_xpath = tag_xpath('button', :type => 'submit')
|
40
|
+
input_xpath = tag_xpath('input', :type => 'submit')
|
41
|
+
image_xpath = tag_xpath('input', :type => 'image')
|
42
|
+
@checks << "#{input_xpath} or #{button_xpath} or #{image_xpath}"
|
43
|
+
return self
|
44
|
+
end
|
45
|
+
|
46
|
+
def text(field_name, value = nil)
|
47
|
+
input(:text, field_name, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def radio(field_name, *values)
|
51
|
+
if values.empty?
|
52
|
+
input(:radio, field_name)
|
53
|
+
else
|
54
|
+
values.each { |v| input(:radio, field_name, v) }
|
55
|
+
end
|
56
|
+
return self
|
57
|
+
end
|
58
|
+
|
59
|
+
def password(field_name, value = nil)
|
60
|
+
input(:password, field_name, value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def checkbox(field_name, *values)
|
64
|
+
if values.empty?
|
65
|
+
input(:checkbox, field_name)
|
66
|
+
else
|
67
|
+
values.each { |v| input(:checkbox, field_name, v) }
|
68
|
+
end
|
69
|
+
return self
|
70
|
+
end
|
71
|
+
|
72
|
+
def matches?(stringlike)
|
73
|
+
@document = document(stringlike)
|
74
|
+
@xpath = build_xpath_query
|
75
|
+
check_xpath!
|
76
|
+
end
|
77
|
+
|
78
|
+
def failure_message
|
79
|
+
"Specified form (XPath: << #{@xpath} >>) not found in document: \n#{@document.to_s}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def negative_failure_message
|
83
|
+
"Unexpectedly found specified form (XPath: << #{@xpath} >>) in document: \n#{@document.to_s}"
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def tag_xpath(tag, attributes = {})
|
89
|
+
field_xpath = "descendant::#{tag}"
|
90
|
+
attributes.each { |k, v| field_xpath << "[@#{k}='#{v}']" }
|
91
|
+
return field_xpath
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_xpath_query
|
95
|
+
form_xpath = "//form"
|
96
|
+
form_xpath << "[@method='#{method.to_s.downcase}']" if @method # fn:lower-case(@method)
|
97
|
+
form_xpath << "[@action='#{@action}']" if @action
|
98
|
+
form_xpath << "[#{@checks.join('][')}]" unless @checks.empty?
|
99
|
+
|
100
|
+
# puts form_xpath
|
101
|
+
return form_xpath
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def have_form(options = {})
|
106
|
+
form_matcher = HaveForm.new(options)
|
107
|
+
yield(form_matcher) if block_given?
|
108
|
+
return form_matcher
|
109
|
+
end
|
110
|
+
|
111
|
+
module REXML
|
112
|
+
def check_xpath!
|
113
|
+
!@document.elements[@xpath].nil?
|
114
|
+
end
|
115
|
+
|
116
|
+
def document(subject)
|
117
|
+
if String === subject
|
118
|
+
::REXML::Document.new(subject).root
|
119
|
+
elsif subject.respond_to?(:body)
|
120
|
+
::REXML::Document.new(subject.body).root
|
121
|
+
elsif ::REXML::Element === subject
|
122
|
+
subject
|
123
|
+
else
|
124
|
+
raise "Cannot handle this XML input type"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module LibXML
|
130
|
+
def check_xpath!
|
131
|
+
!@document.find_first(@xpath).nil?
|
132
|
+
end
|
133
|
+
|
134
|
+
def document(subject)
|
135
|
+
if String === subject
|
136
|
+
XML::HTMLParser.string(subject).parse
|
137
|
+
elsif subject.respond_to?(:body)
|
138
|
+
XML::HTMLParser.string(subject.body).parse
|
139
|
+
elsif XML::Node === subject
|
140
|
+
subject
|
141
|
+
elsif XML::Document === subject
|
142
|
+
subject
|
143
|
+
else
|
144
|
+
raise "Cannot handle this XML input type"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
begin
|
150
|
+
require 'xml'
|
151
|
+
HaveForm.send(:include, LibXML)
|
152
|
+
rescue LoadError => e
|
153
|
+
require 'rexml/document'
|
154
|
+
HaveForm.send(:include, REXML)
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'rspec_form_matcher'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.date = '2009-08-28'
|
5
|
+
|
6
|
+
s.summary = "RSpec matcher to check forms including their fields."
|
7
|
+
s.description = <<-eos
|
8
|
+
The RSpec form matcher will check a string to see whether a specified form exists with the correct action and method.
|
9
|
+
Moreover, it will check whether the specified fields actually occur within this form tag.
|
10
|
+
eos
|
11
|
+
|
12
|
+
s.author = 'Willem van Bergen'
|
13
|
+
s.email = 'willem@vanbergen.org'
|
14
|
+
s.homepage = 'http://github.com/wvanbergen/rspec_form_matcher'
|
15
|
+
|
16
|
+
s.add_dependency('rspec', ['>= 1.2.4'])
|
17
|
+
|
18
|
+
s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
|
19
|
+
s.extra_rdoc_files = ['README.rdoc']
|
20
|
+
|
21
|
+
s.files = [".gitignore", "LICENSE", "README.rdoc", "Rakefile", "lib/rspec_form_matcher.rb", "rspec_form_matcher.gemspec", "spec/form_matcher_spec.rb", "spec/spec_helper.rb"]
|
22
|
+
s.test_files = s.files.select { |file| file =~ /^spec\/.*_spec\.rb$/ }
|
23
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/spec_helper.rb"
|
2
|
+
|
3
|
+
describe Spec::Matchers::FormMatcher do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@correct_form = <<-eos
|
7
|
+
<html>
|
8
|
+
<form action="/endpoint" method="post">
|
9
|
+
<input type="hidden" name="foo" value="bar" />
|
10
|
+
|
11
|
+
<input type="radio" name="choice" value="1" />
|
12
|
+
<input type="radio" name="choice" value="2" />
|
13
|
+
|
14
|
+
<input type="hidden" name="select" value="0" />
|
15
|
+
<input type="checkbox" name="select" value="1" />
|
16
|
+
|
17
|
+
<input type="text" name="username" />
|
18
|
+
<input type="password" name="password" />
|
19
|
+
|
20
|
+
<button type="submit"> Submit! </button>
|
21
|
+
</form>
|
22
|
+
</html>
|
23
|
+
eos
|
24
|
+
|
25
|
+
@wrong_form = <<-eos
|
26
|
+
<html>
|
27
|
+
<form action="/endpoint" method="post"></form>
|
28
|
+
|
29
|
+
<input type="hidden" name="foo" value="bar" />
|
30
|
+
|
31
|
+
<input type="radio" name="choice" value="1" />
|
32
|
+
<input type="radio" name="choice" value="2" />
|
33
|
+
|
34
|
+
<input type="hidden" name="select" value="0" />
|
35
|
+
<input type="checkbox" name="select" value="1" />
|
36
|
+
|
37
|
+
<input type="text" name="username" />
|
38
|
+
<input type="password" name="password" />
|
39
|
+
|
40
|
+
<button type="submit"> Submit! </button>
|
41
|
+
</html>
|
42
|
+
eos
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should assert the presence of the form correctly" do
|
47
|
+
@correct_form.should have_form(:post => '/endpoint')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not assert the presence of the form with a wrong HTTP method" do
|
51
|
+
@correct_form.should_not have_form(:put => '/endpoint')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should not assert the presence of the form with a wrong action" do
|
55
|
+
@correct_form.should_not have_form(:post => '/different_endpoint')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should assert the submit button" do
|
59
|
+
@correct_form.should have_form(:post => '/endpoint').with(:submit_button)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not assert the submit button if it is outside the form tag" do
|
63
|
+
@wrong_form.should_not have_form(:post => '/endpoint').with(:submit_button)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should assert the hidden field 'foo' without value" do
|
67
|
+
@correct_form.should have_form(:post => '/endpoint').with(:hidden, 'foo')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should assert the hidden field 'foo' when it is outside the form tag" do
|
71
|
+
@wrong_form.should_not have_form(:post => '/endpoint').with(:hidden, 'foo')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should assert the hidden field 'foo' with value 'bar'" do
|
75
|
+
@correct_form.should have_form(:post => '/endpoint').with(:hidden, 'foo', 'bar')
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should assert the presence of different radio buttons" do
|
79
|
+
@correct_form.should have_form(:post => '/endpoint').with(:radio, 'choice', '1', '2')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not assert the presence of different radio buttons when outside the form tag" do
|
83
|
+
@wrong_form.should_not have_form(:post => '/endpoint').with(:radio, 'choice', '1', '2')
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should assert the presence of different radio buttons with one too much values" do
|
87
|
+
@correct_form.should_not have_form(:post => '/endpoint').with(:radio, 'choice', '1', '2', '3')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should assert the presence of the checkbox" do
|
91
|
+
@correct_form.should have_form(:post => '/endpoint').with(:checkbox, 'select', '1')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not assert the presence of the checkbox when outside the form tag" do
|
95
|
+
@correct_form.should have_form(:post => '/endpoint').with(:checkbox, 'select', '1')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should assert a form using a block syntax" do
|
99
|
+
@correct_form.should have_form(:post => '/endpoint') { |form|
|
100
|
+
form.hidden 'foo', 'bar'
|
101
|
+
form.checkbox 'select', '1'
|
102
|
+
form.radio 'choice', '1', '2'
|
103
|
+
form.text :username
|
104
|
+
form.password :password
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/autorun'
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
6
|
+
require "rspec_form_matcher"
|
7
|
+
|
8
|
+
Spec::Runner.configure do |config|
|
9
|
+
# Include matchers for view specs
|
10
|
+
config.include Spec::Matchers::FormMatcher
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wvanbergen-rspec_form_matcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Willem van Bergen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-28 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.4
|
24
|
+
version:
|
25
|
+
description: The RSpec form matcher will check a string to see whether a specified form exists with the correct action and method. Moreover, it will check whether the specified fields actually occur within this form tag.
|
26
|
+
email: willem@vanbergen.org
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- LICENSE
|
36
|
+
- README.rdoc
|
37
|
+
- Rakefile
|
38
|
+
- lib/rspec_form_matcher.rb
|
39
|
+
- rspec_form_matcher.gemspec
|
40
|
+
- spec/form_matcher_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
has_rdoc: false
|
43
|
+
homepage: http://github.com/wvanbergen/rspec_form_matcher
|
44
|
+
licenses:
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --title
|
48
|
+
- rspec_form_matcher
|
49
|
+
- --main
|
50
|
+
- README.rdoc
|
51
|
+
- --line-numbers
|
52
|
+
- --inline-source
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 2
|
73
|
+
summary: RSpec matcher to check forms including their fields.
|
74
|
+
test_files:
|
75
|
+
- spec/form_matcher_spec.rb
|