site_prism 4.0.3 → 5.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/README.md +60 -54
 - data/lib/site_prism/deprecator.rb +2 -21
 - data/lib/site_prism/dsl/builder.rb +144 -0
 - data/lib/site_prism/dsl/locators.rb +85 -0
 - data/lib/site_prism/dsl/methods.rb +135 -0
 - data/lib/site_prism/dsl/validator.rb +95 -0
 - data/lib/site_prism/dsl.rb +14 -327
 - data/lib/site_prism/element_checker.rb +2 -2
 - data/lib/site_prism/error.rb +4 -1
 - data/lib/site_prism/loadable.rb +13 -23
 - data/lib/site_prism/logger.rb +1 -1
 - data/lib/site_prism/page.rb +4 -15
 - data/lib/site_prism/version.rb +1 -1
 - data/lib/site_prism.rb +7 -3
 - metadata +51 -36
 - data/lib/site_prism/dsl_validator.rb +0 -75
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 3036f6b2266855a0a4d08a3c72b44b08d1ce6f423ca72e5dbe5b539cb90b1216
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 1314d67df7aeb3c9d70478e5b884aec2cfb7b4216c58cf9841d4d6dc8b7bc9f5
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 057576ee2b44eb40824344eeef80243e4fbbe8b30df7ab8e6b011053f652b0257065472103b16ed6a4ce094ed3ad608cf8c72dcb8e11c0e3e4f3daa2aaa9ef38
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c9ef4c0ac315e0664f0089cd13a75e8566e7be3f553e85a7f2d17ef1bce7943762074b478767790bb44497968fc6ee0919440a1e613ed6a27f0efde585e19f24
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -29,8 +29,8 @@ We have a brief set of setup docs [HERE](https://github.com/site-prism/site_pris 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
            ## Supported Rubies / Browsers
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            SitePrism is built and tested to work on Ruby 2. 
     | 
| 
       33 
     | 
    
         
            -
            If you are using SitePrism with Ruby 2. 
     | 
| 
      
 32 
     | 
    
         
            +
            SitePrism is built and tested to work on Ruby 2.7 - 3.2.
         
     | 
| 
      
 33 
     | 
    
         
            +
            If you are using SitePrism with Ruby 2.7 it is highly advisable to upgrade to a more modern
         
     | 
| 
       34 
34 
     | 
    
         
             
            Ruby (v3+), if for any other reason, to get a performance improvement!
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
            SitePrism should run on all major browsers. The gem's integration tests are run on Chrome and Firefox.
         
     | 
| 
         @@ -66,7 +66,7 @@ class SearchResults < SitePrism::Page 
     | 
|
| 
       66 
66 
     | 
    
         
             
              end
         
     | 
| 
       67 
67 
     | 
    
         
             
            end
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
            #  
     | 
| 
      
 69 
     | 
    
         
            +
            # Define sections that are used on multiple pages or multiple times on one page
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
            class Menu < SitePrism::Section
         
     | 
| 
       72 
72 
     | 
    
         
             
              element :search, 'a.search'
         
     | 
| 
         @@ -79,7 +79,7 @@ class SearchResults < SitePrism::Section 
     | 
|
| 
       79 
79 
     | 
    
         
             
              element :blurb, 'span.result-description'
         
     | 
| 
       80 
80 
     | 
    
         
             
            end
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
            #  
     | 
| 
      
 82 
     | 
    
         
            +
            # Then we can write some tests
         
     | 
| 
       83 
83 
     | 
    
         | 
| 
       84 
84 
     | 
    
         
             
            When('I navigate to the google home page') do
         
     | 
| 
       85 
85 
     | 
    
         
             
              @home = Home.new
         
     | 
| 
         @@ -88,6 +88,7 @@ end 
     | 
|
| 
       88 
88 
     | 
    
         | 
| 
       89 
89 
     | 
    
         
             
            Then('the home page should contain the menu and the search form') do
         
     | 
| 
       90 
90 
     | 
    
         
             
              @home.wait_until_menu_visible(wait: 5)
         
     | 
| 
      
 91 
     | 
    
         
            +
              
         
     | 
| 
       91 
92 
     | 
    
         
             
              expect(@home).to have_menu
         
     | 
| 
       92 
93 
     | 
    
         
             
              expect(@home).to have_search_field
         
     | 
| 
       93 
94 
     | 
    
         
             
              expect(@home).to have_search_button
         
     | 
| 
         @@ -100,11 +101,13 @@ end 
     | 
|
| 
       100 
101 
     | 
    
         | 
| 
       101 
102 
     | 
    
         
             
            Then('the search results page is displayed') do
         
     | 
| 
       102 
103 
     | 
    
         
             
              @results_page = SearchResults.new
         
     | 
| 
      
 104 
     | 
    
         
            +
              
         
     | 
| 
       103 
105 
     | 
    
         
             
              expect(@results_page).to be_displayed
         
     | 
| 
       104 
106 
     | 
    
         
             
            end
         
     | 
| 
       105 
107 
     | 
    
         | 
| 
       106 
108 
     | 
    
         
             
            Then('the search results page contains 10 individual search results') do
         
     | 
| 
       107 
109 
     | 
    
         
             
              @results_page.wait_until_search_results_visible(wait: 5)
         
     | 
| 
      
 110 
     | 
    
         
            +
              
         
     | 
| 
       108 
111 
     | 
    
         
             
              expect(@results_page).to have_search_results(count: 10)
         
     | 
| 
       109 
112 
     | 
    
         
             
            end
         
     | 
| 
       110 
113 
     | 
    
         | 
| 
         @@ -113,8 +116,6 @@ Then('the search results contain a link to the wikipedia sausages page') do 
     | 
|
| 
       113 
116 
     | 
    
         
             
            end
         
     | 
| 
       114 
117 
     | 
    
         
             
            ```
         
     | 
| 
       115 
118 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
            Now for the details...
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
119 
     | 
    
         
             
            ## Setup
         
     | 
| 
       119 
120 
     | 
    
         | 
| 
       120 
121 
     | 
    
         
             
            ### Installation
         
     | 
| 
         @@ -166,19 +167,15 @@ And again, as above, a sample driver is no different to a normal driver instanti 
     | 
|
| 
       166 
167 
     | 
    
         | 
| 
       167 
168 
     | 
    
         
             
            ## Introduction to the Page Object Model
         
     | 
| 
       168 
169 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
            The Page Object Model is a test automation pattern that aims to create
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
            to then use instances of those classes in your tests.
         
     | 
| 
      
 170 
     | 
    
         
            +
            The Page Object Model is a test automation pattern that aims to create an abstraction of your sites user
         
     | 
| 
      
 171 
     | 
    
         
            +
            interface that can be used in tests. The most common way to do this is to model each page as a class and
         
     | 
| 
      
 172 
     | 
    
         
            +
            then to use instances of those classes in your tests.
         
     | 
| 
       173 
173 
     | 
    
         | 
| 
       174 
     | 
    
         
            -
            If a class represents a page then each element of the page is
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
            element that can then be acted upon (clicked, type in some text), or
         
     | 
| 
       177 
     | 
    
         
            -
            queried (is it enabled? / visible?).
         
     | 
| 
      
 174 
     | 
    
         
            +
            If a class represents a page then each element of the page is represented by a method that, when called, returns a
         
     | 
| 
      
 175 
     | 
    
         
            +
            reference to that element that can then be acted upon (clicked, type in some text), or queried (is it enabled? / visible?).
         
     | 
| 
       178 
176 
     | 
    
         | 
| 
       179 
     | 
    
         
            -
            SitePrism is based around this concept, but goes further as you'll see
         
     | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
       181 
     | 
    
         
            -
            multiple pages, or many times on a page using the concept of sections.
         
     | 
| 
      
 177 
     | 
    
         
            +
            SitePrism is based around this concept, but goes further as you'll see below by also allowing modelling of
         
     | 
| 
      
 178 
     | 
    
         
            +
            repeated sections that appear on multiple pages, or many times on a page using the concept of sections.
         
     | 
| 
       182 
179 
     | 
    
         | 
| 
       183 
180 
     | 
    
         
             
            ## Pages
         
     | 
| 
       184 
181 
     | 
    
         | 
| 
         @@ -216,9 +213,8 @@ class Home < SitePrism::Page 
     | 
|
| 
       216 
213 
     | 
    
         
             
            end
         
     | 
| 
       217 
214 
     | 
    
         
             
            ```
         
     | 
| 
       218 
215 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
            Note that setting a URL is **optional** - you only need to set a url if you want to be able to
         
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
            home page or a login page, but probably not a search results page.
         
     | 
| 
      
 216 
     | 
    
         
            +
            Note that setting a URL is **optional** - you only need to set a url if you want to be able to navigate directly to that page.
         
     | 
| 
      
 217 
     | 
    
         
            +
            It makes sense to set the URL for a page model of a home page or a login page, but probably not a search results page.
         
     | 
| 
       222 
218 
     | 
    
         | 
| 
       223 
219 
     | 
    
         
             
            #### Parametrized URLs
         
     | 
| 
       224 
220 
     | 
    
         | 
| 
         @@ -281,10 +277,9 @@ navigate to the URL set against that page's class. 
     | 
|
| 
       281 
277 
     | 
    
         | 
| 
       282 
278 
     | 
    
         
             
            ### Verifying that a particular page is displayed
         
     | 
| 
       283 
279 
     | 
    
         | 
| 
       284 
     | 
    
         
            -
            Automated tests often need to verify that a particular page is
         
     | 
| 
       285 
     | 
    
         
            -
             
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
            currently viewed page. For example, with the following URL template:
         
     | 
| 
      
 280 
     | 
    
         
            +
            Automated tests often need to verify that a particular page is displayed. SitePrism can automatically parse
         
     | 
| 
      
 281 
     | 
    
         
            +
            your templated URL and verify that whatever components your template specifies match the currently viewed page.
         
     | 
| 
      
 282 
     | 
    
         
            +
            For example, with the following URL template:
         
     | 
| 
       288 
283 
     | 
    
         | 
| 
       289 
284 
     | 
    
         
             
            ```ruby
         
     | 
| 
       290 
285 
     | 
    
         
             
            class Account < SitePrism::Page
         
     | 
| 
         @@ -302,10 +297,9 @@ expect(@account_page.current_url).to end_with('/accounts/22?token=ca2786616a4285 
     | 
|
| 
       302 
297 
     | 
    
         
             
            expect(@account_page).to be_displayed
         
     | 
| 
       303 
298 
     | 
    
         
             
            ```
         
     | 
| 
       304 
299 
     | 
    
         | 
| 
       305 
     | 
    
         
            -
            Calling `#displayed?` will return true if the browser's current URL
         
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
     | 
    
         
            -
            wait time in seconds as the first argument like this:
         
     | 
| 
      
 300 
     | 
    
         
            +
            Calling `#displayed?` will return true if the browser's current URL matches the page's template and false if
         
     | 
| 
      
 301 
     | 
    
         
            +
            it doesn't. It will wait for `Capybara.default_max_wait_time` seconds or you can pass an explicit wait time in
         
     | 
| 
      
 302 
     | 
    
         
            +
            seconds as the first argument like this:
         
     | 
| 
       309 
303 
     | 
    
         | 
| 
       310 
304 
     | 
    
         
             
            ```ruby
         
     | 
| 
       311 
305 
     | 
    
         
             
            @account_page.displayed?(10) # wait up to 10 seconds for display
         
     | 
| 
         @@ -346,9 +340,8 @@ expect(@account_page.url_matches.dig('query', 'token')).to eq('ca2786616a4285bc' 
     | 
|
| 
       346 
340 
     | 
    
         | 
| 
       347 
341 
     | 
    
         
             
            #### Falling back to basic regexp matchers
         
     | 
| 
       348 
342 
     | 
    
         | 
| 
       349 
     | 
    
         
            -
            If SitePrism's built-in URL matching is not sufficient for your needs
         
     | 
| 
       350 
     | 
    
         
            -
             
     | 
| 
       351 
     | 
    
         
            -
            URL matchers by it by calling `set_url_matcher`:
         
     | 
| 
      
 343 
     | 
    
         
            +
            If SitePrism's built-in URL matching is not sufficient for your needs you can override and use SitePrism's
         
     | 
| 
      
 344 
     | 
    
         
            +
            support for regular expression-based URL matchers by it by calling `set_url_matcher`:
         
     | 
| 
       352 
345 
     | 
    
         | 
| 
       353 
346 
     | 
    
         
             
            ```ruby
         
     | 
| 
       354 
347 
     | 
    
         
             
            class Account < SitePrism::Page
         
     | 
| 
         @@ -377,6 +370,7 @@ end 
     | 
|
| 
       377 
370 
     | 
    
         | 
| 
       378 
371 
     | 
    
         
             
            @account = Account.new
         
     | 
| 
       379 
372 
     | 
    
         
             
            @account.current_url #=> "http://www.example.com/account/123"
         
     | 
| 
      
 373 
     | 
    
         
            +
             
     | 
| 
       380 
374 
     | 
    
         
             
            expect(@account.current_url).to include('example.com/account/')
         
     | 
| 
       381 
375 
     | 
    
         
             
            ```
         
     | 
| 
       382 
376 
     | 
    
         | 
| 
         @@ -394,10 +388,9 @@ end 
     | 
|
| 
       394 
388 
     | 
    
         | 
| 
       395 
389 
     | 
    
         
             
            ### HTTP vs. HTTPS
         
     | 
| 
       396 
390 
     | 
    
         | 
| 
       397 
     | 
    
         
            -
            You can easily tell if the page is secure or not by checking to see if
         
     | 
| 
       398 
     | 
    
         
            -
            the current  
     | 
| 
       399 
     | 
    
         
            -
             
     | 
| 
       400 
     | 
    
         
            -
            'https' and false if it doesn't. For example:
         
     | 
| 
      
 391 
     | 
    
         
            +
            You can easily tell if the page is secure or not by checking to see if the current URL begins with 'https' or not.
         
     | 
| 
      
 392 
     | 
    
         
            +
            SitePrism provides the `#secure?` method that will return true if the current url begins with 'https' and false if it doesn't.
         
     | 
| 
      
 393 
     | 
    
         
            +
            For example:
         
     | 
| 
       401 
394 
     | 
    
         | 
| 
       402 
395 
     | 
    
         
             
            ```ruby
         
     | 
| 
       403 
396 
     | 
    
         
             
            class Account < SitePrism::Page
         
     | 
| 
         @@ -405,16 +398,15 @@ end 
     | 
|
| 
       405 
398 
     | 
    
         | 
| 
       406 
399 
     | 
    
         
             
            @account = Account.new
         
     | 
| 
       407 
400 
     | 
    
         
             
            @account.secure? #=> true/false
         
     | 
| 
      
 401 
     | 
    
         
            +
             
     | 
| 
       408 
402 
     | 
    
         
             
            expect(@account).to be_secure
         
     | 
| 
       409 
403 
     | 
    
         
             
            ```
         
     | 
| 
       410 
404 
     | 
    
         | 
| 
       411 
405 
     | 
    
         
             
            ## Elements
         
     | 
| 
       412 
406 
     | 
    
         | 
| 
       413 
     | 
    
         
            -
            Pages are made up of elements (text fields, buttons, combo boxes, etc),
         
     | 
| 
       414 
     | 
    
         
            -
             
     | 
| 
       415 
     | 
    
         
            -
             
     | 
| 
       416 
     | 
    
         
            -
            element collections would be items in any sort of list, eg: menu items,
         
     | 
| 
       417 
     | 
    
         
            -
            images in a carousel, etc.
         
     | 
| 
      
 407 
     | 
    
         
            +
            Pages are made up of elements (text fields, buttons, combo boxes, etc), either individual elements or groups of
         
     | 
| 
      
 408 
     | 
    
         
            +
            them. Examples of individual elements would be a search field or a company logo image; examples of element collections would
         
     | 
| 
      
 409 
     | 
    
         
            +
            be items in any sort of list, eg: menu items,images in a carousel, etc.
         
     | 
| 
       418 
410 
     | 
    
         | 
| 
       419 
411 
     | 
    
         
             
            ### Individual Elements
         
     | 
| 
       420 
412 
     | 
    
         | 
| 
         @@ -508,18 +500,16 @@ Using the above example: 
     | 
|
| 
       508 
500 
     | 
    
         | 
| 
       509 
501 
     | 
    
         
             
            ```ruby
         
     | 
| 
       510 
502 
     | 
    
         
             
            Then('the search field exists')do
         
     | 
| 
       511 
     | 
    
         
            -
              expect(@home).to have_no_search_field #NB: NOT => expect(@home).not_to have_search_field
         
     | 
| 
      
 503 
     | 
    
         
            +
              expect(@home).to have_no_search_field #NB: NOT THE SAME AS => expect(@home).not_to have_search_field
         
     | 
| 
       512 
504 
     | 
    
         
             
            end
         
     | 
| 
       513 
505 
     | 
    
         
             
            ```
         
     | 
| 
       514 
506 
     | 
    
         | 
| 
       515 
507 
     | 
    
         
             
            #### Waiting for an element to become visible
         
     | 
| 
       516 
508 
     | 
    
         | 
| 
       517 
     | 
    
         
            -
            A method that gets added by calling `element` is the
         
     | 
| 
       518 
     | 
    
         
            -
            `wait_until_<element_name>_visible` method.
         
     | 
| 
      
 509 
     | 
    
         
            +
            A method that gets added by calling `element` is the `wait_until_<element_name>_visible` method.
         
     | 
| 
       519 
510 
     | 
    
         
             
            This method delegates to [Capybara::Node::Matchers#has_selector?](https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Matchers#has_selector%3F-instance_method).
         
     | 
| 
       520 
     | 
    
         
            -
            Calling this method will cause the test to wait for Capybara's default wait time for the element
         
     | 
| 
       521 
     | 
    
         
            -
             
     | 
| 
       522 
     | 
    
         
            -
            of seconds to wait in-line or configuring the default wait time.
         
     | 
| 
      
 511 
     | 
    
         
            +
            Calling this method will cause the test to wait for Capybara's default wait time for the element to become visible. You can customise
         
     | 
| 
      
 512 
     | 
    
         
            +
            the wait time by supplying a number of seconds to wait in-line or configuring the default wait time.
         
     | 
| 
       523 
513 
     | 
    
         | 
| 
       524 
514 
     | 
    
         
             
            ```ruby
         
     | 
| 
       525 
515 
     | 
    
         
             
            @home.wait_until_search_field_visible # using the default wait time set
         
     | 
| 
         @@ -529,12 +519,10 @@ of seconds to wait in-line or configuring the default wait time. 
     | 
|
| 
       529 
519 
     | 
    
         | 
| 
       530 
520 
     | 
    
         
             
            #### Waiting for an element to become invisible
         
     | 
| 
       531 
521 
     | 
    
         | 
| 
       532 
     | 
    
         
            -
            Another method added by calling `element` is the
         
     | 
| 
       533 
     | 
    
         
            -
            `wait_until_<element_name>_invisible` method.
         
     | 
| 
      
 522 
     | 
    
         
            +
            Another method added by calling `element` is the `wait_until_<element_name>_invisible` method.
         
     | 
| 
       534 
523 
     | 
    
         
             
            This method delegates to [Capybara::Node::Matchers#has_no_selector?](https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Matchers#has_no_selector%3F-instance_method).
         
     | 
| 
       535 
     | 
    
         
            -
            Calling this method will cause the test to wait for Capybara's default
         
     | 
| 
       536 
     | 
    
         
            -
             
     | 
| 
       537 
     | 
    
         
            -
            waiter, customise the wait time in the same way.
         
     | 
| 
      
 524 
     | 
    
         
            +
            Calling this method will cause the test to wait for Capybara's default wait time for the element to become invisible.
         
     | 
| 
      
 525 
     | 
    
         
            +
            You can (as with the visibility waiter), customise the wait time in the same way.
         
     | 
| 
       538 
526 
     | 
    
         | 
| 
       539 
527 
     | 
    
         
             
            ```ruby
         
     | 
| 
       540 
528 
     | 
    
         
             
            @home.wait_until_search_field_invisible # using the default wait time set
         
     | 
| 
         @@ -544,9 +532,8 @@ waiter, customise the wait time in the same way. 
     | 
|
| 
       544 
532 
     | 
    
         | 
| 
       545 
533 
     | 
    
         
             
            #### CSS Selectors vs. XPath Expressions
         
     | 
| 
       546 
534 
     | 
    
         | 
| 
       547 
     | 
    
         
            -
            While the above examples all use CSS selectors to find elements, it is
         
     | 
| 
       548 
     | 
    
         
            -
             
     | 
| 
       549 
     | 
    
         
            -
            can use a CSS selector, you can use an XPath expression.
         
     | 
| 
      
 535 
     | 
    
         
            +
            While the above examples all use CSS selectors to find elements, it is possible to use XPath expressions too.
         
     | 
| 
      
 536 
     | 
    
         
            +
            In SitePrism, everywhere that you can use a CSS selector, you can use an XPath expression (Standard Capybara logic).
         
     | 
| 
       550 
537 
     | 
    
         | 
| 
       551 
538 
     | 
    
         
             
            An example:
         
     | 
| 
       552 
539 
     | 
    
         | 
| 
         @@ -1686,6 +1673,25 @@ When('I log in') do 
     | 
|
| 
       1686 
1673 
     | 
    
         
             
            end
         
     | 
| 
       1687 
1674 
     | 
    
         
             
            ```
         
     | 
| 
       1688 
1675 
     | 
    
         | 
| 
      
 1676 
     | 
    
         
            +
            ## Shadow Root
         
     | 
| 
      
 1677 
     | 
    
         
            +
             
     | 
| 
      
 1678 
     | 
    
         
            +
            SitePrism allows you to interact with Shadow Roots too.
         
     | 
| 
      
 1679 
     | 
    
         
            +
             
     | 
| 
      
 1680 
     | 
    
         
            +
            ### Creating an Shadow Root
         
     | 
| 
      
 1681 
     | 
    
         
            +
             
     | 
| 
      
 1682 
     | 
    
         
            +
            You can use the `section` methods and provide the arguments. Specify the reference name for the Shadow Root,
         
     | 
| 
      
 1683 
     | 
    
         
            +
            the CSS selector to locate it, and add the `:shadow_root` option. For example:
         
     | 
| 
      
 1684 
     | 
    
         
            +
             
     | 
| 
      
 1685 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1686 
     | 
    
         
            +
            class Home < SitePrism::Page
         
     | 
| 
      
 1687 
     | 
    
         
            +
              section :foo, '.foo', shadow_root: true do
         
     | 
| 
      
 1688 
     | 
    
         
            +
                element :bar, '.bar'
         
     | 
| 
      
 1689 
     | 
    
         
            +
              end
         
     | 
| 
      
 1690 
     | 
    
         
            +
            end
         
     | 
| 
      
 1691 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1692 
     | 
    
         
            +
             
     | 
| 
      
 1693 
     | 
    
         
            +
            NB: By default `shadow_root` will be set to false
         
     | 
| 
      
 1694 
     | 
    
         
            +
             
     | 
| 
       1689 
1695 
     | 
    
         
             
            ## SitePrism Configuration
         
     | 
| 
       1690 
1696 
     | 
    
         | 
| 
       1691 
1697 
     | 
    
         
             
            SitePrism can be configured to change its behaviour.
         
     | 
| 
         @@ -4,7 +4,7 @@ module SitePrism 
     | 
|
| 
       4 
4 
     | 
    
         
             
              # [SitePrism::Deprecator]
         
     | 
| 
       5 
5 
     | 
    
         
             
              class Deprecator
         
     | 
| 
       6 
6 
     | 
    
         
             
                class << self
         
     | 
| 
       7 
     | 
    
         
            -
                  # @return SitePrism.logger.warn(msg)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # @return [SitePrism.logger.warn(msg)]
         
     | 
| 
       8 
8 
     | 
    
         
             
                  #
         
     | 
| 
       9 
9 
     | 
    
         
             
                  # Tells the user that they are using old functionality, which needs removing in the
         
     | 
| 
       10 
10 
     | 
    
         
             
                  # next major version
         
     | 
| 
         @@ -15,22 +15,7 @@ module SitePrism 
     | 
|
| 
       15 
15 
     | 
    
         
             
                      warn("#{old} is being deprecated and should no longer be used.")
         
     | 
| 
       16 
16 
     | 
    
         
             
                    end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                    warn("#{old} will be removed in SitePrism  
     | 
| 
       19 
     | 
    
         
            -
                  end
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                  # @return SitePrism.logger.debug(msg)
         
     | 
| 
       22 
     | 
    
         
            -
                  #
         
     | 
| 
       23 
     | 
    
         
            -
                  # Tells the user that they are using functionality which is non-optimal
         
     | 
| 
       24 
     | 
    
         
            -
                  # The functionality should usually provide a reason for it being poor, as well as an
         
     | 
| 
       25 
     | 
    
         
            -
                  # optional way of upgrading to something different
         
     | 
| 
       26 
     | 
    
         
            -
                  #
         
     | 
| 
       27 
     | 
    
         
            -
                  # NB: As this is bubbled up at debug level, often users will not see this. So it will
         
     | 
| 
       28 
     | 
    
         
            -
                  # never be a candidate for removal directly
         
     | 
| 
       29 
     | 
    
         
            -
                  def soft_deprecate(old, reason, new = nil)
         
     | 
| 
       30 
     | 
    
         
            -
                    debug("The #{old} method is changing, as is SitePrism, and is now advised to be changed.")
         
     | 
| 
       31 
     | 
    
         
            -
                    debug("REASON: #{reason}.")
         
     | 
| 
       32 
     | 
    
         
            -
                    debug('Moving forwards into SitePrism v5, the default behaviour will change.')
         
     | 
| 
       33 
     | 
    
         
            -
                    debug("We advise you change to using #{new}") if new
         
     | 
| 
      
 18 
     | 
    
         
            +
                    warn("#{old} will be removed in SitePrism v6. You have been warned!")
         
     | 
| 
       34 
19 
     | 
    
         
             
                  end
         
     | 
| 
       35 
20 
     | 
    
         | 
| 
       36 
21 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -38,10 +23,6 @@ module SitePrism 
     | 
|
| 
       38 
23 
     | 
    
         
             
                  def warn(msg)
         
     | 
| 
       39 
24 
     | 
    
         
             
                    SitePrism.logger.warn(msg)
         
     | 
| 
       40 
25 
     | 
    
         
             
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  def debug(msg)
         
     | 
| 
       43 
     | 
    
         
            -
                    SitePrism.logger.debug(msg)
         
     | 
| 
       44 
     | 
    
         
            -
                  end
         
     | 
| 
       45 
26 
     | 
    
         
             
                end
         
     | 
| 
       46 
27 
     | 
    
         
             
              end
         
     | 
| 
       47 
28 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,144 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SitePrism
         
     | 
| 
      
 4 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 5 
     | 
    
         
            +
                # [SitePrism::DSL::Builder]
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # The Building logic - Initially coming from `.build`
         
     | 
| 
      
 10 
     | 
    
         
            +
                #   This will take a request to build from a DSL invocation such as `element` and generate a series of
         
     | 
| 
      
 11 
     | 
    
         
            +
                #   helper methods and waiters. It will also generate the correct classes for `SitePrism::Section` objects
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   Whilst doing all of this, it will also build up a "map" of objects in memory which can be used for
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   future interrogation. There are 2 ways of this being stored currently (Default as a hash, Legacy as an array)
         
     | 
| 
      
 15 
     | 
    
         
            +
                #
         
     | 
| 
      
 16 
     | 
    
         
            +
                module Builder
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # Return a list of all mapped items on a SitePrism class instance (Page or Section)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def mapped_items
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @mapped_items ||= { element: [], elements: [], section: [], sections: [], iframe: [] }
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  private
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def build(type, name, *find_args)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    invalid_element_name if invalid_element_name?(name)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    blank_element(name) if find_args.empty?
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    mapped_items[type] << name.to_sym
         
     | 
| 
      
 31 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 32 
     | 
    
         
            +
                    add_helper_methods(name, type, *find_args)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def invalid_element_name
         
     | 
| 
      
 36 
     | 
    
         
            +
                    raise InvalidDSLNameError, dsl_name_error
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def invalid_element_name?(name)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    !dsl_validation_disabled? && name_invalid?(name)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def dsl_validation_disabled?
         
     | 
| 
      
 44 
     | 
    
         
            +
                    SitePrism.dsl_validation_disabled || ENV.key?('SITEPRISM_DSL_VALIDATION_DISABLED')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def blank_element(name)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    raise SitePrism::InvalidElementError, "#{name} has come from an item with no locators."
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def add_helper_methods(name, _type, *find_args)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    create_existence_checker(name, *find_args)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    create_nonexistence_checker(name, *find_args)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    SitePrism::RSpecMatchers.new(name)._create_rspec_existence_matchers if defined?(RSpec)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    create_visibility_waiter(name, *find_args)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    create_invisibility_waiter(name, *find_args)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def create_existence_checker(element_name, *find_args)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    method_name = "has_#{element_name}?"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    create_helper_method(method_name, *find_args) do
         
     | 
| 
      
 62 
     | 
    
         
            +
                      define_method(method_name) do |*runtime_args|
         
     | 
| 
      
 63 
     | 
    
         
            +
                        args = merge_args(find_args, runtime_args)
         
     | 
| 
      
 64 
     | 
    
         
            +
                        element_exists?(*args)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def create_nonexistence_checker(element_name, *find_args)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    method_name = "has_no_#{element_name}?"
         
     | 
| 
      
 71 
     | 
    
         
            +
                    create_helper_method(method_name, *find_args) do
         
     | 
| 
      
 72 
     | 
    
         
            +
                      define_method(method_name) do |*runtime_args|
         
     | 
| 
      
 73 
     | 
    
         
            +
                        args = merge_args(find_args, runtime_args)
         
     | 
| 
      
 74 
     | 
    
         
            +
                        element_does_not_exist?(*args)
         
     | 
| 
      
 75 
     | 
    
         
            +
                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  def create_visibility_waiter(element_name, *find_args)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    method_name = "wait_until_#{element_name}_visible"
         
     | 
| 
      
 81 
     | 
    
         
            +
                    create_helper_method(method_name, *find_args) do
         
     | 
| 
      
 82 
     | 
    
         
            +
                      define_method(method_name) do |*runtime_args|
         
     | 
| 
      
 83 
     | 
    
         
            +
                        args = merge_args(find_args, runtime_args, visible: true)
         
     | 
| 
      
 84 
     | 
    
         
            +
                        return true if element_exists?(*args)
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                        raise SitePrism::ElementVisibilityTimeoutError
         
     | 
| 
      
 87 
     | 
    
         
            +
                      end
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  def create_invisibility_waiter(element_name, *find_args)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    method_name = "wait_until_#{element_name}_invisible"
         
     | 
| 
      
 93 
     | 
    
         
            +
                    create_helper_method(method_name, *find_args) do
         
     | 
| 
      
 94 
     | 
    
         
            +
                      define_method(method_name) do |*runtime_args|
         
     | 
| 
      
 95 
     | 
    
         
            +
                        args = merge_args(find_args, runtime_args, visible: true)
         
     | 
| 
      
 96 
     | 
    
         
            +
                        return true if element_does_not_exist?(*args)
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        raise SitePrism::ElementInvisibilityTimeoutError
         
     | 
| 
      
 99 
     | 
    
         
            +
                      end
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  def create_helper_method(proposed_method_name, *find_args)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    return blank_element(proposed_method_name) if find_args.empty?
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  def extract_section_options(args, &block)
         
     | 
| 
      
 110 
     | 
    
         
            +
                    if args.first.is_a?(Class)
         
     | 
| 
      
 111 
     | 
    
         
            +
                      klass = args.shift
         
     | 
| 
      
 112 
     | 
    
         
            +
                      section_class = klass if klass <= SitePrism::Section
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    section_class = deduce_section_class(section_class, &block)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    arguments = deduce_search_arguments(section_class, args)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    [section_class, arguments]
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  def deduce_section_class(base_class, &block)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    klass = base_class
         
     | 
| 
      
 122 
     | 
    
         
            +
                    klass = Class.new(klass || SitePrism::Section, &block) if block
         
     | 
| 
      
 123 
     | 
    
         
            +
                    return klass if klass
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    raise ArgumentError, 'You should provide descendant of SitePrism::Section class or/and a block as the second argument.'
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  def deduce_search_arguments(section_class, args)
         
     | 
| 
      
 129 
     | 
    
         
            +
                    extract_search_arguments(args) ||
         
     | 
| 
      
 130 
     | 
    
         
            +
                      extract_search_arguments(section_class.default_search_arguments) ||
         
     | 
| 
      
 131 
     | 
    
         
            +
                      invalidate_search_arguments!
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  def extract_search_arguments(args)
         
     | 
| 
      
 135 
     | 
    
         
            +
                    args if args && !args.empty?
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  def invalidate_search_arguments!
         
     | 
| 
      
 139 
     | 
    
         
            +
                    SitePrism.logger.error('Could not deduce search_arguments')
         
     | 
| 
      
 140 
     | 
    
         
            +
                    raise(ArgumentError, 'search arguments are needed in `section` definition or alternatively use `set_default_search_arguments`')
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
              end
         
     | 
| 
      
 144 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SitePrism
         
     | 
| 
      
 4 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 5 
     | 
    
         
            +
                # [SitePrism::DSL::Locators]
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # The locator logic for scoping all items - for use in locators, boolean tests and waiters
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                module Locators
         
     | 
| 
      
 12 
     | 
    
         
            +
                  private
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def _find(*find_args)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    kwargs = find_args.pop
         
     | 
| 
      
 16 
     | 
    
         
            +
                    shadow_root = kwargs.delete(:shadow_root) { false }
         
     | 
| 
      
 17 
     | 
    
         
            +
                    check_capybara_version_if_creating_shadow_root if shadow_root
         
     | 
| 
      
 18 
     | 
    
         
            +
                    to_capybara_node.find(*find_args, **kwargs).tap do |element|
         
     | 
| 
      
 19 
     | 
    
         
            +
                      break element.shadow_root if shadow_root
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def _all(*find_args)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    kwargs = find_args.pop
         
     | 
| 
      
 25 
     | 
    
         
            +
                    shadow_root = kwargs.delete(:shadow_root) { false }
         
     | 
| 
      
 26 
     | 
    
         
            +
                    check_capybara_version_if_creating_shadow_root if shadow_root
         
     | 
| 
      
 27 
     | 
    
         
            +
                    to_capybara_node.all(*find_args, **kwargs).tap do |element|
         
     | 
| 
      
 28 
     | 
    
         
            +
                      break element.map(&:shadow_root) if shadow_root
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def element_exists?(*find_args)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    kwargs = find_args.pop
         
     | 
| 
      
 34 
     | 
    
         
            +
                    kwargs.delete(:shadow_root)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    to_capybara_node.has_selector?(*find_args, **kwargs)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def element_does_not_exist?(*find_args)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    kwargs = find_args.pop
         
     | 
| 
      
 40 
     | 
    
         
            +
                    kwargs.delete(:shadow_root)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    to_capybara_node.has_no_selector?(*find_args, **kwargs)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Sanitize method called before calling any SitePrism DSL method or
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # meta-programmed method. This ensures that the Capybara query is correct.
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # Accepts any combination of arguments sent at DSL definition or runtime
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # and combines them in such a way that Capybara can operate with them.
         
     | 
| 
      
 49 
     | 
    
         
            +
                  def merge_args(find_args, runtime_args, visibility_args = {})
         
     | 
| 
      
 50 
     | 
    
         
            +
                    find_args = find_args.dup
         
     | 
| 
      
 51 
     | 
    
         
            +
                    runtime_args = runtime_args.dup
         
     | 
| 
      
 52 
     | 
    
         
            +
                    options = visibility_args.dup
         
     | 
| 
      
 53 
     | 
    
         
            +
                    SitePrism.logger.debug("Initial args: #{find_args}, #{runtime_args}.")
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    recombine_args(find_args, runtime_args, options)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    return [*find_args, *runtime_args, {}] if options.empty?
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    [*find_args, *runtime_args, options]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  # Options re-combiner. This takes the original inputs and combines
         
     | 
| 
      
 63 
     | 
    
         
            +
                  # them such that there is only one hash passed as a final argument
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # to Capybara.
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # If the hash is empty, then the hash is omitted from the payload sent
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # to Capybara, and the find / runtime arguments are sent alone.
         
     | 
| 
      
 68 
     | 
    
         
            +
                  #
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # NB: If the +wait+ key is present in the options hash, even as false or 0, It will
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # be set as the user-supplied value (So user error can be the cause for issues).
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def recombine_args(find_args, runtime_args, options)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    options.merge!(find_args.pop) if find_args.last.is_a? Hash
         
     | 
| 
      
 73 
     | 
    
         
            +
                    options.merge!(runtime_args.pop) if runtime_args.last.is_a? Hash
         
     | 
| 
      
 74 
     | 
    
         
            +
                    options[:wait] = Capybara.default_max_wait_time unless options.key?(:wait)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def check_capybara_version_if_creating_shadow_root
         
     | 
| 
      
 78 
     | 
    
         
            +
                    minimum_version = '3.37.0'
         
     | 
| 
      
 79 
     | 
    
         
            +
                    raise SitePrism::UnsupportedGemVersionError unless Capybara::VERSION >= minimum_version
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    SitePrism.logger.error("Shadow root support requires Capybara version >= #{minimum_version}. You are using #{Capybara::VERSION}.")
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,135 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SitePrism
         
     | 
| 
      
 4 
     | 
    
         
            +
              module DSL
         
     | 
| 
      
 5 
     | 
    
         
            +
                # [SitePrism::DSL::Methods]
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # The meta-programmed methods for using SitePrism during runtime. This public DSL contains all the methods
         
     | 
| 
      
 8 
     | 
    
         
            +
                # you will use on `SitePrism::Page` or `SitePrism::Section` classes
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                module Methods
         
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_reader :expected_items
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # Sets the `expected_items` iVar on a class. This property is used in conjunction with
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # `all_there?` to provide a way of granularising the check made to only interrogate a sub-set
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # of DSL defined items
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def expected_elements(*elements)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @expected_items = elements
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # Creates an instance of a SitePrism Element - This will create several methods designed to
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # Locate the element -> @return [Capybara::Node::Element]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # Check the elements presence or non-presence -> @return [Boolean]
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # Wait for the elements to be present or not -> @return [TrueClass, SitePrism::Error]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # Validate certain properties about the element
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def element(name, *find_args)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    raise_if_build_time_block_supplied(self, name, block_given?, :element)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    build(:element, name, *find_args) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                      define_method(name) do |*runtime_args, &runtime_block|
         
     | 
| 
      
 29 
     | 
    
         
            +
                        raise_if_runtime_block_supplied(self, name, runtime_block, :element)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        _find(*merge_args(find_args, runtime_args))
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # Creates a enumerable instance of a SitePrism Element - This will create several methods designed to
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # Locate the enumerable element -> @return [Capybara::Result]
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # Check the elements presence or non-presence -> @return [Boolean]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # Wait for the elements to be present or not -> @return [TrueClass, SitePrism::Error]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # Validate certain properties about the elements
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def elements(name, *find_args)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    raise_if_build_time_block_supplied(self, name, block_given?, :elements)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    build(:elements, name, *find_args) do
         
     | 
| 
      
 43 
     | 
    
         
            +
                      define_method(name) do |*runtime_args, &runtime_block|
         
     | 
| 
      
 44 
     | 
    
         
            +
                        raise_if_runtime_block_supplied(self, name, runtime_block, :elements)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        _all(*merge_args(find_args, runtime_args))
         
     | 
| 
      
 46 
     | 
    
         
            +
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # Creates an instance of a SitePrism Section - This will create several methods designed to
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # Locate the section -> @return [SitePrism::Section]
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # Check the section presence or non-presence -> @return [Boolean]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # Wait for the section to be present or not -> @return [TrueClass, SitePrism::Error]
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # Validate certain properties about the section
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def section(name, *args, &block)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    section_class, find_args = extract_section_options(args, &block)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    build(:section, name, *find_args) do
         
     | 
| 
      
 58 
     | 
    
         
            +
                      define_method(name) do |*runtime_args, &runtime_block|
         
     | 
| 
      
 59 
     | 
    
         
            +
                        section_element = _find(*merge_args(find_args, runtime_args))
         
     | 
| 
      
 60 
     | 
    
         
            +
                        section_class.new(self, section_element, &runtime_block)
         
     | 
| 
      
 61 
     | 
    
         
            +
                      end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  # Creates an enumerable instance of a SitePrism Section - This will create several methods designed to
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # Locate the sections -> @return [Array]
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # Check the sections presence or non-presence -> @return [Boolean]
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # Wait for the sections to be present or not -> @return [TrueClass, SitePrism::Error]
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # Validate certain properties about the section
         
     | 
| 
      
 70 
     | 
    
         
            +
                  def sections(name, *args, &block)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    section_class, find_args = extract_section_options(args, &block)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    build(:sections, name, *find_args) do
         
     | 
| 
      
 73 
     | 
    
         
            +
                      define_method(name) do |*runtime_args, &runtime_block|
         
     | 
| 
      
 74 
     | 
    
         
            +
                        raise_if_runtime_block_supplied(self, name, runtime_block, :sections)
         
     | 
| 
      
 75 
     | 
    
         
            +
                        _all(*merge_args(find_args, runtime_args)).map do |element|
         
     | 
| 
      
 76 
     | 
    
         
            +
                          section_class.new(self, element)
         
     | 
| 
      
 77 
     | 
    
         
            +
                        end
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def iframe(name, klass, *args)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    raise_if_build_time_block_supplied(self, name, block_given?, :elements)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    element_find_args = deduce_iframe_element_find_args(args)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    scope_find_args = deduce_iframe_scope_find_args(args)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    build(:iframe, name, *element_find_args) do
         
     | 
| 
      
 87 
     | 
    
         
            +
                      define_method(name) do |&block|
         
     | 
| 
      
 88 
     | 
    
         
            +
                        raise MissingBlockError unless block
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                        within_frame(*scope_find_args) { block.call(klass.new) }
         
     | 
| 
      
 91 
     | 
    
         
            +
                      end
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  private
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def raise_if_build_time_block_supplied(parent_object, name, has_block, type)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    return unless has_block
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    SitePrism.logger.debug("Type passed in: #{type}")
         
     | 
| 
      
 101 
     | 
    
         
            +
                    SitePrism.logger.error("#{name} has been defined as a '#{type}' item in #{parent_object}. It does not accept build-time blocks.")
         
     | 
| 
      
 102 
     | 
    
         
            +
                    raise SitePrism::UnsupportedBlockError
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  def deduce_iframe_element_find_args(args)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    warn_on_invalid_selector_input(args)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    case args[0]
         
     | 
| 
      
 108 
     | 
    
         
            +
                    when Integer then "iframe:nth-of-type(#{args[0] + 1})"
         
     | 
| 
      
 109 
     | 
    
         
            +
                    when String  then [:css, args[0]]
         
     | 
| 
      
 110 
     | 
    
         
            +
                    else args
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  def deduce_iframe_scope_find_args(args)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    warn_on_invalid_selector_input(args)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    case args[0]
         
     | 
| 
      
 117 
     | 
    
         
            +
                    when Integer then [args[0]]
         
     | 
| 
      
 118 
     | 
    
         
            +
                    when String  then [:css, args[0]]
         
     | 
| 
      
 119 
     | 
    
         
            +
                    else args
         
     | 
| 
      
 120 
     | 
    
         
            +
                    end
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  def warn_on_invalid_selector_input(args)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    return unless looks_like_xpath?(args[0])
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    SitePrism.logger.warn('The arguments passed in look like xpath. Check your locators.')
         
     | 
| 
      
 127 
     | 
    
         
            +
                    SitePrism.logger.debug("Default locator strategy: #{Capybara.default_selector}")
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  def looks_like_xpath?(arg)
         
     | 
| 
      
 131 
     | 
    
         
            +
                    arg.is_a?(String) && arg.start_with?('/', './')
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
              end
         
     | 
| 
      
 135 
     | 
    
         
            +
            end
         
     |