site_prism 2.8 → 2.9
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/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
|