site_prism 3.7.3 → 4.0
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/LICENSE.md +1 -1
- data/README.md +78 -110
- data/lib/site_prism/addressable_url_matcher.rb +1 -1
- data/lib/site_prism/deprecator.rb +3 -3
- data/lib/site_prism/dsl.rb +57 -81
- data/lib/site_prism/dsl_validator.rb +75 -0
- data/lib/site_prism/element_checker.rb +3 -19
- data/lib/site_prism/error.rb +6 -10
- data/lib/site_prism/loadable.rb +4 -5
- data/lib/site_prism/page.rb +39 -1
- data/lib/site_prism/rspec_matchers.rb +9 -3
- data/lib/site_prism/section.rb +12 -11
- data/lib/site_prism/timer.rb +10 -10
- data/lib/site_prism/version.rb +1 -1
- data/lib/site_prism/waiter.rb +4 -4
- data/lib/site_prism.rb +4 -12
- metadata +39 -59
- data/lib/site_prism/recursion_checker.rb +0 -77
data/lib/site_prism/dsl.rb
CHANGED
@@ -11,65 +11,39 @@ module SitePrism
|
|
11
11
|
module DSL
|
12
12
|
def self.included(klass)
|
13
13
|
klass.extend ClassMethods
|
14
|
+
klass.extend DSLValidator
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
17
18
|
|
18
|
-
|
19
|
+
def raise_if_runtime_block_supplied(object, name, has_block, type)
|
20
|
+
return unless has_block
|
21
|
+
|
22
|
+
SitePrism.logger.debug("Type passed in: #{type}")
|
23
|
+
SitePrism.logger.error("#{object.class}##{name} cannot accept runtime blocks")
|
24
|
+
raise SitePrism::UnsupportedBlockError
|
25
|
+
end
|
26
|
+
|
19
27
|
def _find(*find_args)
|
20
28
|
kwargs = find_args.pop
|
21
29
|
to_capybara_node.find(*find_args, **kwargs)
|
22
30
|
end
|
23
31
|
|
24
|
-
# Call `all` inside `to_capybara_node` context (Either Capybara::Session or Capybara::Node::Element)
|
25
32
|
def _all(*find_args)
|
26
33
|
kwargs = find_args.pop
|
27
34
|
to_capybara_node.all(*find_args, **kwargs)
|
28
35
|
end
|
29
36
|
|
30
|
-
# Call `has_selector?` inside `to_capybara_node` context (Either Capybara::Session or Capybara::Node::Element)
|
31
37
|
def element_exists?(*find_args)
|
32
38
|
kwargs = find_args.pop
|
33
39
|
to_capybara_node.has_selector?(*find_args, **kwargs)
|
34
40
|
end
|
35
41
|
|
36
|
-
# Call `has_no_selector?` inside `to_capybara_node` context (Either Capybara::Session or Capybara::Node::Element)
|
37
42
|
def element_does_not_exist?(*find_args)
|
38
43
|
kwargs = find_args.pop
|
39
44
|
to_capybara_node.has_no_selector?(*find_args, **kwargs)
|
40
45
|
end
|
41
46
|
|
42
|
-
# Prevent users from calling methods with blocks when they shouldn't be.
|
43
|
-
#
|
44
|
-
# Example (Triggering error):
|
45
|
-
#
|
46
|
-
# class MyPage
|
47
|
-
# element :sample, '.css-locator' do
|
48
|
-
# puts "This won't be output"
|
49
|
-
# end
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# At runtime this will generate a `SitePrism::UnsupportedBlockError`
|
53
|
-
#
|
54
|
-
# The only DSL keywords that can use blocks are :section and :iframe
|
55
|
-
def raise_if_block(obj, name, has_block, type)
|
56
|
-
return unless has_block
|
57
|
-
|
58
|
-
SitePrism.logger.debug("Type passed in: #{type}")
|
59
|
-
SitePrism.logger.warn('section / iFrame can only accept blocks.')
|
60
|
-
SitePrism.logger.error("#{obj.class}##{name} does not accept blocks")
|
61
|
-
|
62
|
-
raise SitePrism::UnsupportedBlockError
|
63
|
-
end
|
64
|
-
|
65
|
-
# Warn users from naming the elements starting with no_
|
66
|
-
def warn_if_dsl_collision(obj, name)
|
67
|
-
return unless name.to_s.start_with?('no_')
|
68
|
-
|
69
|
-
SitePrism.logger.warn("#{obj.class}##{name} should not start with no_")
|
70
|
-
SitePrism::Deprecator.deprecate('Using no_ prefix in DSL definition')
|
71
|
-
end
|
72
|
-
|
73
47
|
# Sanitize method called before calling any SitePrism DSL method or
|
74
48
|
# meta-programmed method. This ensures that the Capybara query is correct.
|
75
49
|
#
|
@@ -112,17 +86,23 @@ module SitePrism
|
|
112
86
|
module ClassMethods
|
113
87
|
attr_reader :expected_items
|
114
88
|
|
89
|
+
# Sets the `expected_items` iVar on a class. This property is used in conjunction with
|
90
|
+
# `all_there?` to provide a way of granularising the check made to only interrogate a sub-set
|
91
|
+
# of DSL defined items
|
92
|
+
def expected_elements(*elements)
|
93
|
+
@expected_items = elements
|
94
|
+
end
|
95
|
+
|
115
96
|
# Creates an instance of a SitePrism Element - This will create several methods designed to
|
116
97
|
# Locate the element -> @return [Capybara::Node::Element]
|
117
98
|
# Check the elements presence or non-presence -> @return [Boolean]
|
118
99
|
# Wait for the elements to be present or not -> @return [TrueClass, SitePrism::Error]
|
119
100
|
# Validate certain properties about the element
|
120
101
|
def element(name, *find_args)
|
121
|
-
|
102
|
+
raise_if_build_time_block_supplied(self, name, block_given?, :element)
|
122
103
|
build(:element, name, *find_args) do
|
123
|
-
define_method(name) do |*runtime_args, &
|
124
|
-
|
125
|
-
raise_if_block(self, name, !element_block.nil?, :element)
|
104
|
+
define_method(name) do |*runtime_args, &runtime_block|
|
105
|
+
raise_if_runtime_block_supplied(self, name, runtime_block, :element)
|
126
106
|
_find(*merge_args(find_args, runtime_args))
|
127
107
|
end
|
128
108
|
end
|
@@ -134,23 +114,15 @@ module SitePrism
|
|
134
114
|
# Wait for the elements to be present or not -> @return [TrueClass, SitePrism::Error]
|
135
115
|
# Validate certain properties about the elements
|
136
116
|
def elements(name, *find_args)
|
137
|
-
|
117
|
+
raise_if_build_time_block_supplied(self, name, block_given?, :elements)
|
138
118
|
build(:elements, name, *find_args) do
|
139
|
-
define_method(name) do |*runtime_args, &
|
140
|
-
|
141
|
-
raise_if_block(self, name, !element_block.nil?, :elements)
|
119
|
+
define_method(name) do |*runtime_args, &runtime_block|
|
120
|
+
raise_if_runtime_block_supplied(self, name, runtime_block, :elements)
|
142
121
|
_all(*merge_args(find_args, runtime_args))
|
143
122
|
end
|
144
123
|
end
|
145
124
|
end
|
146
125
|
|
147
|
-
# Sets the `expected_items` iVar on a class. This property is used in conjunction with
|
148
|
-
# `all_there?` to provide a way of granularising the check made to only interrogate a sub-set
|
149
|
-
# of DSL defined items
|
150
|
-
def expected_elements(*elements)
|
151
|
-
@expected_items = elements
|
152
|
-
end
|
153
|
-
|
154
126
|
# Creates an instance of a SitePrism Section - This will create several methods designed to
|
155
127
|
# Locate the section -> @return [SitePrism::Section]
|
156
128
|
# Check the section presence or non-presence -> @return [Boolean]
|
@@ -160,7 +132,6 @@ module SitePrism
|
|
160
132
|
section_class, find_args = extract_section_options(args, &block)
|
161
133
|
build(:section, name, *find_args) do
|
162
134
|
define_method(name) do |*runtime_args, &runtime_block|
|
163
|
-
warn_if_dsl_collision(self, name)
|
164
135
|
section_element = _find(*merge_args(find_args, runtime_args))
|
165
136
|
section_class.new(self, section_element, &runtime_block)
|
166
137
|
end
|
@@ -175,8 +146,8 @@ module SitePrism
|
|
175
146
|
def sections(name, *args, &block)
|
176
147
|
section_class, find_args = extract_section_options(args, &block)
|
177
148
|
build(:sections, name, *find_args) do
|
178
|
-
define_method(name) do |*runtime_args, &
|
179
|
-
|
149
|
+
define_method(name) do |*runtime_args, &runtime_block|
|
150
|
+
raise_if_runtime_block_supplied(self, name, runtime_block, :sections)
|
180
151
|
_all(*merge_args(find_args, runtime_args)).map do |element|
|
181
152
|
section_class.new(self, element)
|
182
153
|
end
|
@@ -185,7 +156,7 @@ module SitePrism
|
|
185
156
|
end
|
186
157
|
|
187
158
|
def iframe(name, klass, *args)
|
188
|
-
|
159
|
+
raise_if_build_time_block_supplied(self, name, block_given?, :elements)
|
189
160
|
element_find_args = deduce_iframe_element_find_args(args)
|
190
161
|
scope_find_args = deduce_iframe_scope_find_args(args)
|
191
162
|
build(:iframe, name, *element_find_args) do
|
@@ -200,46 +171,30 @@ module SitePrism
|
|
200
171
|
# Return a list of all mapped items on a SitePrism class instance (Page or Section)
|
201
172
|
# If legacy is set to true (Default) -> @return [Array]
|
202
173
|
# If legacy is set to false (New behaviour) -> @return [Hash]
|
203
|
-
def mapped_items(legacy:
|
204
|
-
return
|
174
|
+
def mapped_items(legacy: false)
|
175
|
+
return legacy_mapped_items if legacy
|
205
176
|
|
206
|
-
|
177
|
+
@mapped_items ||= { element: [], elements: [], section: [], sections: [], iframe: [] }
|
207
178
|
end
|
208
179
|
|
209
180
|
private
|
210
181
|
|
211
|
-
def old_mapped_items
|
212
|
-
SitePrism::Deprecator.soft_deprecate(
|
213
|
-
'.mapped_items on a class',
|
214
|
-
'To allow easier recursion through the items in conjunction with #all_there?',
|
215
|
-
'.mapped_items(legacy: false)'
|
216
|
-
)
|
217
|
-
@old_mapped_items ||= []
|
218
|
-
end
|
219
|
-
|
220
|
-
def new_mapped_items
|
221
|
-
@new_mapped_items ||= { element: [], elements: [], section: [], sections: [], iframe: [] }
|
222
|
-
end
|
223
|
-
|
224
182
|
def build(type, name, *find_args)
|
183
|
+
raise InvalidDSLNameError if ENV.fetch('SITEPRISM_DSL_VALIDATION_ENABLED', nil) && invalid?(name)
|
184
|
+
|
225
185
|
if find_args.empty?
|
226
186
|
create_error_method(name)
|
227
187
|
else
|
228
188
|
map_item(type, name)
|
229
189
|
yield
|
230
190
|
end
|
231
|
-
add_helper_methods(name, *find_args)
|
191
|
+
add_helper_methods(name, type, *find_args)
|
232
192
|
end
|
233
193
|
|
234
|
-
def
|
235
|
-
old_mapped_items << { type => name }
|
236
|
-
new_mapped_items[type] << name.to_sym
|
237
|
-
end
|
238
|
-
|
239
|
-
def add_helper_methods(name, *find_args)
|
194
|
+
def add_helper_methods(name, _type, *find_args)
|
240
195
|
create_existence_checker(name, *find_args)
|
241
196
|
create_nonexistence_checker(name, *find_args)
|
242
|
-
SitePrism::
|
197
|
+
SitePrism::RSpecMatchers.new(name)._create_rspec_existence_matchers if defined?(RSpec)
|
243
198
|
create_visibility_waiter(name, *find_args)
|
244
199
|
create_invisibility_waiter(name, *find_args)
|
245
200
|
end
|
@@ -295,14 +250,35 @@ module SitePrism
|
|
295
250
|
end
|
296
251
|
|
297
252
|
def create_error_method(name)
|
298
|
-
SitePrism.
|
299
|
-
SitePrism::Deprecator.soft_deprecate(
|
253
|
+
SitePrism::Deprecator.deprecate(
|
300
254
|
'DSL definition with no find_args',
|
301
|
-
'
|
255
|
+
'DSL definition with at least 1 find_arg'
|
302
256
|
)
|
257
|
+
SitePrism.logger.error("#{name} has come from an item with no locators.")
|
303
258
|
define_method(name) { raise SitePrism::InvalidElementError }
|
304
259
|
end
|
305
260
|
|
261
|
+
def raise_if_build_time_block_supplied(parent_object, name, has_block, type)
|
262
|
+
return unless has_block
|
263
|
+
|
264
|
+
SitePrism.logger.debug("Type passed in: #{type}")
|
265
|
+
SitePrism.logger.error("#{name} has been defined as a '#{type}' item in #{parent_object}. It does not accept build-time blocks.")
|
266
|
+
raise SitePrism::UnsupportedBlockError
|
267
|
+
end
|
268
|
+
|
269
|
+
def legacy_mapped_items
|
270
|
+
SitePrism::Deprecator.deprecate(
|
271
|
+
'.mapped_items structure (internally), on a class',
|
272
|
+
'Thew new .mapped_items structure'
|
273
|
+
)
|
274
|
+
@legacy_mapped_items ||= []
|
275
|
+
end
|
276
|
+
|
277
|
+
def map_item(type, name)
|
278
|
+
mapped_items(legacy: true) << { type => name }
|
279
|
+
mapped_items[type] << name.to_sym
|
280
|
+
end
|
281
|
+
|
306
282
|
def deduce_iframe_scope_find_args(args)
|
307
283
|
warn_on_invalid_selector_input(args)
|
308
284
|
case args[0]
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SitePrism
|
4
|
+
# [SitePrism::DSLValidator]
|
5
|
+
#
|
6
|
+
# This is the new validator module which will check all DSL items against a whitelist
|
7
|
+
# for any entries which are prohibited
|
8
|
+
module DSLValidator
|
9
|
+
def invalid?(name)
|
10
|
+
prefix_invalid?(name) ||
|
11
|
+
suffix_invalid?(name) ||
|
12
|
+
characters_invalid?(name) ||
|
13
|
+
blacklisted?(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def prefix_invalid?(name)
|
19
|
+
prefix_blacklist.any? { |prefix| name.start_with?(prefix) }.tap { |result| log_failure(name, 'prefix') unless result }
|
20
|
+
end
|
21
|
+
|
22
|
+
def suffix_invalid?(name)
|
23
|
+
suffix_blacklist.any? { |prefix| name.end_with?(prefix) }.tap { |result| log_failure(name, 'suffix') unless result }
|
24
|
+
end
|
25
|
+
|
26
|
+
def characters_invalid?(name)
|
27
|
+
!name.match?(regex_permission).tap { |result| log_failure(name, 'character(s)') unless result }
|
28
|
+
end
|
29
|
+
|
30
|
+
def blacklisted?(name)
|
31
|
+
blacklisted_names.include?(name).tap { |result| log_failure(name, 'name (blacklisted entry)') unless result }
|
32
|
+
end
|
33
|
+
|
34
|
+
def regex_permission
|
35
|
+
/^[a-z]\w+$/
|
36
|
+
end
|
37
|
+
|
38
|
+
def prefix_blacklist
|
39
|
+
%w[
|
40
|
+
no_
|
41
|
+
_
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def suffix_blacklist
|
46
|
+
%w[
|
47
|
+
_
|
48
|
+
?
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
def blacklisted_names
|
53
|
+
%w[
|
54
|
+
attributes
|
55
|
+
html
|
56
|
+
no
|
57
|
+
title
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_failure(name, type)
|
62
|
+
SitePrism.logger.error("DSL item: #{name} has an invalid #{type}")
|
63
|
+
SitePrism.logger.debug(debug_error(type))
|
64
|
+
end
|
65
|
+
|
66
|
+
def debug_error(type)
|
67
|
+
case type
|
68
|
+
when 'prefix'; then "Invalid Prefixes: #{prefix_blacklist.join(', ')}."
|
69
|
+
when 'suffix'; then "Invalid Suffixes: #{suffix_blacklist.join(', ')}"
|
70
|
+
when 'character(s)'; then "Invalid DSL Names: #{blacklisted_names.join(', ')}"
|
71
|
+
else "DSL Charset REGEX: #{regex_permission.inspect}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SitePrism
|
4
|
+
#
|
4
5
|
# [SitePrism::ElementChecker]
|
5
6
|
#
|
6
7
|
# This allows users to run `#all_there?` checks on an instance.
|
7
8
|
#
|
8
|
-
# NB: This functionality is being removed in v4 in favour of the all_there gem
|
9
9
|
module ElementChecker
|
10
10
|
# Runnable in the scope of any SitePrism::Page or Section.
|
11
11
|
# Returns +true+ when "every item" that is being checked is
|
@@ -26,13 +26,7 @@ module SitePrism
|
|
26
26
|
# Override: 'one' => Perform one recursive dive into all section/sections
|
27
27
|
# items and call #all_there? on all of those items too.
|
28
28
|
def all_there?(recursion: :none)
|
29
|
-
|
30
|
-
when :none; then elements_to_check.all? { |name| there?(name) }
|
31
|
-
when :one; then all_there_with_recursion
|
32
|
-
else
|
33
|
-
SitePrism.logger.debug("Input value '#{recursion}'. Valid values are :none or :one.")
|
34
|
-
SitePrism.logger.error('Invalid recursion setting, Will not run #all_there?.')
|
35
|
-
end
|
29
|
+
SitePrism::AllThere::RecursionChecker.new(self).all_there?(recursion: recursion)
|
36
30
|
end
|
37
31
|
|
38
32
|
# Returns each element that is currently present inside the scope being tested
|
@@ -51,16 +45,6 @@ module SitePrism
|
|
51
45
|
|
52
46
|
private
|
53
47
|
|
54
|
-
def all_there_with_recursion
|
55
|
-
if SitePrism.use_all_there_gem
|
56
|
-
SitePrism::AllThere::RecursionChecker.new(self).all_there?
|
57
|
-
else
|
58
|
-
RecursionChecker.new(self).all_there?
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# If the page or section has expected_items set, return expected_items that are mapped
|
63
|
-
# otherwise just return the list of all mapped_items
|
64
48
|
def elements_to_check
|
65
49
|
if _expected_items
|
66
50
|
SitePrism.logger.debug('Expected Items has been set.')
|
@@ -71,7 +55,7 @@ module SitePrism
|
|
71
55
|
end
|
72
56
|
|
73
57
|
def _mapped_items
|
74
|
-
self.class.mapped_items
|
58
|
+
self.class.mapped_items.values.flatten.uniq
|
75
59
|
end
|
76
60
|
|
77
61
|
def _expected_items
|
data/lib/site_prism/error.rb
CHANGED
@@ -8,50 +8,46 @@ module SitePrism
|
|
8
8
|
class PageLoadError < SitePrismError; end
|
9
9
|
|
10
10
|
# A page calls #load with no URL set
|
11
|
-
# Formerly known as `NoUrlForPage`
|
12
11
|
class NoUrlForPageError < PageLoadError; end
|
13
12
|
|
14
13
|
# A page calls #displayed? with no URL matcher set
|
15
|
-
# Formerly known as `NoUrlMatcherForPage`
|
16
14
|
class NoUrlMatcherForPageError < PageLoadError; end
|
17
15
|
|
18
16
|
# The URL matcher was not recognised as a Regex or String and as such
|
19
17
|
# it couldn't be parsed by Addressable. It also could be caused by
|
20
18
|
# the usage of templated port numbers - which aren't supported
|
21
|
-
# Formerly known as `InvalidUrlMatcher`
|
22
19
|
class InvalidUrlMatcherError < PageLoadError; end
|
23
20
|
|
24
21
|
# A SitePrism defined DSL item was defined without a selector
|
25
|
-
# Formerly known as `NoSelectorForElement`
|
26
22
|
class InvalidElementError < SitePrismError; end
|
27
23
|
|
28
24
|
# The condition that was being evaluated inside the block did not evaluate
|
29
25
|
# to true within the time limit
|
30
|
-
# Formerly known as `TimeoutException`
|
31
26
|
class TimeoutError < SitePrismError; end
|
32
27
|
|
33
28
|
# The wait_until_*_visible meta-programmed method didn't evaluate to true
|
34
29
|
# within the prescribed time limit
|
35
|
-
# Formerly known as `TimeOutWaitingForElementVisibility`
|
36
30
|
class ElementVisibilityTimeoutError < TimeoutError; end
|
37
31
|
|
38
32
|
# The wait_until_*_invisible meta-programmed method didn't evaluate to true
|
39
33
|
# within the prescribed time limit
|
40
|
-
# Formerly known as `TimeOutWaitingForElementInvisibility`
|
41
34
|
class ElementInvisibilityTimeoutError < TimeoutError; end
|
42
35
|
|
43
36
|
# Generic Block validation family of errors inherit from this error
|
44
37
|
class BlockError < SitePrismError; end
|
45
38
|
|
46
39
|
# A Block was passed to the method, which it cannot interpret
|
47
|
-
# Formerly known as `UnsupportedBlock`
|
48
40
|
class UnsupportedBlockError < BlockError; end
|
49
41
|
|
50
42
|
# A Block was required, but not supplied
|
51
|
-
# Formerly known as `BlockMissingError`
|
52
43
|
class MissingBlockError < BlockError; end
|
53
44
|
|
54
45
|
# A page was loaded then failed one of the validations defined by the user
|
55
|
-
# Formerly known as `NotLoadedError`
|
56
46
|
class FailedLoadValidationError < PageLoadError; end
|
47
|
+
|
48
|
+
# Generic Attribute validation family of errors inherit from this error
|
49
|
+
class AttributeValidationError < SitePrismError; end
|
50
|
+
|
51
|
+
# DSL items are not permitted to start with certain prefixes
|
52
|
+
class InvalidDSLNameError < AttributeValidationError; end
|
57
53
|
end
|
data/lib/site_prism/loadable.rb
CHANGED
@@ -43,10 +43,11 @@ module SitePrism
|
|
43
43
|
|
44
44
|
# Check if the page is loaded.
|
45
45
|
#
|
46
|
-
# On failure, if an error was reported by a failing validation,
|
47
|
-
# it will be available via the `load_error` accessor.
|
46
|
+
# On failure, if an error was reported by a failing validation, it will be available via the `load_error` accessor
|
48
47
|
#
|
49
|
-
#
|
48
|
+
# It will return true if the page has been loaded successfully; otherwise it returns false
|
49
|
+
#
|
50
|
+
# @return [Boolean]
|
50
51
|
def loaded?
|
51
52
|
self.load_error = nil
|
52
53
|
|
@@ -57,8 +58,6 @@ module SitePrism
|
|
57
58
|
|
58
59
|
private
|
59
60
|
|
60
|
-
# If any load validations from page subclasses returns false,
|
61
|
-
# immediately return false.
|
62
61
|
def load_validations_pass?
|
63
62
|
self.class.load_validations.all? do |validation|
|
64
63
|
passed, message = instance_eval(&validation)
|
data/lib/site_prism/page.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SitePrism
|
4
|
+
# [SitePrism::Page]
|
5
|
+
#
|
6
|
+
# SitePrism Pages are the top level construct of the POM framework
|
7
|
+
#
|
8
|
+
# Instances of this class represent a full web page that can either be dynamically navigated to
|
9
|
+
# through clicking buttons or filling in fields, or verbosely loaded by using the `#load` method
|
10
|
+
#
|
11
|
+
# All method calls made whilst on a page are scoped using `#to_capybara_node` which defaults to the
|
12
|
+
# current Capybara session or the `@page` that has been loaded in-line
|
4
13
|
class Page
|
5
14
|
include Capybara::DSL
|
6
15
|
include ElementChecker
|
@@ -39,12 +48,15 @@ module SitePrism
|
|
39
48
|
#
|
40
49
|
# @return [Capybara::Node::Simple || Capybara::Session]
|
41
50
|
def page
|
51
|
+
SitePrism::Deprecator.deprecate('Calling #page on a SitePrism::Page instance')
|
42
52
|
(defined?(@page) && @page) || Capybara.current_session
|
43
53
|
end
|
44
54
|
|
45
55
|
# This scopes our calls inside Page correctly to the `Capybara::Session`
|
56
|
+
#
|
57
|
+
# @return [Capybara::Node::Simple || Capybara::Session]
|
46
58
|
def to_capybara_node
|
47
|
-
page
|
59
|
+
(defined?(@page) && @page) || Capybara.current_session
|
48
60
|
end
|
49
61
|
|
50
62
|
# Loads the page.
|
@@ -76,12 +88,21 @@ module SitePrism
|
|
76
88
|
return_yield || true
|
77
89
|
end
|
78
90
|
|
91
|
+
# Returns true if the page is displayed within the requisite time
|
92
|
+
# Returns false if the page is not displayed within the requisite time
|
93
|
+
#
|
94
|
+
# @return [Boolean]
|
79
95
|
def displayed?(*args)
|
80
96
|
wait_until_displayed(*args)
|
81
97
|
rescue SitePrism::TimeoutError
|
82
98
|
false
|
83
99
|
end
|
84
100
|
|
101
|
+
# Wait until the page is displayed according to input arguments
|
102
|
+
# If no url_matcher is provided we don't know how to determine if the page is displayed. So we return an error
|
103
|
+
# Then we wait until the url matches the expected mappings
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
85
106
|
def wait_until_displayed(*args)
|
86
107
|
raise SitePrism::NoUrlMatcherForPageError unless url_matcher
|
87
108
|
|
@@ -90,6 +111,13 @@ module SitePrism
|
|
90
111
|
Waiter.wait_until_true(seconds) { url_matches?(expected_mappings) }
|
91
112
|
end
|
92
113
|
|
114
|
+
# Return the matching information of a page
|
115
|
+
#
|
116
|
+
# Return nil if the page is not displayed correctly
|
117
|
+
# Return the regex matches if we have provided a regexp style url_matcher
|
118
|
+
# Otherwise fall back to an addressable-style template of matches
|
119
|
+
#
|
120
|
+
# @return [Nil || MatchData || Hash]
|
93
121
|
def url_matches(seconds = Capybara.default_max_wait_time)
|
94
122
|
return unless displayed?(seconds)
|
95
123
|
return regexp_backed_matches if url_matcher.is_a?(Regexp)
|
@@ -97,14 +125,24 @@ module SitePrism
|
|
97
125
|
template_backed_matches
|
98
126
|
end
|
99
127
|
|
128
|
+
# Returns the templated url from the set_url property defined during the page definition
|
129
|
+
# Returns `nil` if there was not a property set (i.e. the page should not be directly loaded)
|
130
|
+
#
|
131
|
+
# @return [NilClass || String]
|
100
132
|
def url(expansion = {})
|
101
133
|
self.class.url && Addressable::Template.new(self.class.url).expand(expansion).to_s
|
102
134
|
end
|
103
135
|
|
136
|
+
# Returns the url_matcher property defined during the page definition
|
137
|
+
#
|
138
|
+
# @return [Regexp]
|
104
139
|
def url_matcher
|
105
140
|
self.class.url_matcher
|
106
141
|
end
|
107
142
|
|
143
|
+
# Returns true if the page is secure, otherwise returns false
|
144
|
+
#
|
145
|
+
# @return [Boolean]
|
108
146
|
def secure?
|
109
147
|
page.current_url.start_with?('https')
|
110
148
|
end
|
@@ -1,13 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SitePrism
|
4
|
-
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
#
|
7
|
+
class RSpecMatchers
|
5
8
|
attr_reader :element_name
|
6
9
|
|
7
10
|
def initialize(element_name)
|
8
11
|
@element_name = element_name
|
9
12
|
end
|
10
13
|
|
14
|
+
# Create the positive and negative rspec matchers that will use the SitePrism boolean methods
|
15
|
+
#
|
16
|
+
# @return [Symbol]
|
11
17
|
def _create_rspec_existence_matchers
|
12
18
|
SitePrism.logger.debug('Including all relevant matcher names / warnings in RSpec scope.')
|
13
19
|
create_rspec_existence_matchers(matcher, object_method, negated_object_method, warning)
|
@@ -40,8 +46,8 @@ module SitePrism
|
|
40
46
|
end
|
41
47
|
|
42
48
|
def warning
|
43
|
-
"The RSpec matcher '#{matcher}' was added by SitePrism, but the object under test "\
|
44
|
-
"does not respond to '#{negated_object_method}' and is probably not a SitePrism object. "\
|
49
|
+
"The RSpec matcher '#{matcher}' was added by SitePrism, but the object under test " \
|
50
|
+
"does not respond to '#{negated_object_method}' and is probably not a SitePrism object. " \
|
45
51
|
'Falling back to the default RSpec matcher.'
|
46
52
|
end
|
47
53
|
end
|
data/lib/site_prism/section.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SitePrism
|
4
|
+
# [SitePrism::Section]
|
5
|
+
#
|
6
|
+
# SitePrism Sections are the mid level construct of the POM framework
|
7
|
+
#
|
8
|
+
# Instances of this class represent a a part of a web page that can either sit inside a SitePrism::Page
|
9
|
+
# or sit inside another N sections, which then eventually will sit inside a page
|
10
|
+
#
|
11
|
+
# All method calls made whilst on a page are scoped using `#to_capybara_node` which will be represented by
|
12
|
+
# the current `#root_element`. This is the locator for the section itself and is a mandatory argument
|
4
13
|
class Section
|
5
14
|
include ElementChecker
|
6
15
|
include Loadable
|
@@ -55,22 +64,14 @@ module SitePrism
|
|
55
64
|
root_element
|
56
65
|
end
|
57
66
|
|
58
|
-
# This allows us to return anything
|
67
|
+
# This allows us to return anything that's passed in as a block to the section at
|
59
68
|
# creation time, so that an anonymous section or such-like will have the extra methods
|
69
|
+
#
|
70
|
+
# This can also be used manually at runtime to allow people to abbreviate their calls
|
60
71
|
def within
|
61
72
|
Capybara.within(root_element) { yield(self) }
|
62
73
|
end
|
63
74
|
|
64
|
-
# This was the old API-style of delegating through the Capybara.page call and over-loading
|
65
|
-
# the method so we always went through our correct `root_element`
|
66
|
-
def page
|
67
|
-
SitePrism::Deprecator.deprecate('Using page inside section')
|
68
|
-
return root_element if root_element
|
69
|
-
|
70
|
-
SitePrism.logger.warn('Root Element not found; Falling back to Capybara.current_session')
|
71
|
-
capybara_session
|
72
|
-
end
|
73
|
-
|
74
75
|
def capybara_session
|
75
76
|
Capybara.current_session
|
76
77
|
end
|
data/lib/site_prism/timer.rb
CHANGED
@@ -7,9 +7,9 @@ module SitePrism
|
|
7
7
|
class Timer
|
8
8
|
attr_reader :wait_time
|
9
9
|
|
10
|
-
# Return &block
|
11
|
-
#
|
12
10
|
# Count towards a specified time (Supplied)
|
11
|
+
#
|
12
|
+
# @return [Proc]
|
13
13
|
def self.run(wait_time, &block)
|
14
14
|
new(wait_time).run(&block)
|
15
15
|
end
|
@@ -19,16 +19,16 @@ module SitePrism
|
|
19
19
|
@done = false
|
20
20
|
end
|
21
21
|
|
22
|
-
# Return Boolean
|
23
|
-
#
|
24
22
|
# Whether the timer has completed
|
23
|
+
#
|
24
|
+
# @return [Boolean]
|
25
25
|
def done?
|
26
26
|
@done == true
|
27
27
|
end
|
28
28
|
|
29
|
-
# Return &block
|
30
|
-
#
|
31
29
|
# Start the Timer and re-evaluate the block repeatedly
|
30
|
+
#
|
31
|
+
# @return [Proc]
|
32
32
|
def run
|
33
33
|
start
|
34
34
|
yield self
|
@@ -36,9 +36,9 @@ module SitePrism
|
|
36
36
|
stop
|
37
37
|
end
|
38
38
|
|
39
|
-
# Return [Boolean, Nil]
|
40
|
-
#
|
41
39
|
# Start the Timer in a separate process
|
40
|
+
#
|
41
|
+
# Return [True]
|
42
42
|
def start
|
43
43
|
stop
|
44
44
|
return if wait_time.zero?
|
@@ -50,9 +50,9 @@ module SitePrism
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
# Return True
|
54
|
-
#
|
55
53
|
# Forcibly stop the timer, and kill any threads created by it
|
54
|
+
#
|
55
|
+
# Return [True]
|
56
56
|
def stop
|
57
57
|
if @thread
|
58
58
|
@thread.kill
|