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 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