site_prism 2.8 → 2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +157 -6
- data/lib/site_prism.rb +0 -2
- data/lib/site_prism/element_container.rb +9 -9
- data/lib/site_prism/exceptions.rb +1 -0
- data/lib/site_prism/loadable.rb +85 -0
- data/lib/site_prism/page.rb +24 -7
- data/lib/site_prism/section.rb +8 -0
- data/lib/site_prism/version.rb +2 -1
- data/lib/site_prism/waiter.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7128da9d4090d139472b9eb01a8afcdc4ce68fb1
|
4
|
+
data.tar.gz: 970b46406b90860bf99397080a61fd0aefdc3c79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c17b940cae8a127f9a06157d515420a2886a6e062c0cac67f8817a603b31c596cf431f9613dc3c6a01699127b881c7fdc8f16dbf345cfb63a3fcfc872b7d8b50
|
7
|
+
data.tar.gz: fe19493132869f4b645eb1aef2f6518e48d71ee64e5a507a078974c5d8c0ff796f093ee9c221b9001cac062fab908c0b8ffd6027b901f835bd7146552898baf3
|
data/README.md
CHANGED
@@ -459,7 +459,7 @@ that should be used to test for non-existence. Using the above example:
|
|
459
459
|
|
460
460
|
```ruby
|
461
461
|
Then /^the search field exists$/ do
|
462
|
-
expect(@home).to have_no_search_field #NB: NOT => expect(@home).
|
462
|
+
expect(@home).to have_no_search_field #NB: NOT => expect(@home).not_to have_search_field
|
463
463
|
end
|
464
464
|
```
|
465
465
|
|
@@ -860,6 +860,22 @@ Then /^the home page menu contains a link to the various search functions$/ do
|
|
860
860
|
end
|
861
861
|
```
|
862
862
|
|
863
|
+
##### Accessing section elements using a block
|
864
|
+
|
865
|
+
Sections have a `within` method that allows scoped access to the section's elements inside a block. This is similar to Capybara's `within` method and allows for shorter test code particularly with nested sections.
|
866
|
+
Some of this test code can be made a little prettier by simply passing a block in.
|
867
|
+
|
868
|
+
```ruby
|
869
|
+
Then /^the home page menu contains a link to the various search functions$/ do
|
870
|
+
@home.menu do |menu|
|
871
|
+
expect(menu).to have_search
|
872
|
+
expect(menu.search['href']).to include "google.com"
|
873
|
+
expect(menu).to have_images
|
874
|
+
expect(menu).to have_maps
|
875
|
+
end
|
876
|
+
end
|
877
|
+
```
|
878
|
+
|
863
879
|
#### Getting a section's parent
|
864
880
|
|
865
881
|
It is possible to ask a section for its parent (page, or section if this
|
@@ -1217,6 +1233,143 @@ end
|
|
1217
1233
|
@results_page.wait_for_search_results(10) #=> waits for 10 seconds instead of the default capybara timeout
|
1218
1234
|
```
|
1219
1235
|
|
1236
|
+
## Load Validations
|
1237
|
+
|
1238
|
+
Load validations enable common validations to be abstracted and performed on a Page or Section to determine
|
1239
|
+
when it has finished loading and is ready for interaction in your tests.
|
1240
|
+
|
1241
|
+
For example, suppose you have a page which displays a 'Loading...' message while the body of
|
1242
|
+
the page is loaded in the background. Load validations can be used to ensure tests wait for the correct url
|
1243
|
+
to be displayed and the loading message removed before trying to interact with with the page.
|
1244
|
+
|
1245
|
+
Other use cases include Sections which are displayed conditionally and may take time to become ready to
|
1246
|
+
interact with, such as animated lightboxes.
|
1247
|
+
|
1248
|
+
### Using Load Validations
|
1249
|
+
|
1250
|
+
Load validations can be used in three constructs:
|
1251
|
+
|
1252
|
+
* Passing a block to `Page#load`
|
1253
|
+
* Passing a block to `Loadable#when_loaded`
|
1254
|
+
* Calling `Loadable#loaded?`
|
1255
|
+
|
1256
|
+
#### Page#load
|
1257
|
+
|
1258
|
+
When a block is passed to the `Page#load` method, the url will be loaded normally and then the block will be
|
1259
|
+
executed within the context of `when_loaded`. See `when_loaded` documentation below for further details.
|
1260
|
+
|
1261
|
+
Example:
|
1262
|
+
|
1263
|
+
```ruby
|
1264
|
+
# Load the page and then execute a block after all load validations pass:
|
1265
|
+
my_page_instance.load do |page|
|
1266
|
+
page.do_something
|
1267
|
+
end
|
1268
|
+
```
|
1269
|
+
|
1270
|
+
#### Loadable#when_loaded
|
1271
|
+
|
1272
|
+
The `Loadable#when_loaded` method on a Loadable class instance will yield the instance of the class into a
|
1273
|
+
block after all load validations have passed.
|
1274
|
+
|
1275
|
+
If any load validation fails, an error will be raised with the reason, if given, for the failure.
|
1276
|
+
|
1277
|
+
Example:
|
1278
|
+
|
1279
|
+
```ruby
|
1280
|
+
# Execute a block after all load validations pass:
|
1281
|
+
a_loadable_page_or_section.when_loaded do |loadable|
|
1282
|
+
loadable.do_something
|
1283
|
+
end
|
1284
|
+
```
|
1285
|
+
|
1286
|
+
#### Loadable#loaded?
|
1287
|
+
|
1288
|
+
You can explicitly run load validations on a Loadable via the `loaded?` method.
|
1289
|
+
This method will execute all load validations on the object and return a boolean value.
|
1290
|
+
In the event of a validation failure, a validation error can be accessed via the `load_error`
|
1291
|
+
method on the object, if any error message was emitted by the failing validation.
|
1292
|
+
|
1293
|
+
Example:
|
1294
|
+
|
1295
|
+
```ruby
|
1296
|
+
it 'loads the page' do
|
1297
|
+
some_page.load
|
1298
|
+
some_page.loaded? #=> true if/when all load validations pass
|
1299
|
+
another_page.loaded? #=> false if any load validations fail
|
1300
|
+
another_page.load_error #=> A string error message if one was supplied by the failing load validation, or nil
|
1301
|
+
end
|
1302
|
+
```
|
1303
|
+
|
1304
|
+
### Defining Load Validations
|
1305
|
+
|
1306
|
+
A load validation is a block which returns a boolean value when evaluated against an instance of the Loadable.
|
1307
|
+
|
1308
|
+
```ruby
|
1309
|
+
class SomePage < SitePrism::Page
|
1310
|
+
element :foo_element, '.foo'
|
1311
|
+
load_validation { has_foo_element? }
|
1312
|
+
end
|
1313
|
+
```
|
1314
|
+
|
1315
|
+
The block may instead return a two-element array which includes the boolean result as the first element and an
|
1316
|
+
error message as the second element. It is highly recommended to supply an error message, as they are
|
1317
|
+
extremely useful in debugging validation errors.
|
1318
|
+
|
1319
|
+
The error message will be ignored unless the boolean value is falsey.
|
1320
|
+
|
1321
|
+
```ruby
|
1322
|
+
class SomePage < SitePrism::Page
|
1323
|
+
element :foo_element, '.foo'
|
1324
|
+
load_validation { [has_foo_element?, 'did not have foo element!'] }
|
1325
|
+
end
|
1326
|
+
```
|
1327
|
+
|
1328
|
+
Load validations may be defined on `SitePrism::Page` and `SitePrism::Section` classes (herein referred
|
1329
|
+
to as `Loadables`) and are evaluated against an instance of the class when executed.
|
1330
|
+
|
1331
|
+
### Load Validation Inheritance and Execution Order
|
1332
|
+
|
1333
|
+
Any number of load validations may be defined on a Loadable class and will be inherited by its subclasses.
|
1334
|
+
|
1335
|
+
Load validations are executed in the order that they are defined. Inherited load validations are executed
|
1336
|
+
from the top of the inheritance chain (e.g. `SitePrism::Page` or `SitePrism::Section`) to the bottom.
|
1337
|
+
|
1338
|
+
For example:
|
1339
|
+
|
1340
|
+
```ruby
|
1341
|
+
class BasePage < SitePrism::Page
|
1342
|
+
element :loading_message, '.loader'
|
1343
|
+
|
1344
|
+
load_validation do
|
1345
|
+
wait_for_loading_message(1)
|
1346
|
+
[ has_no_loading_message?(wait: 10), 'loading message was still displayed' ]
|
1347
|
+
end
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
class FooPage < BasePage
|
1351
|
+
set_url '/foo'
|
1352
|
+
|
1353
|
+
section :form, '#form'
|
1354
|
+
element :some_other_element, '.myelement'
|
1355
|
+
|
1356
|
+
load_validation { [has_form?, 'form did not appear'] }
|
1357
|
+
load_validation { [has_some_other_element?, 'some other element did not appear'] }
|
1358
|
+
end
|
1359
|
+
```
|
1360
|
+
|
1361
|
+
In the above example, when `loaded?` is called on an instance of `FooPage`, the validations will be performed in the
|
1362
|
+
following order:
|
1363
|
+
|
1364
|
+
1. The `SitePrism::Page` default load validation will check `displayed?`
|
1365
|
+
2. The `BasePage` load validation will wait for the loading message to disappear.
|
1366
|
+
3. The `FooPage` load validation will wait for the `form` element to be present.
|
1367
|
+
4. The `FooPage` load validation will wait for the `some_other_element` element to be present.
|
1368
|
+
|
1369
|
+
NOTE: `SitePrism::Page` includes a default load validation on `page.displayed?` which is applied
|
1370
|
+
to all pages. It is therefore not necessary to define a load validation for this condition on
|
1371
|
+
inheriting page objects.
|
1372
|
+
|
1220
1373
|
## Using Capybara Query Options
|
1221
1374
|
|
1222
1375
|
When querying an element, section or a collection of elements or sections, you may
|
@@ -1339,7 +1492,7 @@ end
|
|
1339
1492
|
To expose the iframe, reference it from another page or class using the `iframe`
|
1340
1493
|
method. The `iframe` method takes 3 arguments; the name by which you
|
1341
1494
|
would like to reference the iframe, the page class that represents the
|
1342
|
-
iframe, and an ID by which you can locate the iframe. For example:
|
1495
|
+
iframe, and an ID or class by which you can locate the iframe. For example:
|
1343
1496
|
|
1344
1497
|
```ruby
|
1345
1498
|
class PageContainingIframe < SitePrism::Page
|
@@ -1347,10 +1500,8 @@ class PageContainingIframe < SitePrism::Page
|
|
1347
1500
|
end
|
1348
1501
|
```
|
1349
1502
|
|
1350
|
-
|
1351
|
-
|
1352
|
-
contain a selector that will locate the iframe node, and the portion of
|
1353
|
-
the selector that locates the iframe node must use the iframe node's ID.
|
1503
|
+
The third argument to the `iframe` method must
|
1504
|
+
contain a selector that will locate the iframe node.
|
1354
1505
|
|
1355
1506
|
### Testing for an iframe's existence
|
1356
1507
|
|
data/lib/site_prism.rb
CHANGED
@@ -19,14 +19,13 @@ module SitePrism
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
alias collection elements
|
23
23
|
|
24
24
|
def section(section_name, *args, &block)
|
25
25
|
section_class, find_args = extract_section_options args, &block
|
26
26
|
build section_name, *find_args do
|
27
|
-
define_method section_name do |*runtime_args, &
|
28
|
-
|
29
|
-
section_class.new self, find_first(*find_args, *runtime_args)
|
27
|
+
define_method section_name do |*runtime_args, &runtime_block|
|
28
|
+
section_class.new self, find_first(*find_args, *runtime_args), &runtime_block
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
@@ -64,7 +63,7 @@ module SitePrism
|
|
64
63
|
|
65
64
|
def raise_if_block(obj, name, has_block)
|
66
65
|
return unless has_block
|
67
|
-
|
66
|
+
raise SitePrism::UnsupportedBlock, "#{obj.class}##{name} does not accept blocks, did you mean to define a (i)frame?"
|
68
67
|
end
|
69
68
|
|
70
69
|
private
|
@@ -159,7 +158,7 @@ module SitePrism
|
|
159
158
|
|
160
159
|
def create_no_selector(method_name)
|
161
160
|
define_method method_name do
|
162
|
-
|
161
|
+
raise SitePrism::NoSelectorForElement.new, "#{self.class.name} => :#{method_name} needs a selector"
|
163
162
|
end
|
164
163
|
end
|
165
164
|
|
@@ -172,12 +171,13 @@ module SitePrism
|
|
172
171
|
end
|
173
172
|
|
174
173
|
def extract_section_options(args, &block)
|
175
|
-
|
174
|
+
case
|
175
|
+
when args.first.is_a?(Class)
|
176
176
|
section_class = args.shift
|
177
|
-
|
177
|
+
when block_given?
|
178
178
|
section_class = Class.new SitePrism::Section, &block
|
179
179
|
else
|
180
|
-
|
180
|
+
raise ArgumentError, 'You should provide section class either as a block, or as the second argument'
|
181
181
|
end
|
182
182
|
return section_class, args
|
183
183
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module SitePrism
|
2
|
+
module Loadable
|
3
|
+
module ClassMethods
|
4
|
+
# The list of load_validations. They will be executed in the order they are defined.
|
5
|
+
#
|
6
|
+
# @return [Array]
|
7
|
+
def load_validations
|
8
|
+
if superclass.respond_to?(:load_validations)
|
9
|
+
superclass.load_validations + _load_validations
|
10
|
+
else
|
11
|
+
_load_validations
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Appends a load validation block to the page class.
|
16
|
+
#
|
17
|
+
# When `loaded?` is called, these blocks are instance_eval'd against the current page
|
18
|
+
# instance. This allows users to wait for specific events to occur on the page or certain elements
|
19
|
+
# to be loaded before performing any actions on the page.
|
20
|
+
#
|
21
|
+
# @param block [&block] A block which returns true if the page loaded successfully, or false if it did not.
|
22
|
+
def load_validation(&block)
|
23
|
+
_load_validations << block
|
24
|
+
end
|
25
|
+
|
26
|
+
def _load_validations
|
27
|
+
@_load_validations ||= []
|
28
|
+
end
|
29
|
+
private :_load_validations
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.included(base)
|
33
|
+
base.extend(ClassMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
# In certain circumstances, we cache that the page has already been confirmed to be loaded so that actions which
|
37
|
+
# call `loaded?` a second time do not need to perform the load_validation queries against the page a second time.
|
38
|
+
attr_accessor :loaded, :load_error
|
39
|
+
|
40
|
+
# Executes the given block after the page is loaded.
|
41
|
+
#
|
42
|
+
# The loadable object instance is yielded into the block.
|
43
|
+
#
|
44
|
+
# @param block [&block] The block to be executed once the page has finished loading.
|
45
|
+
def when_loaded(&_block)
|
46
|
+
previously_loaded = loaded # Get original loaded value, in case we are nested inside another when_loaded block.
|
47
|
+
raise(ArgumentError, 'A block was expected, but none received.') unless block_given?
|
48
|
+
|
49
|
+
# Within the block, cache loaded? to optimize performance.
|
50
|
+
self.loaded = loaded?
|
51
|
+
unless loaded
|
52
|
+
message = "Failed to load because: #{load_error || 'no reason given'}"
|
53
|
+
raise(::SitePrism::NotLoadedError, message)
|
54
|
+
end
|
55
|
+
|
56
|
+
yield self
|
57
|
+
ensure
|
58
|
+
self.loaded = previously_loaded
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check if the page is loaded.
|
62
|
+
#
|
63
|
+
# On failure, if an error was reported by a failing validation, it will be available via the `load_error` accessor.
|
64
|
+
#
|
65
|
+
# @return [Boolean] True if the page loaded successfully; otherwise false.
|
66
|
+
def loaded?
|
67
|
+
self.load_error = nil
|
68
|
+
|
69
|
+
return true if loaded
|
70
|
+
|
71
|
+
load_validations_pass?
|
72
|
+
end
|
73
|
+
|
74
|
+
# If any load validations from page subclasses returns false, immediately return false.
|
75
|
+
def load_validations_pass?
|
76
|
+
self.class.load_validations.all? do |validation|
|
77
|
+
passed, message = instance_eval(&validation)
|
78
|
+
|
79
|
+
self.load_error = message if message && !passed
|
80
|
+
passed
|
81
|
+
end
|
82
|
+
end
|
83
|
+
private :load_validations_pass?
|
84
|
+
end
|
85
|
+
end
|
data/lib/site_prism/page.rb
CHANGED
@@ -1,27 +1,43 @@
|
|
1
|
+
require 'site_prism/loadable'
|
2
|
+
|
1
3
|
module SitePrism
|
2
4
|
class Page
|
3
5
|
include Capybara::DSL
|
4
6
|
include ElementChecker
|
7
|
+
include Loadable
|
5
8
|
extend ElementContainer
|
6
9
|
|
10
|
+
load_validation do
|
11
|
+
[displayed?, "Expected #{current_url} to match #{url_matcher} but it did not."]
|
12
|
+
end
|
13
|
+
|
7
14
|
def page
|
8
15
|
@page || Capybara.current_session
|
9
16
|
end
|
10
17
|
|
11
|
-
|
18
|
+
# Loads the page.
|
19
|
+
# Executes the block, if given, after running load validations on the page.
|
20
|
+
#
|
21
|
+
# @param expansion_or_html
|
22
|
+
# @param block [&block] A block to run once the page is loaded. The page will yield itself into the block.
|
23
|
+
def load(expansion_or_html = {}, &block)
|
24
|
+
self.loaded = false
|
25
|
+
|
12
26
|
if expansion_or_html.is_a? String
|
13
27
|
@page = Capybara.string(expansion_or_html)
|
14
28
|
else
|
15
29
|
expanded_url = url(expansion_or_html)
|
16
|
-
|
30
|
+
raise SitePrism::NoUrlForPage if expanded_url.nil?
|
17
31
|
visit expanded_url
|
18
32
|
end
|
33
|
+
|
34
|
+
when_loaded(&block) if block_given?
|
19
35
|
end
|
20
36
|
|
21
37
|
def displayed?(*args)
|
22
38
|
expected_mappings = args.last.is_a?(::Hash) ? args.pop : {}
|
23
|
-
seconds = args.
|
24
|
-
|
39
|
+
seconds = !args.empty? ? args.first : Waiter.default_wait_time
|
40
|
+
raise SitePrism::NoUrlMatcherForPage if url_matcher.nil?
|
25
41
|
begin
|
26
42
|
Waiter.wait_until_true(seconds) { url_matches?(expected_mappings) }
|
27
43
|
rescue SitePrism::TimeoutException
|
@@ -95,12 +111,13 @@ module SitePrism
|
|
95
111
|
end
|
96
112
|
|
97
113
|
def url_matches?(expected_mappings = {})
|
98
|
-
|
114
|
+
case
|
115
|
+
when url_matcher.is_a?(Regexp)
|
99
116
|
url_matches_by_regexp?
|
100
|
-
|
117
|
+
when url_matcher.respond_to?(:to_str)
|
101
118
|
url_matches_by_template?(expected_mappings)
|
102
119
|
else
|
103
|
-
|
120
|
+
raise SitePrism::InvalidUrlMatcher
|
104
121
|
end
|
105
122
|
end
|
106
123
|
|
data/lib/site_prism/section.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'site_prism/loadable'
|
2
|
+
|
1
3
|
module SitePrism
|
2
4
|
class Section
|
3
5
|
include Capybara::DSL
|
4
6
|
include ElementChecker
|
7
|
+
include Loadable
|
5
8
|
extend ElementContainer
|
6
9
|
|
7
10
|
attr_reader :root_element, :parent
|
@@ -9,12 +12,17 @@ module SitePrism
|
|
9
12
|
def initialize(parent, root_element)
|
10
13
|
@parent = parent
|
11
14
|
@root_element = root_element
|
15
|
+
Capybara.within(@root_element) { yield(self) } if block_given?
|
12
16
|
end
|
13
17
|
|
14
18
|
def visible?
|
15
19
|
root_element.visible?
|
16
20
|
end
|
17
21
|
|
22
|
+
def text
|
23
|
+
root_element.text
|
24
|
+
end
|
25
|
+
|
18
26
|
def execute_script(input)
|
19
27
|
Capybara.current_session.execute_script input
|
20
28
|
end
|
data/lib/site_prism/version.rb
CHANGED
data/lib/site_prism/waiter.rb
CHANGED
@@ -7,7 +7,7 @@ module SitePrism
|
|
7
7
|
break unless Time.now - start_time <= wait_time_seconds
|
8
8
|
sleep(0.05)
|
9
9
|
end
|
10
|
-
|
10
|
+
raise SitePrism::TimeoutException, 'Timed out while waiting for block to return true'
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.default_wait_time
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: site_prism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.9'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nat Ritmeyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/site_prism/element_checker.rb
|
79
79
|
- lib/site_prism/element_container.rb
|
80
80
|
- lib/site_prism/exceptions.rb
|
81
|
+
- lib/site_prism/loadable.rb
|
81
82
|
- lib/site_prism/page.rb
|
82
83
|
- lib/site_prism/section.rb
|
83
84
|
- lib/site_prism/version.rb
|