mache 2.0.0 → 2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -1
- data/CHANGELOG.md +18 -0
- data/README.md +122 -31
- data/Rakefile +1 -1
- data/lib/mache/dsl.rb +19 -16
- data/lib/mache/helpers/rails/flash.rb +31 -0
- data/lib/mache/helpers/rails/routes.rb +15 -0
- data/lib/mache/helpers/rails.rb +2 -0
- data/lib/mache/node.rb +3 -3
- data/lib/mache/page.rb +4 -4
- data/lib/mache/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 912b56e289aa3e69473fd758575e2f43afe3818b
|
4
|
+
data.tar.gz: abcf1936d0c0c3cf08c6c27a8148f7107333500b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d6ad5456be773a9fe544700e8377227fd4bd50be9d22baca2d94fccd641c212a3c62189114a3de9b59a757eb1fd39852cc3a67230e288b790636cea1bac9ae3
|
7
|
+
data.tar.gz: 8ad576449372f1966b1fcba9e6db5753cb6d1b41c403307496041958270451b3de0b6f621a9b2c8dfbdbbe1945e99d4416a7ed3f4449cd154a42d5453311f3da
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 2.1.0
|
4
|
+
|
5
|
+
- Add flash and routing helpers.
|
6
|
+
|
7
|
+
## 2.0.0
|
8
|
+
|
9
|
+
- Ensure the klass argument is a subclass of `Node` in the DSL methods.
|
10
|
+
- Remove `Component` class.
|
11
|
+
|
12
|
+
## 1.0.1
|
13
|
+
|
14
|
+
- Update documentation.
|
15
|
+
|
16
|
+
## 1.0.0
|
17
|
+
|
18
|
+
- Initial release.
|
data/README.md
CHANGED
@@ -2,27 +2,41 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/nullobject/mache)
|
4
4
|
|
5
|
-
Mâché (pronounced "mash-ay") helps you to write cleaner and more
|
6
|
-
acceptance tests for your web applications using page objects.
|
5
|
+
Mâché (pronounced "mash-ay") is a tool that helps you to write cleaner and more
|
6
|
+
expressive acceptance tests for your Ruby web applications using page objects.
|
7
|
+
|
8
|
+
## Table of contents
|
9
|
+
|
10
|
+
* [Mâché](#mâché)
|
11
|
+
* [Table of contents](#table-of-contents)
|
12
|
+
* [What is a page object?](#what-is-a-page-object)
|
13
|
+
* [Getting started](#getting-started)
|
14
|
+
* [Elements](#elements)
|
15
|
+
* [Components](#components)
|
16
|
+
* [Helpers](#helpers)
|
17
|
+
* [Example](#example)
|
18
|
+
* [API documentation](#api-documentation)
|
19
|
+
* [Contributing](#contributing)
|
20
|
+
* [License](#license)
|
7
21
|
|
8
22
|
## What is a page object?
|
9
23
|
|
10
24
|
A [page object](https://martinfowler.com/bliki/PageObject.html) is a data
|
11
|
-
structure
|
25
|
+
structure that provides an interface to your web application for the purposes
|
12
26
|
of test automation. For example, it could represent a single HTML page, or
|
13
27
|
perhaps even a fragment of HTML on a page.
|
14
28
|
|
15
|
-
From Martin Fowler:
|
29
|
+
From [Martin Fowler](https://martinfowler.com/bliki/PageObject.html):
|
16
30
|
|
17
31
|
> A page object wraps an HTML page, or fragment, with an application-specific
|
18
32
|
> API, allowing you to manipulate page elements without digging around in the
|
19
33
|
> HTML.
|
20
34
|
|
21
|
-
[Capybara](https://github.com/teamcapybara/capybara) can
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
35
|
+
[Capybara](https://github.com/teamcapybara/capybara) can get us part of the way
|
36
|
+
there. It allows us to work with an API rather than manipulating the HTML
|
37
|
+
directly, but what it provides isn't an *application specific* API. It gives us
|
38
|
+
low-level API methods like `find`, `fill_in`, and `click_button`, but it
|
39
|
+
doesn't provide us with high-level methods to do things like "sign in to the
|
26
40
|
app" or "click the Dashboard item in the navigation bar".
|
27
41
|
|
28
42
|
This is where page objects come in. Using Mâché we can for instance define a
|
@@ -40,11 +54,14 @@ HTML fragment for the welcome page in our app:
|
|
40
54
|
<body>
|
41
55
|
<header>
|
42
56
|
<h1>Welcome</h1>
|
57
|
+
<div id="flash" class="notice">lorem ipsum</div>
|
43
58
|
</header>
|
44
59
|
<nav>
|
45
|
-
<
|
46
|
-
|
47
|
-
|
60
|
+
<ul>
|
61
|
+
<li><a href="/foo" class="selected">foo</a></li>
|
62
|
+
<li><a href="/bar">bar</a></li>
|
63
|
+
<li><a href="/baz">baz</a></li>
|
64
|
+
</ul>
|
48
65
|
</nav>
|
49
66
|
<main>
|
50
67
|
lorem ipsum
|
@@ -58,6 +75,8 @@ the `Mache::Page` class. The only method our class needs to provide is `path`,
|
|
58
75
|
this tells Mâché where to go when we want to visit the page:
|
59
76
|
|
60
77
|
```ruby
|
78
|
+
require "mache"
|
79
|
+
|
61
80
|
class WelcomePage < Mache::Page
|
62
81
|
def path
|
63
82
|
"/welcome"
|
@@ -75,13 +94,7 @@ page.current? # true
|
|
75
94
|
Mâché also handily exposes the Capybara API on our page object:
|
76
95
|
|
77
96
|
```ruby
|
78
|
-
page.find("
|
79
|
-
```
|
80
|
-
|
81
|
-
We can also use the `node` attribute to get the underlying Capybara node object:
|
82
|
-
|
83
|
-
```ruby
|
84
|
-
page.node # <Capybara::Node>
|
97
|
+
page.find("main").text # "lorem ipsum"
|
85
98
|
```
|
86
99
|
|
87
100
|
### Elements
|
@@ -93,8 +106,10 @@ that we expect to find on the page using a CSS selector.
|
|
93
106
|
Let's define a `main` element to represent the main section of our HTML page:
|
94
107
|
|
95
108
|
```ruby
|
109
|
+
require "mache"
|
110
|
+
|
96
111
|
class WelcomePage < Mache::Page
|
97
|
-
element :main, "
|
112
|
+
element :main, "main"
|
98
113
|
|
99
114
|
def path
|
100
115
|
"/welcome"
|
@@ -105,7 +120,6 @@ end
|
|
105
120
|
We can query the `main` element as an attribute of our page object:
|
106
121
|
|
107
122
|
```ruby
|
108
|
-
page.has_main? # true
|
109
123
|
page.main.text # "lorem ipsum"
|
110
124
|
```
|
111
125
|
|
@@ -118,6 +132,8 @@ A component can contain any number of elements (or even other components).
|
|
118
132
|
Let's define a `Header` component to represent the header of our HTML page:
|
119
133
|
|
120
134
|
```ruby
|
135
|
+
require "mache"
|
136
|
+
|
121
137
|
class Header < Mache::Node
|
122
138
|
element :title, "h1"
|
123
139
|
end
|
@@ -127,9 +143,11 @@ We can mount the `Header` component in our page object class at a given CSS
|
|
127
143
|
selector using the `component` macro:
|
128
144
|
|
129
145
|
```ruby
|
146
|
+
require "mache"
|
147
|
+
|
130
148
|
class WelcomePage < Mache::Page
|
131
149
|
component :header, Header, "header"
|
132
|
-
element :main, "
|
150
|
+
element :main, "main"
|
133
151
|
|
134
152
|
def path
|
135
153
|
"/welcome"
|
@@ -140,17 +158,65 @@ end
|
|
140
158
|
Querying a component of our page object is much the same as with an element:
|
141
159
|
|
142
160
|
```ruby
|
143
|
-
page.has_header? # true
|
144
161
|
page.header.title.text # "Welcome"
|
145
162
|
```
|
146
163
|
|
164
|
+
### Helpers
|
165
|
+
|
166
|
+
Mâché provides helpers for testing Rails apps.
|
167
|
+
|
168
|
+
#### Flash
|
169
|
+
|
170
|
+
The `Flash` helper provides methods for testing flash messages. First define a
|
171
|
+
flash in your page object class:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
require "mache"
|
175
|
+
require "mache/helpers/rails"
|
176
|
+
|
177
|
+
class WelcomePage < Mache::Page
|
178
|
+
include Mache::Helpers::Rails::Flash
|
179
|
+
|
180
|
+
flash "#flash"
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
Then you can query the flash on your page object:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
page.has_notice_message?("Welcome to the app")
|
188
|
+
page.has_alert_message?("A horrible error occurred")
|
189
|
+
```
|
190
|
+
|
191
|
+
#### Routes
|
192
|
+
|
193
|
+
The `Routes` helper mixes the Rails URL helpers into your page object class.
|
194
|
+
This allows you to use the `*_path` and `*_url` methods as you normally would
|
195
|
+
in your Rails.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
require "mache"
|
199
|
+
require "mache/helpers/rails"
|
200
|
+
|
201
|
+
class WelcomePage < Mache::Page
|
202
|
+
include Mache::Helpers::Rails::Routes
|
203
|
+
|
204
|
+
def path
|
205
|
+
welcome_path
|
206
|
+
end
|
207
|
+
end
|
208
|
+
```
|
209
|
+
|
147
210
|
## Example
|
148
211
|
|
149
|
-
Let's look at
|
150
|
-
`Header`, `NavItem`, and `Nav` components can be reused in any other page
|
212
|
+
Let's look at an example of an acceptance test for our `WelcomePage`. Note that
|
213
|
+
the `Header`, `NavItem`, and `Nav` components can be reused in any other page
|
151
214
|
object classes we may define later for our web application.
|
152
215
|
|
153
216
|
```ruby
|
217
|
+
require "mache"
|
218
|
+
require "mache/helpers/rails"
|
219
|
+
|
154
220
|
class Header < Mache::Node
|
155
221
|
element :title, "h1"
|
156
222
|
end
|
@@ -163,22 +229,26 @@ end
|
|
163
229
|
|
164
230
|
class Nav < Mache::Node
|
165
231
|
components :items, NavItem, "a"
|
232
|
+
|
233
|
+
def selected_item
|
234
|
+
items.find(&:selected?)
|
235
|
+
end
|
166
236
|
end
|
167
237
|
|
168
238
|
class WelcomePage < Mache::Page
|
239
|
+
include Mache::Helpers::Rails::Flash
|
240
|
+
include Mache::Helpers::Rails::Routes
|
241
|
+
|
169
242
|
component :header, Header, "header"
|
170
243
|
component :nav, Nav, "nav"
|
171
244
|
element :main, "main"
|
245
|
+
flash "#flash"
|
172
246
|
|
173
247
|
def path
|
174
|
-
|
248
|
+
welcome_path
|
175
249
|
end
|
176
250
|
end
|
177
|
-
```
|
178
|
-
|
179
|
-
We can use our page objects to write very expressive acceptance tests:
|
180
251
|
|
181
|
-
```ruby
|
182
252
|
feature "Welcome page" do
|
183
253
|
let(:home_page) { WelcomePage.visit }
|
184
254
|
|
@@ -193,13 +263,34 @@ feature "Welcome page" do
|
|
193
263
|
expect(home_page).to have_nav
|
194
264
|
expect(home_page.nav).to have_items
|
195
265
|
expect(home_page.nav.items.count).to be(3)
|
196
|
-
expect(home_page.nav.items[0]).to be_selected
|
197
266
|
expect(home_page.nav.items[0].text).to eq("foo")
|
198
267
|
expect(home_page.nav.items[1].text).to eq("bar")
|
199
268
|
expect(home_page.nav.items[2].text).to eq("baz")
|
269
|
+
expect(home_page.nav.selected_item).to eq("foo")
|
200
270
|
|
201
271
|
# main
|
202
272
|
expect(home_page.main.text).to eq("lorem ipsum")
|
273
|
+
|
274
|
+
# flash
|
275
|
+
expect(home_page).to have_flash
|
276
|
+
expect(home_page).to have_notice_message("lorem ipsum")
|
203
277
|
end
|
204
278
|
end
|
205
279
|
```
|
280
|
+
|
281
|
+
## API documentation
|
282
|
+
|
283
|
+
Read the [API documentation](http://www.rubydoc.info/gems/mache) on RubyDoc.
|
284
|
+
|
285
|
+
## Contributing
|
286
|
+
|
287
|
+
Pull requests are welcome. Please ensure that you run the tests before
|
288
|
+
submitting your PR:
|
289
|
+
|
290
|
+
```
|
291
|
+
> bundle exec rake
|
292
|
+
```
|
293
|
+
|
294
|
+
## License
|
295
|
+
|
296
|
+
Mâché is licensed under the [MIT License](/LICENSE).
|
data/Rakefile
CHANGED
data/lib/mache/dsl.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module Mache
|
2
|
+
# The {DSL} module is mixed into the {Node} class to provide the DSL for
|
3
|
+
# defining elements and components.
|
4
|
+
#
|
2
5
|
# See {ClassMethods} for documentation.
|
3
|
-
module DSL
|
6
|
+
module DSL
|
4
7
|
def self.included(base)
|
5
|
-
base.extend
|
8
|
+
base.extend(ClassMethods)
|
6
9
|
end
|
7
10
|
|
8
11
|
# Provides a set of macro-like methods for wrapping HTML fragments in {Node}
|
@@ -57,9 +60,9 @@ module Mache
|
|
57
60
|
|
58
61
|
# Defines an element that wraps an HTML fragment.
|
59
62
|
#
|
60
|
-
# @param name [String, Symbol]
|
61
|
-
# @param selector [String]
|
62
|
-
# @param options [Hash]
|
63
|
+
# @param name [String, Symbol] a name for the element
|
64
|
+
# @param selector [String] a selector to find the element
|
65
|
+
# @param options [Hash] a hash of options to pass to the Capybara finder
|
63
66
|
def element(name, selector, options = {})
|
64
67
|
define_method(name.to_s) do
|
65
68
|
Node.new(node: @node.find(selector, options))
|
@@ -70,9 +73,9 @@ module Mache
|
|
70
73
|
|
71
74
|
# Defines a collection of elements that wrap HTML fragments.
|
72
75
|
#
|
73
|
-
# @param name [String, Symbol] the elements collection
|
74
|
-
# @param selector [String]
|
75
|
-
# @param options [Hash]
|
76
|
+
# @param name [String, Symbol] a name for the elements collection
|
77
|
+
# @param selector [String] a selector to find the elements
|
78
|
+
# @param options [Hash] a hash of options to pass to the Capybara finder
|
76
79
|
def elements(name, selector, options = {})
|
77
80
|
options = {minimum: 1}.merge(options)
|
78
81
|
|
@@ -87,10 +90,10 @@ module Mache
|
|
87
90
|
|
88
91
|
# Defines a component that wraps an HTML fragment.
|
89
92
|
#
|
90
|
-
# @param name [String, Symbol]
|
91
|
-
# @param klass [Class]
|
92
|
-
# @param selector [String]
|
93
|
-
# @param options [Hash]
|
93
|
+
# @param name [String, Symbol] a name for the component
|
94
|
+
# @param klass [Class] a component class
|
95
|
+
# @param selector [String] a selector to find the component
|
96
|
+
# @param options [Hash] a hash of options to pass to the Capybara finder
|
94
97
|
def component(name, klass, selector, options = {})
|
95
98
|
unless klass < Node
|
96
99
|
raise ArgumentError, "Must be given a subclass of Node"
|
@@ -105,10 +108,10 @@ module Mache
|
|
105
108
|
|
106
109
|
# Defines a collection of components that wrap HTML fragments.
|
107
110
|
#
|
108
|
-
# @param name [String, Symbol] the
|
109
|
-
# @param klass [Class]
|
110
|
-
# @param selector [String]
|
111
|
-
# @param options [Hash]
|
111
|
+
# @param name [String, Symbol] a name for the components collection
|
112
|
+
# @param klass [Class] a component class
|
113
|
+
# @param selector [String] a selector to find the components
|
114
|
+
# @param options [Hash] a hash of options to pass to the Capybara finder
|
112
115
|
def components(name, klass, selector, options = {})
|
113
116
|
unless klass < Node
|
114
117
|
raise ArgumentError, "Must be given a subclass of Node"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mache
|
2
|
+
module Helpers
|
3
|
+
module Rails
|
4
|
+
# The {Flash} module can be Included into page object classes that support
|
5
|
+
# flash behaviour.
|
6
|
+
module Flash
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods # :nodoc:
|
12
|
+
def flash(selector)
|
13
|
+
element :flash, selector
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# rubocop:disable Style/PredicateName
|
18
|
+
def has_notice_message?(text)
|
19
|
+
css_class = flash[:class] || ""
|
20
|
+
css_class.include?("notice") && flash.text =~ /\s*#{text}\s*/
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_alert_message?(text)
|
24
|
+
css_class = flash[:class] || ""
|
25
|
+
css_class.include?("error") && flash.text =~ /\s*#{text}\s*/
|
26
|
+
end
|
27
|
+
# rubocop:enable Style/PredicateName
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Mache
|
2
|
+
module Helpers
|
3
|
+
module Rails
|
4
|
+
# The {Routes} module can be Included into page object classes that
|
5
|
+
# support routing.
|
6
|
+
module Routes
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval do
|
9
|
+
include ::Rails.application.routes.url_helpers
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/mache/node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "mache/dsl"
|
2
2
|
|
3
3
|
module Mache
|
4
|
-
# The Node class represents a wrapped HTML page, or fragment. It exposes all
|
4
|
+
# The {Node} class represents a wrapped HTML page, or fragment. It exposes all
|
5
5
|
# methods from the Mache {DSL}, and forwards any Capybara API methods to the
|
6
6
|
# {#node} object.
|
7
7
|
#
|
@@ -9,14 +9,14 @@ module Mache
|
|
9
9
|
class Node
|
10
10
|
include DSL
|
11
11
|
|
12
|
-
# The underlying Capybara node object wrapped by this
|
12
|
+
# The underlying Capybara node object wrapped by this instance.
|
13
13
|
#
|
14
14
|
# @return [Capybara::Node] the node object
|
15
15
|
attr_reader :node
|
16
16
|
|
17
17
|
# Returns a new instance of Node.
|
18
18
|
#
|
19
|
-
# @param node [Capybara::Node]
|
19
|
+
# @param node [Capybara::Node] a Capybara node object to wrap
|
20
20
|
def initialize(node:)
|
21
21
|
@node ||= node
|
22
22
|
end
|
data/lib/mache/page.rb
CHANGED
@@ -2,8 +2,8 @@ require "capybara"
|
|
2
2
|
require "mache/node"
|
3
3
|
|
4
4
|
module Mache
|
5
|
-
# The Page class wraps an HTML page with an application-specific API. You
|
6
|
-
# extend it to define your own API for manipulating the pages of your web
|
5
|
+
# The {Page} class wraps an HTML page with an application-specific API. You
|
6
|
+
# can extend it to define your own API for manipulating the pages of your web
|
7
7
|
# application.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -29,8 +29,8 @@ module Mache
|
|
29
29
|
|
30
30
|
# Returns a new page object.
|
31
31
|
#
|
32
|
-
# @param node [Capybara::Node]
|
33
|
-
# @param path [String]
|
32
|
+
# @param node [Capybara::Node] a Capybara node to attach to
|
33
|
+
# @param path [String] a path to where the page is located
|
34
34
|
def initialize(node: Capybara.current_session, path: nil)
|
35
35
|
@node ||= node
|
36
36
|
@path ||= path
|
data/lib/mache/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Bassett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- ".ruby-version"
|
109
109
|
- ".travis.yml"
|
110
110
|
- ".yardopts"
|
111
|
+
- CHANGELOG.md
|
111
112
|
- Gemfile
|
112
113
|
- LICENSE
|
113
114
|
- README.md
|
@@ -115,6 +116,9 @@ files:
|
|
115
116
|
- bin/console
|
116
117
|
- lib/mache.rb
|
117
118
|
- lib/mache/dsl.rb
|
119
|
+
- lib/mache/helpers/rails.rb
|
120
|
+
- lib/mache/helpers/rails/flash.rb
|
121
|
+
- lib/mache/helpers/rails/routes.rb
|
118
122
|
- lib/mache/node.rb
|
119
123
|
- lib/mache/page.rb
|
120
124
|
- lib/mache/version.rb
|
@@ -139,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
143
|
version: '0'
|
140
144
|
requirements: []
|
141
145
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.
|
146
|
+
rubygems_version: 2.6.11
|
143
147
|
signing_key:
|
144
148
|
specification_version: 4
|
145
149
|
summary: A library for writing cleaner and more expressive acceptance tests using
|