page_match 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in page_match.gemspec
4
+ gemspec
@@ -0,0 +1,171 @@
1
+ # page_match
2
+
3
+ RSpec 2 matcher class for building custom Capybara matchers for use in acceptance-level tests.
4
+
5
+ ## Install
6
+
7
+ page_match is distributed as a Ruby gem:
8
+
9
+ sudo gem install page_match
10
+
11
+ ## Using page_match
12
+
13
+ With the rise in popularity of RSpec + Capybara as an acceptance (aka. request-level) test replacement for Cucumber, using custom matchers can go a long way to clean up your examples, as well as improve your test readability.
14
+
15
+ Consider the following HTML, which is rendered as the home page of a standard Rack-based web app:
16
+
17
+ ``` html
18
+ <html>
19
+ <head><title>Home Page</title></head>
20
+ <body>
21
+ <div id="#logout-link">
22
+ <a href="/logout">Logout Joe User</a>
23
+ </div>
24
+ <h1 id="#main-header">Welcome Joe User</h1>
25
+ </body>
26
+ </html>
27
+ ```
28
+
29
+ One fairly common acceptance test would be to verify the text rendered for the logout link and the main header to make sure they include the correct user's name. In vanilla RSpec that might look like this:
30
+
31
+ ```ruby
32
+ describe "Visiting the home page", :type => :request do
33
+ before(:each) do
34
+ @user = User.login(:name => "Joe User")
35
+ end
36
+
37
+ context "The home page" do
38
+ before(:each) do
39
+ visit(root_path)
40
+ end
41
+
42
+ it "should have the correct logout link" do
43
+ within("#logout-link") do
44
+ page.should have_content("Logout #{@user.name}")
45
+ end
46
+ end
47
+
48
+ it "should have the correct main header" do
49
+ within("#main-header") do
50
+ page.should have_content("Welcome #{@user.name}")
51
+ end
52
+ end
53
+ end
54
+ end
55
+ ```
56
+
57
+ Running these examples with the _--format documentation_ option turned on, gives us:
58
+
59
+ ```
60
+ Visiting the home page
61
+ The home page
62
+ should have the correct logout link
63
+ should have the correct main header
64
+ ```
65
+
66
+ On the surface, this example code and the documentation output is just fine. However the code is both hard to refactor and extremely verbose, with the HTML structure traversal code directly in the examples. In addition, the output lines that describe the examples are very generic.
67
+
68
+ Using **page_match** we can create helper methods that wrap the matcher logic for our examples, giving us methods that we can reuse and letting us construct much better example descriptions:
69
+
70
+ ```ruby
71
+ module MatchHelpers
72
+ def have_logout_link_for(name)
73
+ PageMatch.match do |pm|
74
+ pm.have %(a logout link for "#{name}")
75
+ pm.page { within ("#logout-link") { has_content?("Logout #{name}") } }
76
+ end
77
+ end
78
+
79
+ def have_main_header_for(name)
80
+ PageMatch.match do |pm|
81
+ pm.have %(a main header for "#{name}")
82
+ pm.page { within ("#main-header") { has_content?("Welcome #{name}") } }
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ With these helper methods in place, we can now rewrite the acceptance test:
89
+
90
+ ``` ruby
91
+ describe "Visiting the home page", :type => :request do
92
+ before(:each) do
93
+ @user = User.login(:name => "Joe User")
94
+ end
95
+
96
+ subject { page }
97
+
98
+ context "The home page" do
99
+ before(:each) do
100
+ visit(root_path)
101
+ end
102
+
103
+ it { should have_logout_link_for(@user.name) }
104
+ it { should have_main_header_for(@user.name) }
105
+ end
106
+ end
107
+ ```
108
+
109
+ When we run this example file, with the _--format documentation_ option, we get:
110
+
111
+ ```
112
+ Visiting the home page
113
+ The home page
114
+ should have a logout link for "Joe User"
115
+ should have a main header for "Joe User"
116
+ ```
117
+
118
+ The resulting test file is now easier to read, contains examples that make use of custom, domain specific matchers, that when output provide us with context-specific descriptions for those examples.
119
+
120
+ ## Included Helper Methods
121
+
122
+ When you install page_match you will also get a set of included helper methods, for the most common types of rendered page inspections. These include:
123
+
124
+ ### have_link
125
+
126
+ ``` ruby
127
+ it { should have_link(<Link Label>) }
128
+ ```
129
+
130
+ ### have_button
131
+
132
+ ``` ruby
133
+ it { should have_button(<Button Label>) }
134
+ ```
135
+
136
+ ### have_flash_notice
137
+
138
+ ``` ruby
139
+ it { should have_flash_notice(<Flash Text>) }
140
+ ```
141
+
142
+ ### have_form_error
143
+
144
+ ``` ruby
145
+ it { should have_form_error(<Error Text>) }
146
+ ```
147
+
148
+ ### have_text_field
149
+
150
+ ``` ruby
151
+ it { should have_text_field(:form_name, :field_name) }
152
+ ```
153
+
154
+ ### have_text_area
155
+
156
+ ``` ruby
157
+ it { should have_text_area(:form_name, :field_name) }
158
+ ```
159
+
160
+ ### have_check_box
161
+
162
+ ``` ruby
163
+ it { should have_check_box(:form_name, :field_name) }
164
+ ```
165
+
166
+ ### have_select_field
167
+
168
+ ``` ruby
169
+ it { should have_select_field(:form_name, :field_name) }
170
+ ```
171
+
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,7 @@
1
+ require "page_match/page"
2
+
3
+ module PageMatch
4
+ def self.match &block
5
+ PageMatch::Page.match &block
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ module PageMatch
2
+ module Helpers
3
+
4
+ def have_link(label)
5
+ PageMatch.match do |m|
6
+ m.have %(a link named "#{label}")
7
+ m.page { has_link?(label) }
8
+ end
9
+ end
10
+
11
+ def have_button(label)
12
+ PageMatch.match do |m|
13
+ m.have %(a button named "#{label}")
14
+ m.page { has_button?(label) }
15
+ end
16
+ end
17
+
18
+ def have_flash_notice(note)
19
+ PageMatch.match do |m|
20
+ m.have %(a flash notice containing "#{note}")
21
+ m.page { within("#flash_notice") { has_content?(note) } }
22
+ end
23
+ end
24
+
25
+ def have_flash_error(error)
26
+ PageMatch.match do |m|
27
+ m.have %(a flash error containing "#{error}")
28
+ m.page { within("#flash_error") { has_content?(error) } }
29
+ end
30
+ end
31
+
32
+ def have_form_error(error)
33
+ PageMatch.match do |m|
34
+ m.have %(a form error that contains "#{error}")
35
+ m.page { within(".error_messages") { has_content?(error) } }
36
+ end
37
+ end
38
+
39
+ def have_text_field(form, field)
40
+ PageMatch.match do |m|
41
+ m.have %(a text field on the #{form} form for #{field.inspect})
42
+ m.page { has_selector?(:xpath, %(.//input[@id="#{form}_#{field}"][@type="text"])) }
43
+ end
44
+ end
45
+
46
+ def have_text_area(form, field)
47
+ PageMatch.match do |m|
48
+ m.have %(a text area on the #{form} form for #{field.inspect})
49
+ m.page { has_selector?(:xpath, %(.//textarea[@id="#{form}_#{field}"])) }
50
+ end
51
+ end
52
+
53
+ def have_check_box(form, field)
54
+ PageMatch.match do |m|
55
+ m.have %(a checkbox on the #{form} form for #{field.inspect.gsub(/_$/, '')})
56
+ m.page { has_selector?(:xpath, %(.//input[@id="#{form}_#{field}"][@type="checkbox"])) }
57
+ end
58
+ end
59
+
60
+ def have_select_field(form, field)
61
+ PageMatch.match do |m|
62
+ m.have %(a select field on the #{form} form for #{field.inspect})
63
+ m.page { has_selector?(:xpath, %(.//select[@id="#{form}_#{field}"])) }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ module PageMatch
2
+ class Page
3
+ attr_reader :description, :message
4
+ attr_accessor :contain, :not_contain, :did, :did_not
5
+
6
+ def initialize
7
+ @contain = %(expected the page to contain)
8
+ @not_contain = %(expected the page to not contain)
9
+ @did = %(but it did)
10
+ @did_not = %(but it didn't)
11
+ end
12
+
13
+ def self.match(&block)
14
+ page = Page.new
15
+ yield page if block_given?
16
+ page
17
+ end
18
+
19
+ def page(&block)
20
+ @page_block = block
21
+ end
22
+
23
+ def have(msg='')
24
+ @message = msg
25
+ @description = %(have #{msg})
26
+ end
27
+
28
+ def failure_message
29
+ %(#{contain} #{message}, #{did_not})
30
+ end
31
+
32
+ def negative_failure_message
33
+ %(#{not_contain} #{message}, #{did})
34
+ end
35
+
36
+ def matches?(page_instance)
37
+ page_instance.instance_eval(&@page_block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module PageMatch
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "page_match/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "page_match"
7
+ s.version = PageMatch::VERSION
8
+ s.authors = ["Brian V. Hughes"]
9
+ s.email = ["brianvh@mac.com"]
10
+ s.homepage = %(https://github.com/brianvh/page_match)
11
+ s.summary = %(#{s.name}-#{s.version})
12
+ s.description = %(PageMatch: RSpec 2 matcher class for building custom
13
+ Capybara matchers in acceptance tests.)
14
+
15
+ s.add_dependency 'capybara', '>= 1.0.0'
16
+
17
+ s.add_development_dependency 'bundler', '>= 1.0.15'
18
+ s.add_development_dependency 'rspec', '~> 2.6.0'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,49 @@
1
+ require 'rspec'
2
+ require 'page_match'
3
+
4
+ describe "PageMatch module" do
5
+ describe ".match" do
6
+ it "returns an instance of PageMatch::Page" do
7
+ matcher = PageMatch.match { have 'a Page instance' }
8
+ matcher.should be_instance_of(PageMatch::Page)
9
+ end
10
+ end
11
+ end
12
+
13
+ describe PageMatch::Page do
14
+ before(:each) do
15
+ @matcher = PageMatch::Page.new
16
+ end
17
+
18
+ it "initializes with a nil message attribute" do
19
+ @matcher.message.should be_nil
20
+ end
21
+
22
+ context "#matches? against a page object" do
23
+ before(:each) do
24
+ @page = mock(:page)
25
+ end
26
+
27
+ context "for a true match" do
28
+ before(:each) do
29
+ @page.should_receive(:is_true?).once.and_return(true)
30
+ @matcher.page { is_true? }
31
+ end
32
+
33
+ it "returns true" do
34
+ @matcher.should be_matches(@page)
35
+ end
36
+ end
37
+
38
+ context "for a false match" do
39
+ before(:each) do
40
+ @page.should_receive(:is_not_true?).once.and_return(false)
41
+ @matcher.page { is_not_true? }
42
+ end
43
+
44
+ it "returns false" do
45
+ @matcher.should_not be_matches(@page)
46
+ end
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: page_match
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Brian V. Hughes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-10-02 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: capybara
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 23
29
+ segments:
30
+ - 1
31
+ - 0
32
+ - 0
33
+ version: 1.0.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 9
45
+ segments:
46
+ - 1
47
+ - 0
48
+ - 15
49
+ version: 1.0.15
50
+ type: :development
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 23
61
+ segments:
62
+ - 2
63
+ - 6
64
+ - 0
65
+ version: 2.6.0
66
+ type: :development
67
+ version_requirements: *id003
68
+ description: |-
69
+ PageMatch: RSpec 2 matcher class for building custom
70
+ Capybara matchers in acceptance tests.
71
+ email:
72
+ - brianvh@mac.com
73
+ executables: []
74
+
75
+ extensions: []
76
+
77
+ extra_rdoc_files: []
78
+
79
+ files:
80
+ - .gitignore
81
+ - .rspec
82
+ - Gemfile
83
+ - README.md
84
+ - Rakefile
85
+ - lib/page_match.rb
86
+ - lib/page_match/helpers.rb
87
+ - lib/page_match/page.rb
88
+ - lib/page_match/version.rb
89
+ - page_match.gemspec
90
+ - spec/page_spec.rb
91
+ homepage: https://github.com/brianvh/page_match
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.24
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: page_match-0.0.1
124
+ test_files:
125
+ - spec/page_spec.rb