mache 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a33c9e84520e543f6693fd7895d9fb6c6835626
4
- data.tar.gz: 7370d76223bd10fe2444e2d091e9ddd64219181a
3
+ metadata.gz: 57a3cffa66255ddaa95d41917a45842c4eb4f6e1
4
+ data.tar.gz: 54565c4f1a3b141facd22ecc3f70acf20d7402a7
5
5
  SHA512:
6
- metadata.gz: 771ff8e66362d6788b046bb94e092a100dacd2f952ee960f0b2530b47eca889a0b8a2a57ba7c4ac40116214663d3d14ca89db988e861f6ed46ed1d52c0bc37a9
7
- data.tar.gz: 4b203e4da5927d68605208ed06b8aa54a81d7c3c8f69d5c1f19cc13b1019e9c3fd7442cfb857aa11ac244b634d967a4fd1badcdc9e9ae3c28db66115b29b634b
6
+ metadata.gz: a713135868ef06a9df6cf2e40a430703de6eac61fe96e6c49eabc3fe039a71e0aa7b31fccac4ba56a02487e4c25af1411ac520b3c3728ae23a2b280723d1675f
7
+ data.tar.gz: 440ebba8dc3a9149e259c80640aa49a738f2e8de5ff967ec3b42482f0ce1a9d13ab466ebac58fc5bd324db0a61cb50770009cd6b162d9fe8ac668e200ac711ea
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  *.gem
2
2
  .bundle
3
+ .yardoc
3
4
  Gemfile.lock
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/README.md CHANGED
@@ -1,12 +1,39 @@
1
- # Mache
1
+ # Mâché
2
2
 
3
3
  [![Build Status](https://travis-ci.org/nullobject/mache.svg?branch=master)](https://travis-ci.org/nullobject/mache)
4
4
 
5
- A [page object](https://martinfowler.com/bliki/PageObject.html) library for writing cleaner acceptance tests with [Capybara](https://github.com/teamcapybara/capybara).
5
+ Mâché (pronounced "mash-ay") helps you to write cleaner and more expressive
6
+ acceptance tests for your web applications using page objects.
7
+
8
+ ## What is a page object?
9
+
10
+ A [page object](https://martinfowler.com/bliki/PageObject.html) is a data
11
+ structure which provides an interface to your web application for the purposes
12
+ of test automation. For example, it could represent a single HTML page, or
13
+ perhaps even a fragment of HTML on a page.
14
+
15
+ From Martin Fowler:
16
+
17
+ > A page object wraps an HTML page, or fragment, with an application-specific
18
+ > API, allowing you to manipulate page elements without digging around in the
19
+ > HTML.
20
+
21
+ [Capybara](https://github.com/teamcapybara/capybara) can only get us part of
22
+ the way there. It allows us to work with an API rather than manipulating the
23
+ HTML directly, but what it provides isn't an *application specific* API. It
24
+ gives us low-level API methods like `find`, `fill_in`, and `click_button`, but
25
+ it doesn't provide us with high-level methods to do things like "sign in to the
26
+ app" or "click the Dashboard item in the navigation bar".
27
+
28
+ This is where page objects come in. Using Mâché we can for instance define a
29
+ page object class called `SignInPage` and use it any time we want to automate
30
+ authenticating with our app. It could handle visiting the sign in page,
31
+ entering the user's credentials, and clicking the "Sign in" button.
6
32
 
7
33
  ## Getting started
8
34
 
9
- Consider the following HTML snippet:
35
+ Let's dive straight in and take a look at an example. Consider the following
36
+ HTML fragment for the welcome page in our app:
10
37
 
11
38
  ```html
12
39
  <html>
@@ -26,9 +53,9 @@ Consider the following HTML snippet:
26
53
  </html>
27
54
  ```
28
55
 
29
- To define a page object class to wrap this HTML snippet, we extend the
30
- `Mache::Page` class. The only method our class needs to provide is `path`, this
31
- tells Mache where to go when we want to visit the page.
56
+ To define a `WelcomePage` page object class to wrap this HTML page, we extend
57
+ the `Mache::Page` class. The only method our class needs to provide is `path`,
58
+ this tells Mâché where to go when we want to visit the page:
32
59
 
33
60
  ```ruby
34
61
  class WelcomePage < Mache::Page
@@ -38,26 +65,32 @@ class WelcomePage < Mache::Page
38
65
  end
39
66
  ```
40
67
 
41
- This is how we visit our page object:
68
+ We can visit our welcome page using our page object:
42
69
 
43
70
  ```ruby
44
71
  page = WelcomePage.visit
45
72
  page.current? # true
46
73
  ```
47
74
 
48
- Any Capybara API methods will be forwarded to the underlying node:
75
+ Mâché also handily exposes the Capybara API on our page object:
49
76
 
50
77
  ```ruby
51
78
  page.find("body > main").text # "lorem ipsum"
52
79
  ```
53
80
 
81
+ We can also use the `node` attribute to get the underlying Capybara node object:
82
+
83
+ ```ruby
84
+ page.node # <Capybara::Node>
85
+ ```
86
+
54
87
  ### Elements
55
88
 
56
89
  To make our page object more useful, we can define an element on our page
57
90
  object class using the `element` macro. An element is simply a HTML element
58
91
  that we expect to find on the page using a CSS selector.
59
92
 
60
- Let's define a `main` element:
93
+ Let's define a `main` element to represent the main section of our HTML page:
61
94
 
62
95
  ```ruby
63
96
  class WelcomePage < Mache::Page
@@ -69,7 +102,7 @@ class WelcomePage < Mache::Page
69
102
  end
70
103
  ```
71
104
 
72
- Now we can query the element on our page object:
105
+ We can query the `main` element as an attribute of our page object:
73
106
 
74
107
  ```ruby
75
108
  page.has_main? # true
@@ -79,10 +112,12 @@ page.main.text # "lorem ipsum"
79
112
 
80
113
  ### Components
81
114
 
82
- For elements that can be shared across an number of page object classes, it may
115
+ For elements that can be shared across any number of page object classes it may
83
116
  be useful to define a reusable component by extending the `Mache::Component`
84
- class. A component class can contain any number of elements or other
85
- components:
117
+ class. A component can contain any number of elements (or even other
118
+ components).
119
+
120
+ Let's define a `Header` component to represent the header of our HTML page:
86
121
 
87
122
  ```ruby
88
123
  class Header < Mache::Component
@@ -90,8 +125,8 @@ class Header < Mache::Component
90
125
  end
91
126
  ```
92
127
 
93
- Our page object class can mount our component at a given CSS selector using the
94
- `component` macro:
128
+ We can mount the `Header` component in our page object class at a given CSS
129
+ selector using the `component` macro:
95
130
 
96
131
  ```ruby
97
132
  class WelcomePage < Mache::Page
@@ -104,7 +139,7 @@ class WelcomePage < Mache::Page
104
139
  end
105
140
  ```
106
141
 
107
- Querying a component on our page object is much the same as an element:
142
+ Querying a component of our page object is much the same as with an element:
108
143
 
109
144
  ```ruby
110
145
  page.has_header? # true
@@ -114,7 +149,9 @@ page.header.title.text # "Welcome"
114
149
 
115
150
  ## Example
116
151
 
117
- Let's look at a more complete example for our `WelcomePage`:
152
+ Let's look at a more complete example for our `WelcomePage`. Note that the
153
+ `Header`, `NavItem`, and `Nav` components can be reused in any other page
154
+ object classes we may define later for our web application.
118
155
 
119
156
  ```ruby
120
157
  class Header < Mache::Component
@@ -142,28 +179,29 @@ class WelcomePage < Mache::Page
142
179
  end
143
180
  ```
144
181
 
145
- We can use our page objects to write expressive tests:
182
+ We can use our page objects to write very expressive acceptance tests:
146
183
 
147
184
  ```ruby
148
- feature "Home page" do
185
+ feature "Welcome page" do
149
186
  let(:home_page) { WelcomePage.visit }
150
187
 
151
- scenario "A user visits the home page" do
188
+ scenario "A user visits the welcome page" do
152
189
  expect(home_page).to be_current
153
190
 
191
+ # header
154
192
  expect(home_page).to have_header
155
193
  expect(home_page.header.title).to eq("Welcome")
156
194
 
195
+ # nav
157
196
  expect(home_page).to have_nav
158
197
  expect(home_page.nav).to have_items
159
198
  expect(home_page.nav.items.count).to be(3)
160
-
199
+ expect(home_page.nav.items[0]).to be_selected
161
200
  expect(home_page.nav.items[0].text).to eq("foo")
162
201
  expect(home_page.nav.items[1].text).to eq("bar")
163
202
  expect(home_page.nav.items[2].text).to eq("baz")
164
203
 
165
- expect(home_page.nav.items[0]).to be_selected
166
-
204
+ # main
167
205
  expect(home_page.main.text).to eq("lorem ipsum")
168
206
  end
169
207
  end
@@ -1,6 +1,22 @@
1
1
  require "mache/node"
2
2
 
3
3
  module Mache
4
+ # The Component class wraps a fragment of HTML and can be used in any number
5
+ # of {Page} classes using the `component` macro. A component can contain
6
+ # elements and other components.
7
+ #
8
+ # @example
9
+ #
10
+ # class NavItem < Mache::Component
11
+ # def selected?
12
+ # node[:class].include?("selected")
13
+ # end
14
+ # end
15
+ #
16
+ # class Nav < Mache::Component
17
+ # components :items, NavItem, "a"
18
+ # end
19
+ #
4
20
  class Component < Node
5
21
  end
6
22
  end
data/lib/mache/dsl.rb CHANGED
@@ -24,16 +24,18 @@ module Mache
24
24
  define_helper_methods(name, selector)
25
25
  end
26
26
 
27
- def component(name, type, selector)
27
+ def component(name, klass, selector)
28
28
  define_method(name.to_s) do
29
- type.new(@node.find(selector))
29
+ klass.new(node: @node.find(selector))
30
30
  end
31
31
  define_helper_methods(name, selector)
32
32
  end
33
33
 
34
- def components(name, type, selector)
34
+ def components(name, klass, selector)
35
35
  define_method(name.to_s) do
36
- @node.all(selector, minimum: 1).map { |element| type.new(element) }
36
+ @node.all(selector, minimum: 1).map do |element|
37
+ klass.new(node: element)
38
+ end
37
39
  end
38
40
  define_helper_methods(name, selector)
39
41
  end
data/lib/mache/node.rb CHANGED
@@ -1,17 +1,27 @@
1
1
  require "mache/dsl"
2
2
 
3
3
  module Mache
4
- # An abstract class that wraps a capybara node object and exposes the mache
5
- # DSL. It also delegates any capybara methods to the underlying node object.
4
+ # The Node class represents a wrapped HTML page, or fragment. It exposes all
5
+ # methods from the Mache {DSL}, and forwards any Capybara API methods to the
6
+ # {#node} object.
7
+ #
8
+ # @abstract
6
9
  class Node
7
10
  include DSL
8
11
 
12
+ # The underlying Capybara node object wrapped by this node.
13
+ #
14
+ # @return [Capybara::Node] the node object
9
15
  attr_reader :node
10
16
 
11
- def initialize(node)
12
- @node = node
17
+ # Returns a new instance of Node.
18
+ #
19
+ # @param node [Capybara::Node] the Capybara node object to wrap
20
+ def initialize(node:)
21
+ @node ||= node
13
22
  end
14
23
 
24
+ # Forwards any Capybara API calls to the node object.
15
25
  def method_missing(name, *args, &block)
16
26
  if @node.respond_to?(name)
17
27
  @node.send(name, *args, &block)
@@ -20,6 +30,7 @@ module Mache
20
30
  end
21
31
  end
22
32
 
33
+ # @!visibility private
23
34
  def respond_to_missing?(name, include_private = false)
24
35
  @node.respond_to?(name) || super
25
36
  end
data/lib/mache/page.rb CHANGED
@@ -2,27 +2,60 @@ require "capybara"
2
2
  require "mache/node"
3
3
 
4
4
  module Mache
5
- # A page provides a DSL for querying a wrapped capybara node object node. A
6
- # page can also has a path which can be visited.
5
+ # The Page class wraps an HTML page with an application-specific API. You can
6
+ # extend it to define your own API for manipulating the pages of your web
7
+ # application.
8
+ #
9
+ # @example
10
+ #
11
+ # class WelcomePage < Mache::page
12
+ # element :main, "#main"
13
+ # component :nav, Nav, "#nav"
14
+ # end
15
+ #
16
+ # page = WelcomePage.new(path: "/welcome")
17
+ # page.visit
18
+ # page.current # true
19
+ # page.main.text # lorem ipsum
20
+ #
7
21
  class Page < Node
22
+ # The path where the page is located, without any domain information.
23
+ #
24
+ # @return [String] the path string
25
+ # @example
26
+ # "/welcome"
27
+ # "/users/sign_in"
8
28
  attr_reader :path
9
29
 
30
+ # Returns a new page object.
31
+ #
32
+ # @param node [Capybara::Node] the Capybara node to attach to
33
+ # @param path [String] the path where the page is located
10
34
  def initialize(node: Capybara.current_session, path: nil)
11
35
  @node ||= node
12
36
  @path ||= path
13
37
  end
14
38
 
39
+ # Visits the page at its {#path}.
40
+ #
41
+ # @return [Page] the page object
15
42
  def visit
16
43
  @node.visit(path)
17
44
  self
18
45
  end
19
46
 
47
+ # Tests whether the page is current.
48
+ #
49
+ # @return [Boolean] `true` if the page is current, `false` otherwise
20
50
  def current?
21
51
  @node.current_path == path
22
52
  end
23
53
 
54
+ # Creates a new page object and calls {#visit} on it.
55
+ #
56
+ # @return [Page] the page object
24
57
  def self.visit
25
- new.tap(&:visit)
58
+ new.visit
26
59
  end
27
60
  end
28
61
  end
data/lib/mache/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mache
2
- VERSION = "1.0.0".freeze
2
+ VERSION = "1.0.1".freeze
3
3
  end
data/mache.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Mache::VERSION
9
9
  spec.authors = ["Joshua Bassett"]
10
10
  spec.email = ["josh.bassett@gmail.com"]
11
- spec.summary = "A page object library for writing cleaner acceptance tests with Capybara."
12
- spec.description = "Mache provides a DSL for writing page object clases to use in your acceptance tests."
11
+ spec.summary = "A library for writing cleaner and more expressive acceptance tests using page objects."
12
+ spec.description = "Mâché provides helps you to write cleaner and more expressive acceptance tests for your web applications using page objects."
13
13
  spec.homepage = "https://github.com/nullobject/mache"
14
14
  spec.license = "MIT"
15
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
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: 1.0.0
4
+ version: 1.0.1
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-07 00:00:00.000000000 Z
11
+ date: 2017-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -94,8 +94,8 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.47'
97
- description: Mache provides a DSL for writing page object clases to use in your acceptance
98
- tests.
97
+ description: Mâché provides helps you to write cleaner and more expressive acceptance
98
+ tests for your web applications using page objects.
99
99
  email:
100
100
  - josh.bassett@gmail.com
101
101
  executables: []
@@ -107,6 +107,7 @@ files:
107
107
  - ".rubocop.yml"
108
108
  - ".ruby-version"
109
109
  - ".travis.yml"
110
+ - ".yardopts"
110
111
  - Gemfile
111
112
  - LICENSE
112
113
  - README.md
@@ -142,5 +143,6 @@ rubyforge_project:
142
143
  rubygems_version: 2.5.2
143
144
  signing_key:
144
145
  specification_version: 4
145
- summary: A page object library for writing cleaner acceptance tests with Capybara.
146
+ summary: A library for writing cleaner and more expressive acceptance tests using
147
+ page objects.
146
148
  test_files: []