page_magic 1.0.4 → 1.1.0

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: a91450be8cb413b43a99d69e44f488d450dbc110
4
- data.tar.gz: f29c4819b1ea546448234b9b4289bb6b176e0829
3
+ metadata.gz: 4acdbbbed10c450234259833ff79fc6fe45ba7f8
4
+ data.tar.gz: 3167a8d0be893169dac91a1f449f9713a057189e
5
5
  SHA512:
6
- metadata.gz: 7786dabc62892b77b1c6d89e8db36ae8adbdeba79c11062f6df7aca65ebace69dd91615b21abaedc235c78431a38c113728b68cb0ac3877c1852eabe44cccba7
7
- data.tar.gz: f89a7eba7766d74aa1e6b6f3d82f7471eb7b21bd995b9ccef3c137ea5bf82084a5d25632e286222d546035bdc1c35cac4956e296d1fa7b1c37c28516191d1313
6
+ metadata.gz: cd8232c273a46b22c349e8819da69f3d12680443bf3493334f0ab549698656d27ab98ddbd0ec0d2957b93038e7ec7735c9dfbfcef831aa20823b0f7cbf39ca18
7
+ data.tar.gz: be986a4a47b5645fb9629a6c695d090de31c0c969a82bff6f23cbef482cca57ca504acc27002a3de98fe6eafd432726a1d3f41197855e8023a9362c65f718526
data/Gemfile CHANGED
@@ -2,7 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'capybara', '>= 2.5'
4
4
  gem 'activesupport', '~> 4'
5
- gem 'wait', '~> 0'
6
5
 
7
6
  group :test do
8
7
  gem 'watir-webdriver'
data/Gemfile.lock CHANGED
@@ -18,7 +18,7 @@ GEM
18
18
  rack (>= 1.0.0)
19
19
  rack-test (>= 0.5.4)
20
20
  xpath (~> 2.0)
21
- certifi (2015.08.10)
21
+ certifi (2015.11.20)
22
22
  childprocess (0.5.8)
23
23
  ffi (~> 1.0, >= 1.0.11)
24
24
  cliver (0.3.2)
@@ -43,7 +43,7 @@ GEM
43
43
  gherkin3 (3.1.2)
44
44
  git (1.2.9.1)
45
45
  github-markup (1.4.0)
46
- github_api (0.12.4)
46
+ github_api (0.13.0)
47
47
  addressable (~> 2.3)
48
48
  descendants_tracker (~> 0.0.4)
49
49
  faraday (~> 0.8, < 0.10)
@@ -65,14 +65,16 @@ GEM
65
65
  rdoc
66
66
  json (1.8.3)
67
67
  jwt (1.5.2)
68
- mime-types (2.6.2)
68
+ mime-types (3.0)
69
+ mime-types-data (~> 3.2015)
70
+ mime-types-data (3.2015.1120)
69
71
  mini_portile (0.6.2)
70
- minitest (5.8.2)
72
+ minitest (5.8.3)
71
73
  multi_json (1.11.2)
72
74
  multi_test (0.1.2)
73
75
  multi_xml (0.5.5)
74
76
  multipart-post (2.0.0)
75
- nokogiri (1.6.6.2)
77
+ nokogiri (1.6.6.4)
76
78
  mini_portile (~> 0.6.0)
77
79
  oauth2 (1.0.0)
78
80
  faraday (>= 0.8, < 0.10)
@@ -82,7 +84,7 @@ GEM
82
84
  rack (~> 1.2)
83
85
  parser (2.2.3.0)
84
86
  ast (>= 1.1, < 3.0)
85
- poltergeist (1.8.0)
87
+ poltergeist (1.8.1)
86
88
  capybara (~> 2.1)
87
89
  cliver (~> 0.3.1)
88
90
  multi_json (~> 1.0)
@@ -105,7 +107,7 @@ GEM
105
107
  rspec-core (~> 3.4.0)
106
108
  rspec-expectations (~> 3.4.0)
107
109
  rspec-mocks (~> 3.4.0)
108
- rspec-core (3.4.0)
110
+ rspec-core (3.4.1)
109
111
  rspec-support (~> 3.4.0)
110
112
  rspec-expectations (3.4.0)
111
113
  diff-lcs (>= 1.2.0, < 2.0)
@@ -113,7 +115,7 @@ GEM
113
115
  rspec-mocks (3.4.0)
114
116
  diff-lcs (>= 1.2.0, < 2.0)
115
117
  rspec-support (~> 3.4.0)
116
- rspec-support (3.4.0)
118
+ rspec-support (3.4.1)
117
119
  rubocop (0.35.1)
118
120
  astrolabe (~> 1.3)
119
121
  parser (>= 2.2.3.0, < 3.0)
@@ -142,7 +144,6 @@ GEM
142
144
  tins (1.6.0)
143
145
  tzinfo (1.2.2)
144
146
  thread_safe (~> 0.1)
145
- wait (0.5.2)
146
147
  watir-webdriver (0.9.1)
147
148
  selenium-webdriver (>= 2.46.2)
148
149
  websocket (1.2.2)
@@ -170,6 +171,5 @@ DEPENDENCIES
170
171
  rubocop (~> 0.34)
171
172
  simplecov
172
173
  sinatra
173
- wait (~> 0)
174
174
  watir-webdriver
175
175
  yard (~> 0.8)
data/README.md CHANGED
@@ -1,4 +1,9 @@
1
- [![Gem Version](https://badge.fury.io/rb/page_magic.svg)](https://badge.fury.io/rb/page_magic) [![Dependency Status](https://gemnasium.com/Ladtech/page_magic.svg)](https://gemnasium.com/Ladtech/page_magic) [![Circle CI](https://circleci.com/gh/Ladtech/page_magic.svg?style=shield&circle-token=49c8f6869c1e0dc6f3b368e6e22a11fcea3aab8a)](https://circleci.com/gh/Ladtech/page_magic) [![Code Climate](https://codeclimate.com/github/Ladtech/page_magic/badges/gpa.svg)](https://codeclimate.com/github/Ladtech/page_magic) [![Test Coverage](https://codeclimate.com/github/Ladtech/page_magic/badges/coverage.svg)](https://codeclimate.com/github/Ladtech/page_magic/coverage) [![PullReview stats](https://www.pullreview.com/github/Ladtech/page_magic/badges/master.svg?)](https://www.pullreview.com/github/Ladtech/page_magic/reviews/master)
1
+ [![Gem Version](https://badge.fury.io/rb/page_magic.svg)](https://badge.fury.io/rb/page_magic)
2
+ [![Dependency Status](https://gemnasium.com/Ladtech/page_magic.svg)](https://gemnasium.com/Ladtech/page_magic)
3
+ [![Circle CI](https://circleci.com/gh/Ladtech/page_magic.svg?style=shield&circle-token=49c8f6869c1e0dc6f3b368e6e22a11fcea3aab8a)](https://circleci.com/gh/Ladtech/page_magic)
4
+ [![Code Climate](https://codeclimate.com/github/Ladtech/page_magic/badges/gpa.svg)](https://codeclimate.com/github/Ladtech/page_magic)
5
+ [![Test Coverage](https://codeclimate.com/github/Ladtech/page_magic/badges/coverage.svg)](https://codeclimate.com/github/Ladtech/page_magic/coverage)
6
+ [![PullReview stats](https://www.pullreview.com/github/Ladtech/page_magic/badges/master.svg?)](https://www.pullreview.com/github/Ladtech/page_magic/reviews/master)
2
7
  #PageMagic
3
8
  PageMagic is an API for testing web applications.
4
9
 
@@ -13,7 +18,8 @@ Wouldn't it be great if there was a framework that could:
13
18
 
14
19
  Well PageMagic might just be the answer!
15
20
 
16
- Give it a try and let us know what you think! There will undoubtedly be things that can be improved and issues that we are not aware of so your feedback/pull requests are greatly appreciated!
21
+ Give it a try and let us know what you think! There will undoubtedly be things that can be improved and issues that
22
+ we are not aware of so your feedback/pull requests are greatly appreciated!
17
23
  # Contents
18
24
 
19
25
  - [Installation](#installation)
@@ -31,6 +37,8 @@ Give it a try and let us know what you think! There will undoubtedly be things t
31
37
  - [Dynamic Selectors](#dynamic-selectors)
32
38
  - [Starting a session](#starting-a-session)
33
39
  - [Page mapping](#page-mapping)
40
+ - [Mapping against query string parameters](#mapping-against-query-string-parameters)
41
+ - [Mapping against fragment identifiers](#mapping-against-fragment-identifiers)
34
42
  - [Watchers](#watchers)
35
43
  - [Method watchers](#method-watchers)
36
44
  - [Simple watchers](#simple-watchers)
@@ -43,210 +51,229 @@ Give it a try and let us know what you think! There will undoubtedly be things t
43
51
  `gem install page_magic`
44
52
 
45
53
  # Quick Start
46
- Getting started with PageMagic is as easy, try running this:
54
+ Getting started with PageMagic is easy, try running this:
47
55
 
48
56
  ```ruby
49
57
  require 'page_magic'
50
58
 
51
- class Google
59
+ class Github
52
60
  include PageMagic
53
- url 'https://www.google.com'
61
+
62
+ url 'https://www.github.com'
54
63
 
55
- text_field :search_field, name: 'q'
56
- button :search_button, name: 'btnG'
64
+ text_field :search_field, name: 'q' do
65
+ watch(:url)
66
+
67
+ after_events do
68
+ wait_until { changed?(:url) }
69
+ end
70
+ end
57
71
 
58
- def search term
59
- search_field.set term
60
- search_button.click
72
+ def search(project_name)
73
+ search_field.set "#{project_name}\n"
61
74
  end
62
75
  end
63
-
64
- google = Google.visit(browser: :chrome)
65
- google.search('page_magic')
76
+ github = Github.visit(browser: chrome)
77
+ github.search('page_magic')
66
78
  ```
67
79
 
68
- This example defines a simple page to represent Google's search page, visits it and performs a search.
80
+ This example defines a page to represent Github's home page, visits it and performs a search.
69
81
 
70
- This code models a single page and will let you [interact](#interacting-with-elements) with the [elements](#elements) defined on it as well as use the [helper method](Helpers) we defined.
82
+ This code models a single page and will let you [interact](#interacting-with-elements) with the [elements](#elements)
83
+ defined on it as well as use the [helper method](Helpers) we defined.
71
84
 
72
- You can do lots with PageMagic including [mapping pages](#page-mapping) to a [session](#starting-a-session) so that they are fluidly switched in for you. You can even define [hooks](#hooks) to run when ever a element is interacted with. So what are you wating for? there' no place better to start than the [beginning](#defining-pages). Have fun! :)
85
+ You can do lots with PageMagic including [mapping pages](#page-mapping) to a [session](#starting-a-session) so that
86
+ they are fluidly switched in for you. You can even define [hooks](#hooks) to run when ever a element is interacted
87
+ with. So what are you wating for? there's no place better to start than the [beginning](#defining-pages). Have fun! :)
73
88
 
74
89
  # Defining Pages
75
90
  To define something that PageMagic can work with, simply include PageMagic in to a class.
76
91
  ```ruby
77
- class LoginPage
92
+ class Github
78
93
  include PageMagic
79
94
  end
80
95
  ```
81
96
  ## Elements
82
- Defining elements is easy see the example below.
97
+ Defining elements is easy. The following example defines a text field called 'search_field' that can be found using its name which is 'q'
83
98
 
84
99
  ```ruby
85
- class LoginPage
100
+ class Github
86
101
  include PageMagic
87
- text_field(:username, label: 'username')
88
- text_field(:password, label: 'password')
89
- button(:login_button, text: 'login')
102
+
103
+ text_field :search_field, name: 'q'
90
104
  end
91
105
  ```
92
106
 
93
107
  ### Interacting with elements
94
- Elements are defined with an id which is the name of the method you will use to reference it. In the above example, the textfields and button were defined with the id's, `:username`, `:password`, and `:login_button`
108
+ Elements are defined with an id which is the name of the method you will use to reference it. In the above example,
109
+ the text field was defined with the ide `:search_field`.
110
+
111
+ After visiting a page you are will get a `Session` object. Elements can be accessed through the session itself.
95
112
 
96
- After visiting a page with a PageMagic session, you can access all of the elements of that page through the session itself.
97
113
  ```ruby
98
- session.username.set 'joe@blogs.com'
99
- session.password.set 'passw0rd'
100
- session.login_button.click
114
+ page.search_field.set 'page_magic'
101
115
  ```
116
+
102
117
  #### Multple Results
103
- Where an element has been scoped to return multple results, these will be returned in an array. These elements can be defined
104
- using all of the same features as described in this readme and behave in exactly the same way.
118
+ Where an element has been scoped to return multple results, these will be returned in an array.
105
119
  ```ruby
106
- class LoginPage
107
- element :links, css: 'a'
120
+ class ResultsPage
121
+ include PageMagic
122
+ element :results, css: '.repo-list-item'
108
123
  end
109
124
 
110
- session.link => Array<Element>
125
+ page.results #=> Array<Element>
111
126
  ```
112
127
 
113
128
  ### Sub Elements
114
- If your pages are complex you can use PageMagic to compose pages, their elements and subelements to as many levels as you need to.
129
+ If your pages are complex you can use PageMagic to compose pages, their elements and subelements to as many levels as
130
+ you need to.
115
131
 
116
132
  ```ruby
117
- class MailBox
133
+ class ResultsPage
118
134
  include PageMagic
119
-
120
- element :message, id: 'message_id' do
121
- link(:read, text: 'read')
135
+
136
+ element :results, css: '.repo-list-item' do
137
+ element :stats, css: '.repo-list-stats'
138
+ element :meta_data, css: '.repo-list-meta'
139
+ link :repo_link, css: 'h3 a'
122
140
  end
123
141
  end
124
142
  ```
143
+
125
144
  Sub elements can be accessed through their parent elements e.g:
126
- ```
127
- session.message.read.click
145
+ ```ruby
146
+ page.results.first.repo_link.click
128
147
  ```
129
148
 
130
149
  ### Custom elements
131
150
  PageMagic allows you to define your own custom elements.
132
151
  ```ruby
133
- class Nav < PageMagic::Element
134
- selector css: '.nav'
135
-
136
- element :options, css: '.options' do
137
- link(:link1, id: 'link1')
138
- link(:link2, id: 'link2')
139
- link(:link3, id: 'link3')
140
- end
152
+ class SearchField < PageMagic::Element
153
+ selector name: 'q'
154
+ # custom stuff
141
155
  end
142
156
 
143
- class MyPage
157
+ class Github
144
158
  include PageMagic
145
- element Nav
159
+ element SearchField
146
160
  end
147
161
  ```
148
- If an id is not specified then the name of the element class will be used. The selector for the element can be specified on the class itself or overiden when defining the element on the page. The custom element can also be extended as with other elements.
162
+
163
+ If an id is not specified then the name of the element class will be used. In the above example the name given to the element of type `SearchField` would be `search_field`. The selector for the element can bespecified on the class itself or overiden when defining the element. The custom element can also be extended as with other elements.
164
+
149
165
  ```ruby
150
166
  class MyPage
151
167
  include PageMagic
152
- element Nav, :navigation, selector: '.custom' do
168
+ element SearchField, :search, selector: '.custom' do
153
169
  link(:extr_link, id: 'extra-link')
154
170
  do
155
171
  end
156
172
  ```
157
173
 
158
174
  ## Hooks
159
- PageMagic provides hooks to allow you to interact at the right moments with your pages.
175
+ PageMagic provides hooks to allow you to define actions that are executed when you pages and elements are interacted with.
160
176
 
161
177
  **Note:**
162
- - with hooks you may well find PageMagic's [watchers](#watchers) useful.
163
- - The following examples wait for actions to happen. You can of course write you own wait code or try out our [wait_until](#waiting) helper:)
178
+ - You may well find PageMagic's [watchers](#watchers) useful.
179
+ - The following examples wait for actions to happen. You can of course write you own wait code or feel free try out our
180
+ [wait_until](#waiting) helper:)
164
181
 
165
182
  ### On load hook
166
- PageMagic lets you define an on_load hook for your pages. This lets you write any custom wait logic you might need
167
- before letting execution continue.
183
+ PageMagic lets you define an on_load hook for your pages. This will be executed when the browser thinks the page has been loaded.
184
+
168
185
  ```ruby
169
- class LoginPage
186
+ class Github
170
187
  # ... code defining elements as shown above
171
188
 
172
189
  on_load do
173
- wait_until{login_fields_have_appeared?}
190
+ # code that needs to run when the page has loaded
174
191
  end
175
192
  end
176
193
  ```
177
194
 
178
195
  ### Element event hooks
179
- Frequently, you are going to have to work with pages that make heavy use of ajax. This means that just because you've clicked something, it doesn't mean that the action is finished. For these occasions PageMagic provides `before_events` and `after_events` hooks that you use to perform custom actions and wait for things to happen.
196
+ Frequently, you are going to have to work with pages that make heavy use of ajax. For these occasions PageMagic provides `before_events`
197
+ and `after_events` hooks that you use to perform custom action.
198
+
199
+ In the following example we have added watchers and event hooks to the SearchField custom element we defined in the
200
+ [previous section](#custom-elements). Encapsulating the business logic here means that we can really add value to
201
+ the pages that reuse this custom element
180
202
 
181
203
  ```ruby
182
- class EmailMessagePage
183
- include PageMagic
184
- ## code defining other elements, such as subject and body
185
-
186
- link(:delete id: 'delete-message') do
187
- after_events do
188
- wait_until{fancy_animation_has_disappeared?}
189
- end
204
+ class SearchField < PageMagic::Element
205
+ selector name: 'q'
206
+ watch(:url)
207
+ after_events do
208
+ wait_until { changed?(:url) }
190
209
  end
191
210
  end
192
211
  ```
193
212
 
194
213
  ## Helper methods
195
- Using elements that are defined on a page is great, but if you are enacting a procedure through interacting with a few of them then your code could end up with some pretty repetitive code. In this case you can define helper methods instead.
214
+ Helper methods can be defined to avoid writing repetive page/element specific code outside of your pages and elements.
196
215
 
197
216
  ```ruby
198
- class LoginPage
217
+ class Github
199
218
  # ... code defining elements as shown above
200
-
201
- def login(user, pass)
202
- username.set user
203
- password.set pass
204
- login_button.click
219
+
220
+ def search(project_name)
221
+ search_field.set "#{project_name}\n"
205
222
  end
206
223
  end
207
224
  ```
208
225
 
209
226
  We can interact with helper in the same way as we did page elements.
227
+
210
228
  ```ruby
211
- session.login('joe', 'blogs')
229
+ page.search('page_magic')
212
230
  ```
213
231
 
214
232
  ## Dynamic Selectors
215
- In some cases you will able to specify the selector for an element until runtime. PageMagic allows you to handle such situations with support for dynamic selectors.
233
+ In some cases you wont be able to specify the selector for an element until runtime. PageMagic allows you to handle
234
+ such situations with support for dynamic selectors. In the case of our Github example it would be nice to select a
235
+ particular result by supplying the owners organisation name.
216
236
 
217
237
  ```ruby
218
- class MailBox
238
+ class ResultsPage
219
239
  include PageMagic
220
-
221
- element :message do |subject:|
222
- selector xpath: '//tr[text()="#{subject}"]'
223
- link(:read, text: 'read')
240
+
241
+ element :results do |organisation:|
242
+
243
+ selector xpath: "//h3/a[contains(text(), '#{organisation}')]/../.."
244
+
245
+ # code for sub elements
224
246
  end
225
247
  end
226
248
  ```
227
- Here we have defined the 'message' element using a block that takes subject argument. This is passed in at run time and given to the xpath selector.
249
+
250
+ In the above example the selector looks for an element that has a link containing text that includes that organisation.
251
+ The example uses a named parameter and is invoked as follows.
228
252
  ```ruby
229
- session.message(subject: 'test message')
253
+ page.results(organisation: 'Ladtech')
230
254
  ```
231
255
 
232
256
  # Starting a session
233
257
  To start a PageMagic session simply decide what browser you want to use and pass it to PageMagic's `.session` method
234
258
  ```ruby
235
- session = PageMagic.session(browser: :chrome, url: 'https://21st-century-mail.com')
259
+ session = PageMagic.session(browser: :chrome, url: 'https://www.github.com)
236
260
  ```
237
261
 
238
- Your session won't you do much besides navigating to the given url until you have [mapped pages](#page-mapping) to it, so take a look at this next!
262
+ Your session won't do much besides navigating to the given url until you have [mapped pages](#page-mapping) to it, so
263
+ take a look at this next!
239
264
 
240
- **Note** PageMagic supports having multiple sessions pointed at different urls using different browsers at the same time :)
265
+ **Note** PageMagic supports having multiple sessions using different browsers at the same time :)
241
266
 
242
267
  ## Rack applications and Rack::Test
243
- To run a session against a rack application instead of a live site, simply supply the rack application when creating the session
268
+ To run a session against a rack application instead of a live site, simply supply the rack application when creating
269
+ the session
270
+
244
271
  ```ruby
245
272
  session = PageMagic.session(application: YourRackApp, url: '/path_to_start_at')
246
273
  ```
247
274
 
248
- By default PageMagic uses the Rack::Test driver for capybara however you are free to use any browser you like as long as
249
- the [driver is registered](#drivers) for it.
275
+ By default PageMagic uses the Rack::Test driver for capybara however you are free to use any browser you like as long
276
+ as the [driver is registered](#drivers) for it.
250
277
 
251
278
  ```ruby
252
279
  session = PageMagic.session(application: YourRackApp, browser: :your_chosen_browser, url: '/path_to_start_at')
@@ -258,24 +285,39 @@ Out of the box, PageMagic supports the following as parameters to browser:
258
285
  - :poltergeist
259
286
  - :rack_test
260
287
 
261
- Under the hood, PageMagic is using [Capybara](https://github.com/jnicklas/capybara) so you can register any Capybara specific driver you want. See [below](#registering-a-custom-driver) for how to do this.
288
+ Under the hood, PageMagic is using [Capybara](https://github.com/jnicklas/capybara) so you can register any Capybara
289
+ compliant driver you want. See [below](#registering-a-custom-driver) for how to do this.
262
290
 
263
- **Note:** We don't want to impose particular driver versions so PageMagic does not list any as dependencies. Therefore you will need add the requiste gem to your Gemfile.
291
+ **Note:** We don't want to impose particular driver versions so PageMagic does not list any as dependencies. Therefore
292
+ you will need add the requiste gems to your Gemfile.
264
293
 
265
294
  # Page mapping
266
- With PageMagic you can map which pages should be used to handle which URL paths. This is a pretty killer feature that will remove a lot of the juggling and bring back fluency to your code!
295
+ With PageMagic you can map which pages should be used to handle which resources. Meaning that when a the page in the browser changes, PageMagic loads the correct PageObject class to handle it. This feature removes a lot of the juggling and brings back fluency to your code!
296
+
267
297
  ```ruby
268
298
  # define what pages map to what
269
- browser.define_page_mappings %r{/messages/\d+} => MessagePage,
270
- '/login' => LoginPage,
271
- '/' => MailBox
299
+ session.define_page_mappings '/' => GitHub, '/search' => ResultsPage
300
+ ```
301
+ You can use even use regular expressions and provide more than one mapping to the same page object class.
302
+
303
+ **Note:** By default mappings are matched against a URL's path. In addition, PageMagic supports mapping against both
304
+ query string parameters and the fragement identifer (see below). Any combination of these can be used to define a page mapping.
305
+
306
+ ## Mapping against query string parameters
307
+ ```ruby
308
+ browser.define_page_mappings PageMagic.mapping(parameters: {parameter_name: string_or_regex}) => ResultsPage
309
+ ```
310
+
311
+ ## Mapping against fragment identifiers
312
+ JavaScript MVC frameworks allow different resources to be mapped the fragment portion of URLs. That is the part of the URL that follows the [Fragement identififer](https://en.wikipedia.org/wiki/Fragment_identifier) (#). PageMagic supports mapping page_objects
313
+ against URL fragments.
314
+
315
+ ```ruby
316
+ browser.define_page_mappings PageMagic.mapping(fragment: string_or_regex) => ResultsPage
272
317
  ```
273
- You can use even use regular expressions to map multiple paths to the same page. In the above example we are mapping paths that that starts with '/messages/' and are followed by one ore more digits to the `MessagePage` class.
274
318
 
275
319
  # Watchers
276
- PageMagic lets you set a watcher on any of the elements that you have defined on your pages. Use watchers to decide when
277
- things have changed. The `watch` method can be called from anywhere within an element definition. For PageObjects it can
278
- only be called from within hooks and helper methods.
320
+ PageMagic lets you set a watcher on any element. Use watchers to decide when things have changed. The `watch` method can be called from anywhere within an element definition. For PageObjects it can only be called from within hooks and helper methods.
279
321
 
280
322
  **Note**: Watchers are not inherited
281
323
 
@@ -292,8 +334,9 @@ end
292
334
  ```
293
335
 
294
336
  ## Simple watchers
295
- Simple watchers use the `watch` method passing two parameters, the first is the name of the element you want to keep an
296
- eye and the second is the method that needs to be called to get the value that should be observed.
337
+ Use `watch` method passing two parameters, the first is the name of the element you want to keep
338
+ an eye on and the second is the method that needs to be called to get the value that should be observed.
339
+
297
340
  ```ruby
298
341
  element :product_row, css '.cta' do
299
342
  watch(:total, :text)
@@ -303,6 +346,7 @@ element :product_row, css '.cta' do
303
346
  end
304
347
  end
305
348
  ```
349
+
306
350
  ## Custom watchers
307
351
  Custom watchers are defined by passing a name and block parameter to the `watch` method. The block returns the value
308
352
  that needs to be observed. Use watch in this way if you need to do something non standard to obtain a value or to
@@ -320,7 +364,7 @@ end
320
364
  ```
321
365
 
322
366
  # Waiting
323
- It's inevitable that if there is JavaScript on the page that you are going to have to wait for things to happen before you can move on. PageMagic supplies the `wait_until` method that can be used anywhere you might need it. The wait_until method takes a block that it will execute until either that block returns true or the timeout occurs. See the method docs for details on configuring timeouts and retry intervals.
367
+ It's inevitable that if there is JavaScript on the page that you are going to have to wait for things to happen. PageMagic supplies the `wait_until` method that can be used anywhere you might need it. The wait_until method takes a block that it will execute until either that block returns true or the timeout occurs. See the method docs for details on configuring timeouts and retry intervals.
324
368
 
325
369
  # Drivers
326
370
  ## Registering a custom driver
@@ -339,10 +383,11 @@ end
339
383
  PageMagic.drivers.register Webkit
340
384
 
341
385
  #3. Use registered driver
342
- session = PageMagic.session(browser: webkit, url: 'https://21st-century-mail.com')
386
+ session = PageMagic.session(browser: webkit, url: 'https://www.github.com')
343
387
  ```
344
388
  # Cucumber quick start
345
- You can obviously use PageMagic anywhere you fancy but one of the places you might decide to use it is within a Cucumber test suite. If that's the case something like the following could prove useful.
389
+ You can obviously use PageMagic anywhere you fancy but one of the places you might decide to use it is within a
390
+ Cucumber test suite. If that's the case something like the following could prove useful.
346
391
 
347
392
  ## Helper methods
348
393
  Put the following in to `features/support/page_magic.rb` to make these helpers available to all of your steps.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.1.0
data/lib/page_magic.rb CHANGED
@@ -14,6 +14,14 @@ require 'page_magic/drivers'
14
14
 
15
15
  # module PageMagic - PageMagic is an api for modelling pages in a website.
16
16
  module PageMagic
17
+ extend SingleForwardable
18
+
19
+ # @!method matcher
20
+ # define match critera for loading a page object class
21
+ # @see Matcher#initialize
22
+ # @return [Matcher]
23
+ def_delegator Matcher, :new, :matcher
24
+
17
25
  class << self
18
26
  # @return [Drivers] registered drivers
19
27
  def drivers
@@ -11,6 +11,9 @@ module PageMagic
11
11
  class InvalidURLException < Exception
12
12
  end
13
13
 
14
+ class MatcherInvalidException < Exception
15
+ end
16
+
14
17
  class TimeoutException < Exception
15
18
  end
16
19
 
@@ -0,0 +1,117 @@
1
+ require 'active_support/core_ext/object/to_query'
2
+ module PageMagic
3
+ # models mapping used to relate pages to uris
4
+ class Matcher
5
+ attr_reader :path, :parameters, :fragment
6
+
7
+ # @raise [MatcherInvalidException] if at least one component is not specified
8
+ def initialize(path = nil, parameters: nil, fragment: nil)
9
+ fail MatcherInvalidException unless path || parameters || fragment
10
+ @path = path
11
+ @parameters = parameters
12
+ @fragment = fragment
13
+ end
14
+
15
+ # @return [Boolean] true if no component contains a Regexp
16
+ def can_compute_uri?
17
+ !fuzzy?(fragment) && !fuzzy?(path) && !fuzzy?(parameters)
18
+ end
19
+
20
+ # @return [String] uri represented by this mapping
21
+ def compute_uri
22
+ "#{path}".tap do |uri|
23
+ uri << "?#{parameters.to_query}" if parameters
24
+ uri << "##{fragment}" if fragment
25
+ end
26
+ end
27
+
28
+ # @return [Fixnum] hash for instance
29
+ def hash
30
+ [path, parameters, fragment].hash
31
+ end
32
+
33
+ # @param [String] uri
34
+ # @return [Boolean] returns true if the uri is matched against this matcher
35
+ def match?(uri)
36
+ uri = URI(uri)
37
+ path_valid?(uri.path) && query_string_valid?(uri.query) && fragment_valid?(uri.fragment)
38
+ end
39
+
40
+ # compare this matcher against another
41
+ # @param [Matcher] other
42
+ # @return [Fixnum] -1 = smaller, 0 = equal to, 1 = greater than
43
+ def <=>(other)
44
+ [:path, :parameters, :fragment].inject(0) do |result, component|
45
+ result == 0 ? compare(send(component), other.send(component)) : result
46
+ end
47
+ end
48
+
49
+ # check equality
50
+ # @param [Matcher] other
51
+ # @return [Boolean]
52
+ def ==(other)
53
+ return false unless other.is_a?(Matcher)
54
+ path == other.path && parameters == other.parameters && fragment == other.fragment
55
+ end
56
+
57
+ alias_method :eql?, :==
58
+
59
+ private
60
+
61
+ def compare(this, other)
62
+ return presence_comparison(this, other) unless this && other
63
+ fuzzy_comparison(this, other)
64
+ end
65
+
66
+ def compatible?(string, comparitor)
67
+ return true if comparitor.nil?
68
+ if fuzzy?(comparitor)
69
+ string =~ comparitor ? true : false
70
+ else
71
+ string == comparitor
72
+ end
73
+ end
74
+
75
+ def fragment_valid?(string)
76
+ compatible?(string, fragment)
77
+ end
78
+
79
+ def fuzzy?(component)
80
+ return false unless component
81
+ if component.is_a?(Hash)
82
+ component.values.any? { |o| fuzzy?(o) }
83
+ else
84
+ component.is_a?(Regexp)
85
+ end
86
+ end
87
+
88
+ def fuzzy_comparison(this, other)
89
+ if fuzzy?(this)
90
+ fuzzy?(other) ? 0 : 1
91
+ else
92
+ fuzzy?(other) ? -1 : 0
93
+ end
94
+ end
95
+
96
+ def parameters_hash(string)
97
+ CGI.parse(string.to_s.downcase).collect { |key, value| [key.downcase, value.first] }.to_h
98
+ end
99
+
100
+ def path_valid?(string)
101
+ compatible?(string, path)
102
+ end
103
+
104
+ def presence_comparison(this, other)
105
+ return 0 if this.nil? && other.nil?
106
+ return 1 if this.nil? && other
107
+ -1
108
+ end
109
+
110
+ def query_string_valid?(string)
111
+ return true unless parameters
112
+ !parameters.any? do |key, value|
113
+ !compatible?(parameters_hash(string)[key.downcase.to_s], value)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -1,29 +1,30 @@
1
- require 'wait'
2
1
  require 'forwardable'
2
+ require 'page_magic/matcher'
3
3
  module PageMagic
4
4
  # class Session - coordinates access to the browser though page objects.
5
5
  class Session
6
6
  URL_MISSING_MSG = 'a path must be mapped or a url supplied'
7
- REGEXP_MAPPING_MSG = 'URL could not be derived because mapping is a Regexp'
7
+ REGEXP_MAPPING_MSG = 'URL could not be derived because mapping contains Regexps'
8
8
  INVALID_MAPPING_MSG = 'mapping must be a string or regexp'
9
9
 
10
10
  extend Forwardable
11
11
 
12
- attr_reader :raw_session, :transitions
12
+ attr_reader :raw_session, :transitions, :base_url
13
13
 
14
14
  # Create a new session instance
15
15
  # @param [Object] capybara_session an instance of a capybara session
16
- # @param [String] url url to start the session at.
17
- def initialize(capybara_session, url = nil)
16
+ # @param [String] base_url url to start the session at.
17
+ def initialize(capybara_session, base_url = nil)
18
18
  @raw_session = capybara_session
19
- visit(url: url) if url
19
+ @base_url = base_url
20
+ visit(url: base_url) if base_url
20
21
  @transitions = {}
21
22
  end
22
23
 
23
24
  # @return [Object] returns page object representing the currently loaded page on the browser. If no mapping
24
25
  # is found then nil returned
25
26
  def current_page
26
- mapping = find_mapped_page(current_path)
27
+ mapping = find_mapped_page(current_url)
27
28
  @current_page = initialize_page(mapping) if mapping
28
29
  @current_page
29
30
  end
@@ -47,7 +48,10 @@ module PageMagic
47
48
  # @option transitions [String] path as literal
48
49
  # @option transitions [Regexp] path as a regexp for dynamic matching.
49
50
  def define_page_mappings(transitions)
50
- @transitions = transitions
51
+ @transitions = transitions.collect do |key, value|
52
+ key = key.is_a?(Matcher) ? key : Matcher.new(key)
53
+ [key, value]
54
+ end.to_h
51
55
  end
52
56
 
53
57
  # @!method execute_script
@@ -84,9 +88,9 @@ module PageMagic
84
88
  def visit(page = nil, url: nil)
85
89
  if url
86
90
  raw_session.visit(url)
87
- elsif (path = transitions.key(page))
88
- fail InvalidURLException, REGEXP_MAPPING_MSG if path.is_a?(Regexp)
89
- raw_session.visit(url(current_url, path))
91
+ elsif (mapping = transitions.key(page))
92
+ fail InvalidURLException, REGEXP_MAPPING_MSG unless mapping.can_compute_uri?
93
+ raw_session.visit(url(base_url, mapping.compute_uri))
90
94
  else
91
95
  fail InvalidURLException, URL_MISSING_MSG
92
96
  end
@@ -96,23 +100,16 @@ module PageMagic
96
100
 
97
101
  private
98
102
 
99
- def find_mapped_page(path)
100
- mapping = transitions.keys.find do |key|
101
- matches?(path, key)
102
- end
103
- transitions[mapping]
103
+ def find_mapped_page(url)
104
+ matches(url).first
104
105
  end
105
106
 
106
- def initialize_page(page_class)
107
- page_class.new(self).execute_on_load
107
+ def matches(url)
108
+ transitions.keys.find_all { |matcher| matcher.match?(url) }.sort.collect { |match| transitions[match] }
108
109
  end
109
110
 
110
- def matches?(string, matcher)
111
- if matcher.is_a?(Regexp)
112
- string =~ matcher
113
- else
114
- string == matcher
115
- end
111
+ def initialize_page(page_class)
112
+ page_class.new(self).execute_on_load
116
113
  end
117
114
 
118
115
  def url(base_url, path)
data/page_magic.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: page_magic 1.0.4 ruby lib
5
+ # stub: page_magic 1.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "page_magic"
9
- s.version = "1.0.4"
9
+ s.version = "1.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Leon Davis"]
14
- s.date = "2015-11-16"
14
+ s.date = "2015-11-25"
15
15
  s.description = "Framework for modeling and interacting with webpages which wraps capybara"
16
16
  s.email = "info@lad-tech.com"
17
17
  s.extra_rdoc_files = [
@@ -47,6 +47,7 @@ Gem::Specification.new do |s|
47
47
  "lib/page_magic/elements.rb",
48
48
  "lib/page_magic/exceptions.rb",
49
49
  "lib/page_magic/instance_methods.rb",
50
+ "lib/page_magic/matcher.rb",
50
51
  "lib/page_magic/session.rb",
51
52
  "lib/page_magic/session_methods.rb",
52
53
  "lib/page_magic/wait_methods.rb",
@@ -67,6 +68,7 @@ Gem::Specification.new do |s|
67
68
  "spec/page_magic/element_definition_builder_spec.rb",
68
69
  "spec/page_magic/elements_spec.rb",
69
70
  "spec/page_magic/instance_methods_spec.rb",
71
+ "spec/page_magic/matcher_spec.rb",
70
72
  "spec/page_magic/session_methods_spec.rb",
71
73
  "spec/page_magic/session_spec.rb",
72
74
  "spec/page_magic/wait_methods_spec.rb",
@@ -93,7 +95,6 @@ Gem::Specification.new do |s|
93
95
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
94
96
  s.add_runtime_dependency(%q<capybara>, [">= 2.5"])
95
97
  s.add_runtime_dependency(%q<activesupport>, ["~> 4"])
96
- s.add_runtime_dependency(%q<wait>, ["~> 0"])
97
98
  s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
98
99
  s.add_development_dependency(%q<rubocop>, ["~> 0.34"])
99
100
  s.add_development_dependency(%q<yard>, ["~> 0.8"])
@@ -102,7 +103,6 @@ Gem::Specification.new do |s|
102
103
  else
103
104
  s.add_dependency(%q<capybara>, [">= 2.5"])
104
105
  s.add_dependency(%q<activesupport>, ["~> 4"])
105
- s.add_dependency(%q<wait>, ["~> 0"])
106
106
  s.add_dependency(%q<jeweler>, ["~> 2.0"])
107
107
  s.add_dependency(%q<rubocop>, ["~> 0.34"])
108
108
  s.add_dependency(%q<yard>, ["~> 0.8"])
@@ -112,7 +112,6 @@ Gem::Specification.new do |s|
112
112
  else
113
113
  s.add_dependency(%q<capybara>, [">= 2.5"])
114
114
  s.add_dependency(%q<activesupport>, ["~> 4"])
115
- s.add_dependency(%q<wait>, ["~> 0"])
116
115
  s.add_dependency(%q<jeweler>, ["~> 2.0"])
117
116
  s.add_dependency(%q<rubocop>, ["~> 0.34"])
118
117
  s.add_dependency(%q<yard>, ["~> 0.8"])
@@ -0,0 +1,235 @@
1
+ # rubocop:disable Metrics/ModuleLength
2
+ module PageMagic
3
+ describe Matcher do
4
+ describe '#initialize' do
5
+ context 'no componentes specified' do
6
+ it 'raises an exeception' do
7
+ expect { described_class.new }.to raise_exception(MatcherInvalidException)
8
+ end
9
+ end
10
+ end
11
+ describe '#can_compute_uri?' do
12
+ context 'regex in path' do
13
+ it 'returns false' do
14
+ expect(described_class.new(//).can_compute_uri?).to eq(false)
15
+ end
16
+ end
17
+
18
+ context 'regex in parameters' do
19
+ it 'returns false' do
20
+ expect(described_class.new(parameters: { param: // }).can_compute_uri?).to eq(false)
21
+ end
22
+ end
23
+
24
+ context 'regexp in fragment' do
25
+ it 'returns false' do
26
+ expect(described_class.new(fragment: //).can_compute_uri?).to eq(false)
27
+ end
28
+ end
29
+
30
+ context 'regexp not present' do
31
+ it 'returns true' do
32
+ expect(described_class.new('/').can_compute_uri?).to eq(true)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe 'compare' do
38
+ subject { described_class.new('/') }
39
+ context 'param 1 not nil' do
40
+ context 'param 2 not nil' do
41
+ context 'literal to fuzzy' do
42
+ it 'returns -1' do
43
+ expect(subject.instance_eval { compare('/', //) }).to eq(-1)
44
+ end
45
+ end
46
+
47
+ context 'literal to literal' do
48
+ it 'returns 0' do
49
+ expect(subject.instance_eval { compare('/', '/') }).to eq(0)
50
+ end
51
+ end
52
+
53
+ context 'fuzzy to fuzzy' do
54
+ it 'returns 0' do
55
+ expect(subject.instance_eval { compare(//, //) }).to eq(0)
56
+ end
57
+ end
58
+
59
+ context 'fuzzy to literal' do
60
+ it 'returns 1' do
61
+ expect(subject.instance_eval { compare(//, '/') }).to eq(1)
62
+ end
63
+ end
64
+ end
65
+
66
+ context 'param2 is nil' do
67
+ it 'returns -1' do
68
+ expect(subject.instance_eval { compare('/', nil) }).to eq(-1)
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'param1 is nil' do
74
+ context 'param2 not nil' do
75
+ it 'returns 1' do
76
+ expect(subject.instance_eval { compare(nil, '/') }).to eq(1)
77
+ end
78
+ end
79
+
80
+ context 'param2 nil' do
81
+ it 'returns 0' do
82
+ expect(subject.instance_eval { compare(nil, nil) }).to eq(0)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ describe 'compute_uri' do
89
+ context 'path present' do
90
+ it 'returns a uri' do
91
+ expect(described_class.new('/').compute_uri).to eq('/')
92
+ end
93
+ end
94
+
95
+ context 'params present' do
96
+ context '1 param' do
97
+ it 'returns a uri' do
98
+ expect(described_class.new(parameters: { a: 1 }).compute_uri).to eq('?a=1')
99
+ end
100
+ end
101
+
102
+ context 'more than 1 param' do
103
+ it 'returns a uri' do
104
+ expect(described_class.new(parameters: { a: 1, b: 2 }).compute_uri).to eq('?a=1&b=2')
105
+ end
106
+ end
107
+ end
108
+
109
+ context 'fragment present' do
110
+ it 'returns a uri' do
111
+ expect(described_class.new(fragment: 'fragment').compute_uri).to eq('#fragment')
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#matches?' do
117
+ let(:matching_url) { 'http://www.example.com/path?foo=bar#fragment' }
118
+ let(:incompatible_url) { 'http://www.example.com/mismatch?miss=match#mismatch' }
119
+ context 'path requirement exists' do
120
+ context 'path is literal' do
121
+ subject do
122
+ described_class.new('/path')
123
+ end
124
+ it 'returns true for an exact match' do
125
+ expect(subject.match?(matching_url)).to eq(true)
126
+ end
127
+
128
+ it 'returns false when not an exact match' do
129
+ expect(subject.match?(incompatible_url)).to eq(false)
130
+ end
131
+ end
132
+
133
+ context 'path is regexp' do
134
+ subject do
135
+ described_class.new(/\d/)
136
+ end
137
+ it 'returns true for a match on the regexp' do
138
+ expect(subject.match?('3')).to eq(true)
139
+ end
140
+
141
+ it 'returns false when regexp is not a match' do
142
+ expect(subject.match?('/mismatch')).to eq(false)
143
+ end
144
+ end
145
+ end
146
+
147
+ context 'query string requirement exists' do
148
+ context 'parameter requirement is a literal' do
149
+ subject do
150
+ described_class.new(parameters: { foo: 'bar' })
151
+ end
152
+
153
+ it 'returns true for a match' do
154
+ expect(subject.match?(matching_url)).to eq(true)
155
+ end
156
+
157
+ it 'returns false when regexp is not a match' do
158
+ expect(subject.match?(incompatible_url)).to eq(false)
159
+ end
160
+ end
161
+
162
+ context 'parameter requirement is a regexp' do
163
+ subject do
164
+ described_class.new(parameters: { foo: /b[a]r/ })
165
+ end
166
+
167
+ it 'returns true for a match on the regexp' do
168
+ expect(subject.match?(matching_url)).to eq(true)
169
+ end
170
+
171
+ it 'returns false when regexp is not a match' do
172
+ expect(subject.match?(incompatible_url)).to eq(false)
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'fragment requirement exists' do
178
+ context 'fragment requirement is a literal' do
179
+ subject do
180
+ described_class.new(fragment: 'fragment')
181
+ end
182
+
183
+ it 'returns true for a match' do
184
+ expect(subject.match?(matching_url)).to eq(true)
185
+ end
186
+
187
+ it 'returns false when regexp is not a match' do
188
+ expect(subject.match?(incompatible_url)).to eq(false)
189
+ end
190
+ end
191
+
192
+ context 'fragment requirement is a regexp' do
193
+ subject do
194
+ described_class.new(fragment: /fragment/)
195
+ end
196
+
197
+ it 'returns true for a match on the regexp' do
198
+ expect(subject.match?(matching_url)).to eq(true)
199
+ end
200
+
201
+ it 'returns false when regexp is not a match' do
202
+ expect(subject.match?(incompatible_url)).to eq(false)
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ describe '<=>' do
209
+ it 'compares paths' do
210
+ subject = described_class.new('/')
211
+ expect(subject).to receive(:compare).with('/', nil).and_return(:result)
212
+ expect(subject <=> described_class.new(parameters: {})).to eq(:result)
213
+ end
214
+
215
+ context 'paths are equal' do
216
+ it 'compares parameters' do
217
+ subject = described_class.new('/', parameters: :params1)
218
+ expect(subject).to receive(:compare).with('/', '/').and_call_original
219
+ expect(subject).to receive(:compare).with(:params1, :params2).and_return(:params_result)
220
+ expect(subject <=> described_class.new('/', parameters: :params2)).to eq(:params_result)
221
+ end
222
+ end
223
+
224
+ context 'parameters are equal' do
225
+ it 'compares fragments' do
226
+ subject = described_class.new('/', parameters: :params, fragment: :frag1)
227
+ expect(subject).to receive(:compare).with('/', '/').and_call_original
228
+ expect(subject).to receive(:compare).with(:params, :params).and_call_original
229
+ expect(subject).to receive(:compare).with(:frag1, :frag2).and_return(:fragment_result)
230
+ expect(subject <=> described_class.new('/', parameters: :params, fragment: :frag2)).to eq(:fragment_result)
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -4,37 +4,38 @@ module PageMagic
4
4
  let(:page) do
5
5
  Class.new do
6
6
  include PageMagic
7
+ url 'http://url.com'
7
8
  end
8
9
  end
9
10
 
10
11
  subject { described_class.new(browser) }
11
12
 
12
- let(:url) { 'http://url.com' }
13
- let(:browser) { double('browser', current_url: url, visit: nil, current_path: :current_path) }
13
+ let(:url) { page.url }
14
+ let(:browser) { double('browser', current_url: "#{url}/somepath", visit: nil, current_path: :current_path) }
14
15
 
15
16
  describe '#current_page' do
16
17
  let(:another_page_class) do
17
18
  Class.new do
18
19
  include PageMagic
19
- url '/another_page1'
20
+ url 'http://www.example.com/another_page1'
20
21
  end
21
22
  end
22
23
 
23
24
  before do
24
- subject.define_page_mappings another_page_class.url => another_page_class
25
+ subject.define_page_mappings '/another_page1' => another_page_class
25
26
  subject.visit(page, url: url)
26
27
  end
27
28
 
28
29
  context 'page url has not changed' do
29
30
  it 'returns the original page' do
30
- allow(browser).to receive(:current_path).and_return(page.url)
31
+ allow(browser).to receive(:current_url).and_return(page.url)
31
32
  expect(subject.current_page).to be_an_instance_of(page)
32
33
  end
33
34
  end
34
35
 
35
36
  context 'page url has changed' do
36
37
  it 'returns the mapped page object' do
37
- allow(browser).to receive(:current_path).and_return(another_page_class.url)
38
+ allow(browser).to receive(:current_url).and_return(another_page_class.url)
38
39
  expect(subject.current_page).to be_an_instance_of(another_page_class)
39
40
  end
40
41
  end
@@ -52,21 +53,46 @@ module PageMagic
52
53
  end
53
54
  end
54
55
 
56
+ describe '#define_page_mappings' do
57
+ context 'mapping includes a literal' do
58
+ it 'creates a matcher to contain the specification' do
59
+ subject.define_page_mappings path: :page
60
+ expect(subject.transitions).to eq(Matcher.new(:path) => :page)
61
+ end
62
+ end
63
+
64
+ context 'mapping is a matcher' do
65
+ it 'leaves it intact' do
66
+ expected_matcher = Matcher.new(:page)
67
+ subject.define_page_mappings expected_matcher => :page
68
+ expect(subject.transitions.key(:page)).to be(expected_matcher)
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#execute_script' do
74
+ it 'calls the execute script method on the capybara session' do
75
+ expect(browser).to receive(:execute_script).with(:script).and_return(:result)
76
+ expect(subject.execute_script(:script)).to be(:result)
77
+ end
78
+ end
79
+
55
80
  describe '#find_mapped_page' do
56
81
  subject do
57
- described_class.new(nil).tap do |session|
58
- session.define_page_mappings '/page' => :mapped_page_using_string, /page\d/ => :mapped_page_using_regex
59
- end
82
+ described_class.new(nil)
60
83
  end
61
84
 
62
- context 'mapping is string' do
85
+ context 'match found' do
63
86
  it 'returns the page class' do
87
+ subject.define_page_mappings '/page' => :mapped_page_using_string
64
88
  expect(subject.instance_eval { find_mapped_page('/page') }).to be(:mapped_page_using_string)
65
89
  end
66
- end
67
- context 'mapping is regex' do
68
- it 'returns the page class' do
69
- expect(subject.instance_eval { find_mapped_page('/page2') }).to be(:mapped_page_using_regex)
90
+
91
+ context 'more than one match is found' do
92
+ it 'returns the most specific match' do
93
+ subject.define_page_mappings %r{/page} => :mapped_page_using_regex, '/page' => :mapped_page_using_string
94
+ expect(subject.instance_eval { find_mapped_page('/page') }).to eq(:mapped_page_using_string)
95
+ end
70
96
  end
71
97
  end
72
98
 
@@ -77,10 +103,22 @@ module PageMagic
77
103
  end
78
104
  end
79
105
 
80
- describe '#execute_script' do
81
- it 'calls the execute script method on the capybara session' do
82
- expect(browser).to receive(:execute_script).with(:script).and_return(:result)
83
- expect(subject.execute_script(:script)).to be(:result)
106
+ describe '#matches' do
107
+ subject do
108
+ described_class.new(nil)
109
+ end
110
+
111
+ it 'returns matching page mappings' do
112
+ subject.define_page_mappings '/page' => :mapped_page_using_string
113
+ expect(subject.instance_eval { matches('/page') }).to eq([:mapped_page_using_string])
114
+ end
115
+
116
+ context 'more than one match on path' do
117
+ it 'orders the results by specificity ' do
118
+ subject.define_page_mappings %r{/page} => :mapped_page_using_regex, '/page' => :mapped_page_using_string
119
+ expected_result = [:mapped_page_using_string, :mapped_page_using_regex]
120
+ expect(subject.instance_eval { matches('/page') }).to eq(expected_result)
121
+ end
84
122
  end
85
123
  end
86
124
 
@@ -167,9 +205,9 @@ module PageMagic
167
205
  expect(session.current_page).to be_a(page)
168
206
  end
169
207
 
170
- it 'uses the current url and the path in the page mappings' do
208
+ it 'uses the base url and the path in the page mappings' do
171
209
  session.define_page_mappings '/page' => page
172
- expect(browser).to receive(:visit).with("#{browser.current_url}/page")
210
+ expect(browser).to receive(:visit).with("#{url}/page")
173
211
  session.visit(page)
174
212
  end
175
213
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: page_magic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leon Davis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-16 00:00:00.000000000 Z
11
+ date: 2015-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '4'
41
- - !ruby/object:Gem::Dependency
42
- name: wait
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: jeweler
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -158,6 +144,7 @@ files:
158
144
  - lib/page_magic/elements.rb
159
145
  - lib/page_magic/exceptions.rb
160
146
  - lib/page_magic/instance_methods.rb
147
+ - lib/page_magic/matcher.rb
161
148
  - lib/page_magic/session.rb
162
149
  - lib/page_magic/session_methods.rb
163
150
  - lib/page_magic/wait_methods.rb
@@ -178,6 +165,7 @@ files:
178
165
  - spec/page_magic/element_definition_builder_spec.rb
179
166
  - spec/page_magic/elements_spec.rb
180
167
  - spec/page_magic/instance_methods_spec.rb
168
+ - spec/page_magic/matcher_spec.rb
181
169
  - spec/page_magic/session_methods_spec.rb
182
170
  - spec/page_magic/session_spec.rb
183
171
  - spec/page_magic/wait_methods_spec.rb