site_prism 2.13 → 2.14

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: 4c6e366dcebe267faac30347ca1d1df9083a35a8
4
- data.tar.gz: 073d46c7c242f29985a11f788d4647be3f1d0aa0
3
+ metadata.gz: 3b74604cbe9c1815149c4bce7c747d9087098ade
4
+ data.tar.gz: 5043802657e6d0f88d6f52ea8790b49b8e634dd5
5
5
  SHA512:
6
- metadata.gz: cd48654ba019e204f5631b571f25e81d2a6dcdf4b8dda728fc40c1a1d8e26f74255cdd2b49dbf12dacd4dca489bc843b20c00cb711884b05cc43b0c94ee14c23
7
- data.tar.gz: 1422a9b00a5a7a891e043d749bddb47efc1c65d85a01c2481d6bc420079171ea89a9ff63e289afd1a2bf4d91a888a9330d7508c7c8008d93bebf8ece9fc52e0f
6
+ metadata.gz: 4683666aa25142921dfab489841ef22eed10768978cd6c0ba69d768517a28cfcd7ef236fa5291c9a951836ea3bf3f762e2c25451ff5ad30940ec7db071c204cb
7
+ data.tar.gz: 5ade19c3956fbf314b096831c72f9c0d9f572ab5995384d389cfd795ada012b4e63e3036ca612fafa3b78658bb037391bddc19f09a1e6fa97f27772901a0a1c1
data/README.md CHANGED
@@ -746,7 +746,6 @@ present in the browser, false if they're not all there.
746
746
  Then /^the friends page contains all the expected elements$/ do
747
747
  expect(@friends_page).to be_all_there
748
748
  end
749
-
750
749
  ```
751
750
 
752
751
  You may wish to have elements declared in a page object class that are not always guaranteed to be present (success or error messages, etc.). If you'd still like to test such a page with `all_there?` you can declare `expected_elements` on your page object class that narrows the elements included in `all_there?` check to those that definitely should be present.
@@ -761,6 +760,20 @@ class TestPage < SitePrism::Page
761
760
  end
762
761
  ```
763
762
 
763
+ And if you aren't sure which elements are present and which are, Then ask SitePrism to tell you!
764
+
765
+ ```ruby
766
+ class TestPage < SitePrism::Page
767
+ element :name_field, '#name'
768
+ element :address_field, '#address'
769
+ element :success_msg, 'span.alert-success'
770
+ end
771
+
772
+ # and... Only `address_field` is on the page
773
+
774
+ @test_page.elements_present #=> [:address_field]
775
+ ```
776
+
764
777
  ## Sections
765
778
 
766
779
  SitePrism allows you to model sections of a page that appear on multiple
@@ -804,12 +817,44 @@ SitePrism allows adding sections to sections) is to call the `section`
804
817
  method. It takes 3 arguments: the first is the name of the section as
805
818
  referred to on the page (sections that appear on multiple pages can be
806
819
  named differently). The second argument is the class of which an
807
- instance will be created to represent the page section, and the third
808
- argument is a css selector that identifies the root node of the section
820
+ instance will be created to represent the page section, and the following
821
+ arguments are [Capybara::Node::Finders](https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Finders).
822
+ These identify the root node of the section
809
823
  on this page (note that the css selector can be different for different
810
824
  pages as the whole point of sections is that they can appear in
811
825
  different places on different pages).
812
826
 
827
+ If you define a section as a class and as an Anonymous section,
828
+ you can have some handy constructs like the one below
829
+
830
+ ```ruby
831
+ class People < SitePrism::Section
832
+ element :headline, 'h2'
833
+ end
834
+
835
+ class HomePage < SitePrism::Page
836
+ # section people_with_block will have headline and
837
+ # headline_clone elements in it
838
+ section :people_with_block, People do
839
+ element :headline_clone, 'h2'
840
+ end
841
+ end
842
+ ```
843
+
844
+ The 3rd argument (Locators), can be omitted if you are re-using the same locator for all
845
+ references to the section Class. In order to do this, simply tell SitePrism that
846
+ you want to use a default search argument.
847
+
848
+ ```ruby
849
+ class People < SitePrism::Section
850
+ set_default_search_arguments '.people'
851
+ end
852
+
853
+ class Home < SitePrism::Page
854
+ section :people, People
855
+ end
856
+ ```
857
+
813
858
  #### Accessing a page's section
814
859
 
815
860
  The `section` method (like the `element` method) adds a few methods to
@@ -1265,7 +1310,7 @@ end
1265
1310
 
1266
1311
  ```ruby
1267
1312
  Then /^there are search results on the page$/ do
1268
- expect(@results.page).to have_search_results
1313
+ expect(@results_page).to have_search_results
1269
1314
  end
1270
1315
  ```
1271
1316
 
@@ -1493,7 +1538,7 @@ into our page and section classes:
1493
1538
 
1494
1539
  ```ruby
1495
1540
  Then /^there are search results on the page$/ do
1496
- expect(@results.page).to have_search_results(count: 25)
1541
+ expect(@results_page).to have_search_results(count: 25)
1497
1542
  end
1498
1543
  ```
1499
1544
 
@@ -1688,6 +1733,42 @@ with this:
1688
1733
  @search_page.search_results
1689
1734
  ```
1690
1735
 
1736
+ ## Raising Errors on wait_for
1737
+
1738
+ By default, when using `wait_for_*` and `wait_for_no_*` methods, SitePrism will not raise
1739
+ an error if an element does not appear or disappear within the timeout period and will
1740
+ simply return `false` and allow the test to continue. This is different from
1741
+ the other methods such as `wait_until_*_visible` which do raise errors.
1742
+
1743
+ Add the following code your spec_helper file to enable errors to be
1744
+ raised immediately when a `wait_for_*` method does not find the element it is
1745
+ waiting for and when a `wait_for_no_*` method continues to find an element it is
1746
+ waiting to not exist:
1747
+
1748
+ ```ruby
1749
+ SitePrism.configure do |config|
1750
+ config.raise_on_wait_fors = true
1751
+ end
1752
+ ```
1753
+
1754
+ This enables you to replace this:
1755
+
1756
+ ```ruby
1757
+ raise unless @search_page.wait_for_search_results
1758
+ # or...
1759
+ raise unless @search_page.wait_for_no_search_results
1760
+ ```
1761
+
1762
+ with this:
1763
+
1764
+ ```
1765
+ # With raise on wait_fors enabled, this will automatically raise
1766
+ # if no search results are found
1767
+ @search_page.wait_for_search_results
1768
+ # or automatically raise if search results are still found
1769
+ @search_page.wait_for_no_search_results
1770
+ ```
1771
+
1691
1772
  ## Using SitePrism with VCR
1692
1773
 
1693
1774
  There's a SitePrism plugin called `site_prism.vcr` that lets you use
@@ -12,7 +12,7 @@ module SitePrism
12
12
  autoload :AddressableUrlMatcher, 'site_prism/addressable_url_matcher'
13
13
 
14
14
  class << self
15
- attr_accessor :use_implicit_waits
15
+ attr_accessor :use_implicit_waits, :raise_on_wait_fors
16
16
 
17
17
  def configure
18
18
  yield self
@@ -20,4 +20,5 @@ module SitePrism
20
20
  end
21
21
 
22
22
  @use_implicit_waits = false
23
+ @raise_on_wait_fors = false
23
24
  end
@@ -5,11 +5,10 @@ require 'base64'
5
5
 
6
6
  module SitePrism
7
7
  class AddressableUrlMatcher
8
- COMPONENT_NAMES = %i[scheme user password host port path query fragment].freeze
9
- COMPONENT_PREFIXES = {
10
- query: '?',
11
- fragment: '#'
12
- }.freeze
8
+ COMPONENT_NAMES = %i[scheme user password host
9
+ port path query fragment ].freeze
10
+
11
+ COMPONENT_PREFIXES = { query: '?', fragment: '#' }.freeze
13
12
 
14
13
  attr_reader :pattern
15
14
 
@@ -17,7 +16,8 @@ module SitePrism
17
16
  @pattern = pattern
18
17
  end
19
18
 
20
- # @return the hash of extracted mappings from parsing the provided URL according to our pattern,
19
+ # @return the hash of extracted mappings from
20
+ # parsing the provided URL according to our pattern,
21
21
  # or nil if the URL doesn't conform to the matcher template.
22
22
  def mappings(url)
23
23
  uri = Addressable::URI.parse(url)
@@ -31,15 +31,15 @@ module SitePrism
31
31
  result
32
32
  end
33
33
 
34
- # Determine whether URL matches our pattern, and optionally whether the extracted mappings match
35
- # a hash of expected values. You can specify values as strings, numbers or regular expressions.
34
+ # Determine whether URL matches our pattern, and
35
+ # optionally whether the extracted mappings match
36
+ # a hash of expected values. You can specify values
37
+ # as strings, numbers or regular expressions.
36
38
  def matches?(url, expected_mappings = {})
37
39
  actual_mappings = mappings(url)
38
- if actual_mappings
39
- expected_mappings.empty? || all_expected_mappings_match?(expected_mappings, actual_mappings)
40
- else
41
- false
42
- end
40
+ return false unless actual_mappings
41
+ expected_mappings.empty? ||
42
+ all_expected_mappings_match?(expected_mappings, actual_mappings)
43
43
  end
44
44
 
45
45
  private
@@ -72,29 +72,30 @@ module SitePrism
72
72
  component_url = component_url.sub(substituted_value, template_value)
73
73
  end
74
74
 
75
- component_templates[component] = Addressable::Template.new(component_url.to_s)
75
+ component_templates[component] =
76
+ Addressable::Template.new(component_url.to_s)
76
77
  end
77
78
  end
78
79
 
79
- # Returns empty hash if the template omits the component, a set of substitutions if the
80
- # provided URI component matches the template component, or nil if the match fails.
80
+ # Returns empty hash if the template omits the component,
81
+ # a set of substitutions if the
82
+ # provided URI component matches the template component,
83
+ # or nil if the match fails.
81
84
  def component_matches(component, uri)
82
- extracted_mappings = {}
83
85
  component_template = component_templates[component]
84
- if component_template
85
- component_url = uri.public_send(component).to_s
86
- extracted_mappings = component_template.extract(component_url)
87
- unless extracted_mappings
88
- # to support Addressable's expansion of queries
89
- # ensure it's parsing the fragment as appropriate (e.g. {?params*})
90
- prefix = COMPONENT_PREFIXES[component]
91
- return nil unless prefix && (extracted_mappings = component_template.extract(prefix + component_url))
92
- end
93
- end
94
- extracted_mappings
86
+ return {} unless component_template
87
+ component_url = uri.public_send(component).to_s
88
+ mappings = component_template.extract(component_url)
89
+ return mappings if mappings
90
+ # to support Addressable's expansion of queries
91
+ # ensure it's parsing the fragment as appropriate (e.g. {?params*})
92
+ prefix = COMPONENT_PREFIXES[component]
93
+ return nil unless prefix
94
+ component_template.extract(prefix + component_url)
95
95
  end
96
96
 
97
- # Convert the pattern into an Addressable URI by substituting the template slugs with nonsense strings.
97
+ # Convert the pattern into an Addressable URI by substituting
98
+ # the template slugs with nonsense strings.
98
99
  def to_substituted_uri
99
100
  url = pattern
100
101
  substitutions.each_pair do |slug, value|
@@ -115,28 +116,34 @@ module SitePrism
115
116
  end
116
117
 
117
118
  def reverse_substitutions
118
- @reverse_substitutions ||= slugs.each_with_index.reduce({}) do |memo, slug_index|
119
- slug, index = slug_index
120
- memo.merge(slug_prefix(slug) + substitution_value(index) => slug, substitution_value(index) => slug)
121
- end
119
+ @reverse_substitutions ||=
120
+ slugs.each_with_index.reduce({}) do |memo, slug_index|
121
+ slug, index = slug_index
122
+ memo.merge(
123
+ slug_prefix(slug) + substitution_value(index) => slug,
124
+ substitution_value(index) => slug
125
+ )
126
+ end
122
127
  end
123
128
 
124
129
  def slugs
125
130
  pattern.scan(/{[^}]+}/)
126
131
  end
127
132
 
128
- # If a slug begins with non-alpha characters, it may denote the start of a new component
129
- # (e.g. query or fragment). We emit this prefix as part of the substituted slug
133
+ # If a slug begins with non-alpha characters,
134
+ # it may denote the start of a new component (e.g. query or fragment).
135
+ # We emit this prefix as part of the substituted slug
130
136
  # so that Addressable's URI parser can see it as such.
131
137
  def slug_prefix(slug)
132
- matches = slug.match(/\A{([^A-Za-z]+)/)
133
- matches && matches[1] || ''
138
+ prefix = slug.match(/\A{([^A-Za-z]+)/)
139
+ prefix && prefix[1] || ''
134
140
  end
135
141
 
136
142
  # Generate a repeatable 5 character uniform alphabetical nonsense string
137
143
  # to allow parsing as a URI
138
144
  def substitution_value(index)
139
- Base64.urlsafe_encode64(Digest::SHA1.digest(index.to_s)).gsub(/[^A-Za-z]/, '')[0..5]
145
+ sha = Digest::SHA1.digest(index.to_s)
146
+ Base64.urlsafe_encode64(sha).gsub(/[^A-Za-z]/, '')[0..5]
140
147
  end
141
148
  end
142
149
  end
@@ -3,15 +3,31 @@
3
3
  module SitePrism
4
4
  module ElementChecker
5
5
  def all_there?
6
- elements_to_check.all? { |element| send "has_#{element}?" }
6
+ elements_to_check.all? { |element| present?(element) }
7
+ end
8
+
9
+ def elements_present
10
+ mapped_items.select { |item_name| present?(item_name) }
7
11
  end
8
12
 
9
13
  private
10
14
 
11
15
  def elements_to_check
12
- elements = self.class.mapped_items
13
- elements = elements.select { |el| self.class.expected_items.include?(el) } if self.class.expected_items
14
- elements
16
+ if self.class.expected_items
17
+ mapped_items.select do |el|
18
+ self.class.expected_items.include?(el)
19
+ end
20
+ else
21
+ mapped_items
22
+ end
23
+ end
24
+
25
+ def mapped_items
26
+ self.class.mapped_items.uniq
27
+ end
28
+
29
+ def present?(element)
30
+ send("has_#{element}?")
15
31
  end
16
32
  end
17
33
  end
@@ -2,222 +2,275 @@
2
2
 
3
3
  module SitePrism
4
4
  module ElementContainer
5
- attr_reader :mapped_items, :expected_items
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ end
6
8
 
7
- def element(element_name, *find_args)
8
- build(element_name, *find_args) do
9
- define_method(element_name.to_s) do |*runtime_args, &element_block|
10
- self.class.raise_if_block(self, element_name.to_s, !element_block.nil?)
11
- find_first(*find_args, *runtime_args)
12
- end
13
- end
9
+ private
10
+
11
+ def raise_if_block(obj, name, has_block)
12
+ return unless has_block
13
+
14
+ raise SitePrism::UnsupportedBlock, "#{obj.class}##{name}"
14
15
  end
15
16
 
16
- def elements(collection_name, *find_args)
17
- build(collection_name, *find_args) do
18
- define_method(collection_name.to_s) do |*runtime_args, &element_block|
19
- self.class.raise_if_block(self, collection_name.to_s, !element_block.nil?)
20
- find_all(*find_args, *runtime_args)
21
- end
22
- end
17
+ def raise_wait_for_if_failed(obj, name, timeout, failed)
18
+ return unless SitePrism.raise_on_wait_fors && failed
19
+
20
+ raise SitePrism::TimeOutWaitingForExistenceError, \
21
+ "Timed out after #{timeout}s waiting for #{obj.class}##{name}"
23
22
  end
24
- alias collection elements
25
23
 
26
- def expected_elements(*elements)
27
- @expected_items = elements
24
+ def raise_wait_for_no_if_failed(obj, name, timeout, failed)
25
+ return unless SitePrism.raise_on_wait_fors && failed
26
+
27
+ raise SitePrism::TimeOutWaitingForNonExistenceError, \
28
+ "Timed out after #{timeout}s waiting for no #{obj.class}##{name}"
28
29
  end
29
30
 
30
- def section(section_name, *args, &block)
31
- section_class, find_args = extract_section_options(args, &block)
32
- build(section_name, *find_args) do
33
- define_method section_name do |*runtime_args, &runtime_block|
34
- section_class.new self, find_first(*find_args, *runtime_args), &runtime_block
31
+ def merge_args(find_args, runtime_args, override_options = {})
32
+ find_args = find_args.dup
33
+ runtime_args = runtime_args.dup
34
+ options = {}
35
+ options.merge!(find_args.pop) if find_args.last.is_a? Hash
36
+ options.merge!(runtime_args.pop) if runtime_args.last.is_a? Hash
37
+ options.merge!(override_options)
38
+ [*find_args, *runtime_args, options]
39
+ end
40
+
41
+ module ClassMethods
42
+ attr_reader :mapped_items, :expected_items
43
+
44
+ def element(element_name, *find_args)
45
+ build(element_name, *find_args) do
46
+ define_method(element_name.to_s) do |*runtime_args, &element_block|
47
+ raise_if_block(self, element_name.to_s, !element_block.nil?)
48
+ find_first(*merge_args(find_args, runtime_args))
49
+ end
35
50
  end
36
51
  end
37
- end
38
52
 
39
- def sections(section_collection_name, *args, &block)
40
- section_class, find_args = extract_section_options(args, &block)
41
- build(section_collection_name, *find_args) do
42
- define_method(section_collection_name) do |*runtime_args, &element_block|
43
- self.class.raise_if_block(self, section_collection_name.to_s, !element_block.nil?)
44
- find_all(*find_args, *runtime_args).map do |element|
45
- section_class.new(self, element)
53
+ def elements(collection_name, *find_args)
54
+ build(collection_name, *find_args) do
55
+ define_method(collection_name.to_s) do |*runtime_args, &element_block|
56
+ raise_if_block(self, collection_name.to_s, !element_block.nil?)
57
+ find_all(*merge_args(find_args, runtime_args))
46
58
  end
47
59
  end
48
60
  end
49
- end
61
+ alias collection elements
62
+
63
+ def expected_elements(*elements)
64
+ @expected_items = elements
65
+ end
50
66
 
51
- def iframe(iframe_name, iframe_page_class, *args)
52
- element_find_args = deduce_iframe_element_find_args(args)
53
- scope_find_args = deduce_iframe_scope_find_args(args)
54
- add_to_mapped_items(iframe_name)
55
- add_iframe_helper_methods(iframe_name, *element_find_args)
56
- define_method(iframe_name) do |&block|
57
- within_frame(*scope_find_args) do
58
- block.call iframe_page_class.new
67
+ def section(section_name, *args, &block)
68
+ section_class, find_args = extract_section_options(args, &block)
69
+ build(section_name, *find_args) do
70
+ define_method section_name do |*runtime_args, &runtime_block|
71
+ section_class.new self, find_first(*merge_args(find_args, runtime_args)), &runtime_block
72
+ end
59
73
  end
60
74
  end
61
- end
62
75
 
63
- def add_to_mapped_items(item)
64
- @mapped_items ||= []
65
- @mapped_items << item
66
- end
76
+ def sections(section_collection_name, *args, &block)
77
+ section_class, find_args = extract_section_options(args, &block)
78
+ build(section_collection_name, *find_args) do
79
+ define_method(section_collection_name) do |*runtime_args, &element_block|
80
+ raise_if_block(self, section_collection_name.to_s, !element_block.nil?)
81
+ find_all(*merge_args(find_args, runtime_args)).map do |element|
82
+ section_class.new(self, element)
83
+ end
84
+ end
85
+ end
86
+ end
67
87
 
68
- def raise_if_block(obj, name, has_block)
69
- return unless has_block
88
+ def iframe(iframe_name, iframe_page_class, *args)
89
+ element_find_args = deduce_iframe_element_find_args(args)
90
+ scope_find_args = deduce_iframe_scope_find_args(args)
91
+ add_to_mapped_items(iframe_name)
92
+ add_iframe_helper_methods(iframe_name, *element_find_args)
93
+ define_method(iframe_name) do |&block|
94
+ within_frame(*scope_find_args) do
95
+ block.call iframe_page_class.new
96
+ end
97
+ end
98
+ end
70
99
 
71
- raise SitePrism::UnsupportedBlock, "#{obj.class}##{name}"
72
- end
100
+ def add_to_mapped_items(item)
101
+ @mapped_items ||= []
102
+ @mapped_items << item
103
+ end
73
104
 
74
- private
105
+ private
75
106
 
76
- def build(name, *find_args)
77
- if find_args.empty?
78
- create_no_selector(name)
79
- else
80
- add_to_mapped_items(name)
81
- yield
107
+ def build(name, *find_args)
108
+ if find_args.empty?
109
+ create_no_selector(name)
110
+ else
111
+ add_to_mapped_items(name)
112
+ yield
113
+ end
114
+ add_helper_methods(name, *find_args)
82
115
  end
83
- add_helper_methods(name, *find_args)
84
- end
85
116
 
86
- def add_helper_methods(name, *find_args)
87
- create_existence_checker(name, *find_args)
88
- create_nonexistence_checker(name, *find_args)
89
- create_waiter(name, *find_args)
90
- create_nonexistence_waiter(name, *find_args)
91
- create_visibility_waiter(name, *find_args)
92
- create_invisibility_waiter(name, *find_args)
93
- end
117
+ def add_helper_methods(name, *find_args)
118
+ create_existence_checker(name, *find_args)
119
+ create_nonexistence_checker(name, *find_args)
120
+ create_waiter(name, *find_args)
121
+ create_nonexistence_waiter(name, *find_args)
122
+ create_visibility_waiter(name, *find_args)
123
+ create_invisibility_waiter(name, *find_args)
124
+ end
94
125
 
95
- def add_iframe_helper_methods(name, *find_args)
96
- create_existence_checker(name, *find_args)
97
- create_nonexistence_checker(name, *find_args)
98
- create_waiter(name, *find_args)
99
- create_nonexistence_waiter(name, *find_args)
100
- end
126
+ def add_iframe_helper_methods(name, *find_args)
127
+ create_existence_checker(name, *find_args)
128
+ create_nonexistence_checker(name, *find_args)
129
+ create_waiter(name, *find_args)
130
+ create_nonexistence_waiter(name, *find_args)
131
+ end
101
132
 
102
- def create_helper_method(proposed_method_name, *find_args)
103
- if find_args.empty?
104
- create_no_selector(proposed_method_name)
105
- else
106
- yield
133
+ def create_helper_method(proposed_method_name, *find_args)
134
+ if find_args.empty?
135
+ create_no_selector(proposed_method_name)
136
+ else
137
+ yield
138
+ end
107
139
  end
108
- end
109
140
 
110
- def create_existence_checker(element_name, *find_args)
111
- method_name = "has_#{element_name}?"
112
- create_helper_method(method_name, *find_args) do
113
- define_method(method_name) do |*runtime_args|
114
- wait_time = SitePrism.use_implicit_waits ? Capybara.default_max_wait_time : 0
115
- Capybara.using_wait_time(wait_time) do
116
- element_exists?(*find_args, *runtime_args)
141
+ def create_existence_checker(element_name, *find_args)
142
+ method_name = "has_#{element_name}?"
143
+ create_helper_method(method_name, *find_args) do
144
+ define_method(method_name) do |*runtime_args|
145
+ wait_time = SitePrism.use_implicit_waits ? Capybara.default_max_wait_time : 0
146
+ Capybara.using_wait_time(wait_time) do
147
+ element_exists?(*merge_args(find_args, runtime_args))
148
+ end
117
149
  end
118
150
  end
119
151
  end
120
- end
121
152
 
122
- def create_nonexistence_checker(element_name, *find_args)
123
- method_name = "has_no_#{element_name}?"
124
- create_helper_method(method_name, *find_args) do
125
- define_method(method_name) do |*runtime_args|
126
- wait_time = SitePrism.use_implicit_waits ? Capybara.default_max_wait_time : 0
127
- Capybara.using_wait_time(wait_time) do
128
- element_does_not_exist?(*find_args, *runtime_args)
153
+ def create_nonexistence_checker(element_name, *find_args)
154
+ method_name = "has_no_#{element_name}?"
155
+ create_helper_method(method_name, *find_args) do
156
+ define_method(method_name) do |*runtime_args|
157
+ wait_time = SitePrism.use_implicit_waits ? Capybara.default_max_wait_time : 0
158
+ Capybara.using_wait_time(wait_time) do
159
+ element_does_not_exist?(*merge_args(find_args, runtime_args))
160
+ end
129
161
  end
130
162
  end
131
163
  end
132
- end
133
164
 
134
- def create_waiter(element_name, *find_args)
135
- method_name = "wait_for_#{element_name}"
136
- create_helper_method(method_name, *find_args) do
137
- define_method(method_name) do |timeout = nil, *runtime_args|
138
- timeout = timeout.nil? ? Capybara.default_max_wait_time : timeout
139
- Capybara.using_wait_time(timeout) do
140
- element_exists?(*find_args, *runtime_args)
165
+ def create_waiter(element_name, *find_args)
166
+ method_name = "wait_for_#{element_name}"
167
+ create_helper_method(method_name, *find_args) do
168
+ define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
169
+ result = Capybara.using_wait_time(timeout) do
170
+ element_exists?(*merge_args(find_args, runtime_args))
171
+ end
172
+ raise_wait_for_if_failed(self, element_name.to_s, timeout, !result)
173
+ result
141
174
  end
142
175
  end
143
176
  end
144
- end
145
177
 
146
- def create_nonexistence_waiter(element_name, *find_args)
147
- method_name = "wait_for_no_#{element_name}"
148
- create_helper_method(method_name, *find_args) do
149
- define_method(method_name) do |timeout = nil, *runtime_args|
150
- timeout = timeout.nil? ? Waiter.default_wait_time : timeout
151
- Capybara.using_wait_time(timeout) do
152
- element_does_not_exist?(*find_args, *runtime_args)
178
+ def create_nonexistence_waiter(element_name, *find_args)
179
+ method_name = "wait_for_no_#{element_name}"
180
+ create_helper_method(method_name, *find_args) do
181
+ define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
182
+ result = Capybara.using_wait_time(timeout) do
183
+ element_does_not_exist?(*merge_args(find_args, runtime_args))
184
+ end
185
+ raise_wait_for_no_if_failed(self, element_name.to_s, timeout, !result)
186
+ result
153
187
  end
154
188
  end
155
189
  end
156
- end
157
190
 
158
- def create_visibility_waiter(element_name, *find_args)
159
- method_name = "wait_until_#{element_name}_visible"
160
- create_helper_method(method_name, *find_args) do
161
- define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
162
- Timeout.timeout(timeout, SitePrism::TimeOutWaitingForElementVisibility) do
163
- Capybara.using_wait_time 0 do
164
- sleep 0.05 until element_exists?(*find_args, *runtime_args, visible: true)
191
+ def create_visibility_waiter(element_name, *find_args)
192
+ method_name = "wait_until_#{element_name}_visible"
193
+ create_helper_method(method_name, *find_args) do
194
+ define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
195
+ unless element_exists?(*merge_args(find_args, runtime_args, visible: true, wait: timeout))
196
+ raise SitePrism::TimeOutWaitingForElementVisibility
165
197
  end
166
198
  end
167
199
  end
168
200
  end
169
- end
170
201
 
171
- def create_invisibility_waiter(element_name, *find_args)
172
- method_name = "wait_until_#{element_name}_invisible"
173
- create_helper_method(method_name, *find_args) do
174
- define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
175
- Timeout.timeout(timeout, SitePrism::TimeOutWaitingForElementInvisibility) do
176
- Capybara.using_wait_time 0 do
177
- sleep 0.05 while element_exists?(*find_args, *runtime_args, visible: true)
202
+ def create_invisibility_waiter(element_name, *find_args)
203
+ method_name = "wait_until_#{element_name}_invisible"
204
+ create_helper_method(method_name, *find_args) do
205
+ define_method(method_name) do |timeout = Capybara.default_max_wait_time, *runtime_args|
206
+ unless element_does_not_exist?(*merge_args(find_args, runtime_args, visible: true, wait: timeout))
207
+ raise SitePrism::TimeOutWaitingForElementInvisibility
178
208
  end
179
209
  end
180
210
  end
181
211
  end
182
- end
183
212
 
184
- def create_no_selector(method_name)
185
- define_method(method_name) do
186
- raise SitePrism::NoSelectorForElement.new, "#{self.class.name} => :#{method_name} needs a selector"
213
+ def create_no_selector(method_name)
214
+ define_method(method_name) do
215
+ raise SitePrism::NoSelectorForElement.new, "#{self.class.name} => :#{method_name} needs a selector"
216
+ end
187
217
  end
188
- end
189
218
 
190
- def deduce_iframe_scope_find_args(args)
191
- case args[0]
192
- when Integer
193
- [args[0]]
194
- when String
195
- [:css, args[0]]
196
- else
197
- args
219
+ def deduce_iframe_scope_find_args(args)
220
+ case args[0]
221
+ when Integer
222
+ [args[0]]
223
+ when String
224
+ [:css, args[0]]
225
+ else
226
+ args
227
+ end
198
228
  end
199
- end
200
229
 
201
- def deduce_iframe_element_find_args(args)
202
- case args[0]
203
- when Integer
204
- "iframe:nth-of-type(#{args[0] + 1})"
205
- when String
206
- [:css, args[0]]
207
- else
208
- args
230
+ def deduce_iframe_element_find_args(args)
231
+ case args[0]
232
+ when Integer
233
+ "iframe:nth-of-type(#{args[0] + 1})"
234
+ when String
235
+ [:css, args[0]]
236
+ else
237
+ args
238
+ end
239
+ end
240
+
241
+ def extract_section_options(args, &block)
242
+ if args.first.is_a?(Class)
243
+ klass = args.shift
244
+ section_class = klass if klass.ancestors.include?(SitePrism::Section)
245
+ end
246
+
247
+ section_class = deduce_section_class(section_class, &block)
248
+ arguments = deduce_search_arguments(section_class, args)
249
+ [section_class, arguments]
250
+ end
251
+
252
+ def deduce_section_class(base_class, &block)
253
+ klass = base_class
254
+
255
+ klass = Class.new(klass || SitePrism::Section, &block) if block_given?
256
+
257
+ unless klass
258
+ raise ArgumentError, "You should provide descendant of SitePrism::Section \
259
+ class or/and a block as the second argument."
260
+ end
261
+ klass
262
+ end
263
+
264
+ def deduce_search_arguments(section_class, args)
265
+ extract_search_arguments(args) ||
266
+ extract_search_arguments(section_class.default_search_arguments) ||
267
+ raise(ArgumentError, "You should provide search arguments \
268
+ in section creation or set_default_search_arguments within section class")
209
269
  end
210
- end
211
270
 
212
- def extract_section_options(args, &block)
213
- if args.first.is_a?(Class)
214
- section_class = args.shift
215
- elsif block_given?
216
- section_class = Class.new(SitePrism::Section, &block)
217
- else
218
- raise ArgumentError, 'You should provide section class either as a block, or as the second argument.'
271
+ def extract_search_arguments(args)
272
+ args if args && !args.empty?
219
273
  end
220
- [section_class, args]
221
274
  end
222
275
  end
223
276
  end
@@ -6,7 +6,8 @@ module SitePrism
6
6
 
7
7
  class InvalidUrlMatcher < StandardError
8
8
  def message
9
- 'Could not automatically match your URL. Templated port numbers are unsupported.'
9
+ "Could not automatically match your URL. \
10
+ Templated port numbers are unsupported."
10
11
  end
11
12
  end
12
13
 
@@ -18,6 +19,8 @@ module SitePrism
18
19
  end
19
20
  end
20
21
 
22
+ class TimeOutWaitingForExistenceError < StandardError; end
23
+ class TimeOutWaitingForNonExistenceError < StandardError; end
21
24
  class TimeOutWaitingForElementVisibility < StandardError; end
22
25
  class TimeOutWaitingForElementInvisibility < StandardError; end
23
26
 
@@ -3,7 +3,8 @@
3
3
  module SitePrism
4
4
  module Loadable
5
5
  module ClassMethods
6
- # The list of load_validations. They will be executed in the order they are defined.
6
+ # The list of load_validations.
7
+ # They will be executed in the order they are defined.
7
8
  #
8
9
  # @return [Array]
9
10
  def load_validations
@@ -16,44 +17,53 @@ module SitePrism
16
17
 
17
18
  # Appends a load validation block to the page class.
18
19
  #
19
- # When `loaded?` is called, these blocks are instance_eval'd against the current page
20
- # instance. This allows users to wait for specific events to occur on the page or certain elements
21
- # to be loaded before performing any actions on the page.
20
+ # When `loaded?` is called, these blocks are instance_eval'd
21
+ # against the current page instance.
22
+ # This allows users to wait for specific events to occur on
23
+ # the page or certain elements to be loaded before performing
24
+ # any actions on the page.
22
25
  #
23
- # @param block [&block] A block which returns true if the page loaded successfully, or false if it did not.
26
+ # @param block [&block] A block which returns true if the page
27
+ # loaded successfully, or false if it did not.
24
28
  def load_validation(&block)
25
29
  _load_validations << block
26
30
  end
27
31
 
32
+ private
33
+
28
34
  def _load_validations
29
35
  @_load_validations ||= []
30
36
  end
31
- private :_load_validations
32
37
  end
33
38
 
34
39
  def self.included(base)
35
40
  base.extend(ClassMethods)
36
41
  end
37
42
 
38
- # In certain circumstances, we cache that the page has already been confirmed to be loaded so that actions which
39
- # call `loaded?` a second time do not need to perform the load_validation queries against the page a second time.
43
+ # In certain circumstances, we cache that the page has already
44
+ # been confirmed to be loaded so that actions which
45
+ # call `loaded?` a second time do not need to perform
46
+ # the load_validation queries against the page a second time.
40
47
  attr_accessor :loaded, :load_error
41
48
 
42
49
  # Executes the given block after the page is loaded.
43
50
  #
44
51
  # The loadable object instance is yielded into the block.
45
52
  #
46
- # @param block [&block] The block to be executed once the page has finished loading.
53
+ # @param block [&block] The block to be executed once the page
54
+ # has finished loading.
47
55
  def when_loaded(&_block)
48
- previously_loaded = loaded # Get original loaded value, in case we are nested inside another when_loaded block.
49
- raise(ArgumentError, 'A block was expected, but none received.') unless block_given?
56
+ # Get original loaded value, in case we are nested
57
+ # inside another when_loaded block.
58
+ previously_loaded = loaded
59
+ message = 'A block was expected, but none received.'
60
+ raise ArgumentError, message unless block_given?
50
61
 
51
62
  # Within the block, cache loaded? to optimize performance.
52
63
  self.loaded = loaded?
53
- unless loaded
54
- message = "Failed to load because: #{load_error || 'no reason given'}"
55
- raise(::SitePrism::NotLoadedError, message)
56
- end
64
+
65
+ message = "Failed to load because: #{load_error || 'no reason given'}"
66
+ raise ::SitePrism::NotLoadedError, message unless loaded
57
67
 
58
68
  yield self
59
69
  ensure
@@ -62,7 +72,8 @@ module SitePrism
62
72
 
63
73
  # Check if the page is loaded.
64
74
  #
65
- # On failure, if an error was reported by a failing validation, it will be available via the `load_error` accessor.
75
+ # On failure, if an error was reported by a failing validation,
76
+ # it will be available via the `load_error` accessor.
66
77
  #
67
78
  # @return [Boolean] True if the page loaded successfully; otherwise false.
68
79
  def loaded?
@@ -73,7 +84,10 @@ module SitePrism
73
84
  load_validations_pass?
74
85
  end
75
86
 
76
- # If any load validations from page subclasses returns false, immediately return false.
87
+ private
88
+
89
+ # If any load validations from page subclasses returns false,
90
+ # immediately return false.
77
91
  def load_validations_pass?
78
92
  self.class.load_validations.all? do |validation|
79
93
  passed, message = instance_eval(&validation)
@@ -82,6 +96,5 @@ module SitePrism
82
96
  passed
83
97
  end
84
98
  end
85
- private :load_validations_pass?
86
99
  end
87
100
  end
@@ -3,14 +3,18 @@
3
3
  require 'site_prism/loadable'
4
4
 
5
5
  module SitePrism
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class Page
7
8
  include Capybara::DSL
8
9
  include ElementChecker
9
10
  include Loadable
10
- extend ElementContainer
11
+ include ElementContainer
11
12
 
12
13
  load_validation do
13
- [displayed?, "Expected #{current_url} to match #{url_matcher} but it did not."]
14
+ [
15
+ displayed?,
16
+ "Expected #{current_url} to match #{url_matcher} but it did not."
17
+ ]
14
18
  end
15
19
 
16
20
  def page
@@ -40,7 +44,8 @@ module SitePrism
40
44
 
41
45
  def displayed?(*args)
42
46
  expected_mappings = args.last.is_a?(::Hash) ? args.pop : {}
43
- seconds = !args.empty? ? args.first : Waiter.default_wait_time
47
+ seconds = !args.empty? ? args.first : Capybara.default_max_wait_time
48
+
44
49
  raise SitePrism::NoUrlMatcherForPage if url_matcher.nil?
45
50
  begin
46
51
  Waiter.wait_until_true(seconds) { url_matches?(expected_mappings) }
@@ -49,7 +54,7 @@ module SitePrism
49
54
  end
50
55
  end
51
56
 
52
- def url_matches(seconds = Waiter.default_wait_time)
57
+ def url_matches(seconds = Capybara.default_max_wait_time)
53
58
  return unless displayed?(seconds)
54
59
 
55
60
  if url_matcher.is_a?(Regexp)
@@ -93,7 +98,7 @@ module SitePrism
93
98
  end
94
99
 
95
100
  def secure?
96
- page.current_url.start_with? 'https'
101
+ page.current_url.start_with?('https')
97
102
  end
98
103
 
99
104
  private
@@ -133,7 +138,8 @@ module SitePrism
133
138
  end
134
139
 
135
140
  def matcher_template
136
- @addressable_url_matcher ||= AddressableUrlMatcher.new(url_matcher)
141
+ @matcher_template ||= AddressableUrlMatcher.new(url_matcher)
137
142
  end
138
143
  end
144
+ # rubocop:enable Metrics/ClassLength
139
145
  end
@@ -7,10 +7,23 @@ module SitePrism
7
7
  include Capybara::DSL
8
8
  include ElementChecker
9
9
  include Loadable
10
- extend ElementContainer
10
+ include ElementContainer
11
11
 
12
12
  attr_reader :root_element, :parent
13
13
 
14
+ def self.set_default_search_arguments(*args)
15
+ @default_search_arguments = args
16
+ end
17
+
18
+ def self.default_search_arguments
19
+ @default_search_arguments ||
20
+ (
21
+ superclass.respond_to?(:default_search_arguments) &&
22
+ superclass.default_search_arguments
23
+ ) ||
24
+ nil
25
+ end
26
+
14
27
  def initialize(parent, root_element)
15
28
  @parent = parent
16
29
  @root_element = root_element
@@ -18,8 +31,11 @@ module SitePrism
18
31
  end
19
32
 
20
33
  # Capybara::DSL module "delegates" Capybara methods to the "page" method
34
+ # as such we need to overload this method so that the correct scoping
35
+ # occurs and calls within a section (For example section.find(element))
36
+ # correctly scope to look within the section only
21
37
  def page
22
- root_element || Capybara.current_session
38
+ root_element || super
23
39
  end
24
40
 
25
41
  def visible?
@@ -36,9 +52,7 @@ module SitePrism
36
52
 
37
53
  def parent_page
38
54
  candidate_page = parent
39
- until candidate_page.is_a?(SitePrism::Page)
40
- candidate_page = candidate_page.parent
41
- end
55
+ candidate_page = candidate_page.parent until candidate_page.is_a?(SitePrism::Page)
42
56
  candidate_page
43
57
  end
44
58
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SitePrism
4
- VERSION = '2.13'.freeze
4
+ VERSION = '2.14'.freeze
5
5
  end
@@ -13,10 +13,5 @@ module SitePrism
13
13
 
14
14
  raise SitePrism::TimeoutException, wait_time
15
15
  end
16
-
17
- def self.default_wait_time
18
- warn 'default_wait_time is now deprecated. This will be removed in an upcoming release.'
19
- Capybara.default_max_wait_time
20
- end
21
16
  end
22
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: site_prism
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.13'
4
+ version: '2.14'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nat Ritmeyer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-21 00:00:00.000000000 Z
12
+ date: 2018-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: addressable
@@ -54,95 +54,89 @@ dependencies:
54
54
  - !ruby/object:Gem::Version
55
55
  version: 3.0.1
56
56
  - !ruby/object:Gem::Dependency
57
- name: rake
57
+ name: dotenv
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '12.0'
62
+ version: '2.2'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '12.0'
69
+ version: '2.2'
70
70
  - !ruby/object:Gem::Dependency
71
- name: rspec
71
+ name: rake
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '3.5'
76
+ version: '12.0'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '3.5'
83
+ version: '12.0'
84
84
  - !ruby/object:Gem::Dependency
85
- name: rubocop
85
+ name: rspec
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - '='
88
+ - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: 0.50.0
90
+ version: '3.5'
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - '='
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: 0.50.0
97
+ version: '3.5'
98
98
  - !ruby/object:Gem::Dependency
99
- name: selenium-webdriver
99
+ name: rubocop
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
103
- - !ruby/object:Gem::Version
104
- version: 3.4.0
105
- - - "<="
102
+ - - "~>"
106
103
  - !ruby/object:Gem::Version
107
- version: 3.10.0
104
+ version: '0.50'
108
105
  type: :development
109
106
  prerelease: false
110
107
  version_requirements: !ruby/object:Gem::Requirement
111
108
  requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- version: 3.4.0
115
- - - "<="
109
+ - - "~>"
116
110
  - !ruby/object:Gem::Version
117
- version: 3.10.0
111
+ version: '0.50'
118
112
  - !ruby/object:Gem::Dependency
119
- name: simplecov
113
+ name: selenium-webdriver
120
114
  requirement: !ruby/object:Gem::Requirement
121
115
  requirements:
122
- - - ">="
116
+ - - "~>"
123
117
  - !ruby/object:Gem::Version
124
- version: '0.12'
118
+ version: '3.4'
125
119
  type: :development
126
120
  prerelease: false
127
121
  version_requirements: !ruby/object:Gem::Requirement
128
122
  requirements:
129
- - - ">="
123
+ - - "~>"
130
124
  - !ruby/object:Gem::Version
131
- version: '0.12'
125
+ version: '3.4'
132
126
  - !ruby/object:Gem::Dependency
133
- name: dotenv
127
+ name: simplecov
134
128
  requirement: !ruby/object:Gem::Requirement
135
129
  requirements:
136
130
  - - "~>"
137
131
  - !ruby/object:Gem::Version
138
- version: '2.2'
132
+ version: '0.12'
139
133
  type: :development
140
134
  prerelease: false
141
135
  version_requirements: !ruby/object:Gem::Requirement
142
136
  requirements:
143
137
  - - "~>"
144
138
  - !ruby/object:Gem::Version
145
- version: '2.2'
139
+ version: '0.12'
146
140
  description: |-
147
141
  SitePrism gives you a simple, clean and semantic DSL for describing your site.
148
142
  SitePrism implements the Page Object Model pattern on top of Capybara.