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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55a6ce7e56d5856c12b9e656a157e69a23d88fb1
4
- data.tar.gz: 3394ad1adb32985236d96080bcc181610538c5f7
3
+ metadata.gz: 7128da9d4090d139472b9eb01a8afcdc4ce68fb1
4
+ data.tar.gz: 970b46406b90860bf99397080a61fd0aefdc3c79
5
5
  SHA512:
6
- metadata.gz: 29a2257b42cb9eb23e552706b0dfdc2a92ee4ef07cf6e41aa42bf3ea4638bfe2880828e23a8aea95e0c8bb8fc2efdc7c671891d9c50bd473415fedf4fe0b05b8
7
- data.tar.gz: 825df856fa2537ba9f7019eb9d03c65a42793966ec218507ce132d761a6a9c92c96d1429f757ff7540a9ab0cb3c10d2ab792efeae1542c594a786b6cbc61ffc3
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).not_to_ have_search_field
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
- NB: An iframe can only be referenced by its ID. This is a limitation
1351
- imposed by Capybara. The third argument to the `iframe` method must
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
 
@@ -17,7 +17,5 @@ module SitePrism
17
17
  end
18
18
  end
19
19
 
20
- private
21
-
22
20
  @use_implicit_waits = false
23
21
  end
@@ -19,14 +19,13 @@ module SitePrism
19
19
  end
20
20
  end
21
21
  end
22
- alias_method :collection, :elements
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, &element_block|
28
- self.class.raise_if_block(self, section_name.to_s, !element_block.nil?)
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
- fail SitePrism::UnsupportedBlock, "#{obj.class}##{name} does not accept blocks, did you mean to define a (i)frame?"
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
- fail SitePrism::NoSelectorForElement.new, "#{self.class.name} => :#{method_name} needs a selector"
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
- if args.first.is_a? Class
174
+ case
175
+ when args.first.is_a?(Class)
176
176
  section_class = args.shift
177
- elsif block_given?
177
+ when block_given?
178
178
  section_class = Class.new SitePrism::Section, &block
179
179
  else
180
- fail ArgumentError, 'You should provide section class either as a block, or as the second argument'
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
@@ -7,4 +7,5 @@ module SitePrism
7
7
  class TimeOutWaitingForElementVisibility < StandardError; end
8
8
  class TimeOutWaitingForElementInvisibility < StandardError; end
9
9
  class UnsupportedBlock < StandardError; end
10
+ NotLoadedError = Class.new(StandardError)
10
11
  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
@@ -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
- def load(expansion_or_html = {})
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
- fail SitePrism::NoUrlForPage if expanded_url.nil?
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.length > 0 ? args.first : Waiter.default_wait_time
24
- fail SitePrism::NoUrlMatcherForPage if url_matcher.nil?
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
- if url_matcher.is_a?(Regexp)
114
+ case
115
+ when url_matcher.is_a?(Regexp)
99
116
  url_matches_by_regexp?
100
- elsif url_matcher.respond_to?(:to_str)
117
+ when url_matcher.respond_to?(:to_str)
101
118
  url_matches_by_template?(expected_mappings)
102
119
  else
103
- fail SitePrism::InvalidUrlMatcher
120
+ raise SitePrism::InvalidUrlMatcher
104
121
  end
105
122
  end
106
123
 
@@ -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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SitePrism
2
- VERSION = '2.8'
3
+ VERSION = '2.9'.freeze
3
4
  end
@@ -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
- fail SitePrism::TimeoutException, 'Timed out while waiting for block to return true'
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.8'
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: 2015-10-30 00:00:00.000000000 Z
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