site_prism 2.6 → 2.7
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 +124 -76
- data/lib/site_prism.rb +3 -3
- data/lib/site_prism/addressable_url_matcher.rb +136 -0
- data/lib/site_prism/element_checker.rb +6 -5
- data/lib/site_prism/element_container.rb +133 -126
- data/lib/site_prism/exceptions.rb +2 -1
- data/lib/site_prism/page.rb +70 -23
- data/lib/site_prism/section.rb +12 -17
- data/lib/site_prism/version.rb +1 -2
- data/lib/site_prism/waiter.rb +4 -8
- metadata +23 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cecd18603e2b752a61108228d5d0977ebbd8117
|
4
|
+
data.tar.gz: cdbd3e91b71ce10e2f1fa210c76f7e0abe47307a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf99da3a4830b9bda41b26f548dbd8694e05e14122464c72503790cc00ff32bfc20c29c8d118726df5a9e264bdcfff33345068141b9e982ff95064c879ddee8d
|
7
|
+
data.tar.gz: c21eca71f43629070428a85bf7e7b1661b6a1af2e7d426490ffc4e20dd61ff9355e633a80140795f3296aaad6e0994ad4ff586e28b3e26cad683f8b0355f8013
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ class SearchResults < SitePrism::Page
|
|
29
29
|
|
30
30
|
section :menu, MenuSection, "#gbx3"
|
31
31
|
sections :search_results, SearchResultSection, "#results li"
|
32
|
-
|
32
|
+
|
33
33
|
def search_result_links
|
34
34
|
search_results.map {|sr| sr.title['href']}
|
35
35
|
end
|
@@ -57,9 +57,9 @@ end
|
|
57
57
|
|
58
58
|
Then /^the home page should contain the menu and the search form$/ do
|
59
59
|
@home.wait_for_menu # menu loads after a second or 2, give it time to arrive
|
60
|
-
@home.
|
61
|
-
@home.
|
62
|
-
@home.
|
60
|
+
expect(@home).to have_menu
|
61
|
+
expect(@home).to have_search_field
|
62
|
+
expect(@home).to have_search_button
|
63
63
|
end
|
64
64
|
|
65
65
|
When /^I search for Sausages$/ do
|
@@ -69,17 +69,16 @@ end
|
|
69
69
|
|
70
70
|
Then /^the search results page is displayed$/ do
|
71
71
|
@results_page = SearchResults.new
|
72
|
-
@results_page.
|
72
|
+
expect(@results_page).to be_displayed
|
73
73
|
end
|
74
74
|
|
75
75
|
Then /^the search results page contains 10 individual search results$/ do
|
76
76
|
@results_page.wait_for_search_results
|
77
|
-
@results_page.
|
78
|
-
@results_page.search_results.size.should == 10
|
77
|
+
expect(@results_page).to have_search_results count: 10
|
79
78
|
end
|
80
79
|
|
81
80
|
Then /^the search results contain a link to the wikipedia sausages page$/ do
|
82
|
-
@results_page.search_result_links.
|
81
|
+
expect(@results_page.search_result_links).to include "http://en.wikipedia.org/wiki/Sausage"
|
83
82
|
end
|
84
83
|
```
|
85
84
|
|
@@ -238,61 +237,87 @@ See https://github.com/sporkmonger/addressable for more details on parameterized
|
|
238
237
|
### Verifying that a particular page is displayed
|
239
238
|
|
240
239
|
Automated tests often need to verify that a particular page is
|
241
|
-
displayed.
|
242
|
-
|
243
|
-
|
244
|
-
matches a regular expression. For example, though `account/1` and `account/2`
|
245
|
-
are the same page, their URLs are different. To deal with this,
|
246
|
-
SitePrism provides the ability to set a URL matcher.
|
240
|
+
displayed. SitePrism can automatically parse your URL template
|
241
|
+
and verify that whatever components your template specifies match the
|
242
|
+
currently viewed page. For example, with the following URL template:
|
247
243
|
|
248
244
|
```ruby
|
249
245
|
class Account < SitePrism::Page
|
250
|
-
|
246
|
+
set_url "/accounts/{id}{?query*}"
|
251
247
|
end
|
252
248
|
```
|
253
249
|
|
254
|
-
|
255
|
-
displayed:
|
250
|
+
The following test code would pass:
|
256
251
|
|
257
252
|
```ruby
|
258
253
|
@account_page = Account.new
|
259
|
-
|
260
|
-
|
254
|
+
@account_page.load(id: 22, query: { token: "ca2786616a4285bc" })
|
255
|
+
|
256
|
+
expect(@account_page.current_url).to end_with "/accounts/22?token=ca2786616a4285bc"
|
257
|
+
expect(@account_page).to be_displayed
|
261
258
|
```
|
262
259
|
|
263
260
|
Calling `#displayed?` will return true if the browser's current URL
|
264
|
-
matches the
|
265
|
-
in the above example (`account/1` and `account/2`), calling
|
266
|
-
`@account_page.displayed?` will return true for both examples.
|
261
|
+
matches the page's template and false if it doesn't.
|
267
262
|
|
268
|
-
####
|
263
|
+
#### Specifying parameter values for templated URLs
|
269
264
|
|
270
|
-
|
271
|
-
|
265
|
+
Sometimes you want to verify not just that the current URL matches the
|
266
|
+
template, but that you're looking at a specific page matching that
|
267
|
+
template.
|
268
|
+
|
269
|
+
Given the previous example, if you wanted to ensure that the browser had loaded
|
270
|
+
account number 22, you could assert the following:
|
272
271
|
|
273
272
|
```ruby
|
274
|
-
|
275
|
-
@account_page.should be_displayed
|
276
|
-
@some_other_page.should_not be_displayed
|
277
|
-
end
|
273
|
+
expect(@account_page).to be_displayed(id: 22)
|
278
274
|
```
|
279
275
|
|
280
|
-
|
281
|
-
|
282
|
-
|
276
|
+
You can even use regular expressions. If, as a contrived example, you wanted to
|
277
|
+
ensure that the browser was displaying an account with an id ending with 2, you could
|
278
|
+
say:
|
283
279
|
|
284
280
|
```ruby
|
285
|
-
|
286
|
-
|
287
|
-
|
281
|
+
expect(@account_page).to be_displayed(id: /2\z/)
|
282
|
+
```
|
283
|
+
|
284
|
+
#### Accessing specific matches from a templated URL in your tests
|
285
|
+
|
286
|
+
If passing options to `displayed?` isn't powerful enough to meet your
|
287
|
+
needs, you can directly access and assert on the `url_matches` found
|
288
|
+
when comparing your page's URL template to the current_url:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
@account_page = Account.new
|
292
|
+
@account_page.load(id: 22, query: { token: "ca2786616a4285bc", color: 'irrelevant' })
|
293
|
+
|
294
|
+
expect(@account_page).to be_displayed(id: 22)
|
295
|
+
expect(@account_page.url_matches['query']['token']).to eq "ca2786616a4285bc"
|
296
|
+
```
|
297
|
+
|
298
|
+
#### Falling back to basic regexp matchers
|
299
|
+
|
300
|
+
If SitePrism's built-in URL matching is not sufficient for your needs
|
301
|
+
you can override and use SitePrism's previous support for regular expression-based
|
302
|
+
URL matchers by it by calling `set_url_matcher`:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
class Account < SitePrism::Page
|
306
|
+
set_url_matcher %r{/accounts/\d+}
|
288
307
|
end
|
289
308
|
```
|
290
309
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
310
|
+
#### Testing for Page display
|
311
|
+
|
312
|
+
SitePrism's `#displayed?` predicate method allows for semantic code in
|
313
|
+
your test:
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
Then /^the account page is displayed$/ do
|
317
|
+
expect(@account_page).to be_displayed
|
318
|
+
expect(@some_other_page).not_to be_displayed
|
319
|
+
end
|
320
|
+
```
|
296
321
|
|
297
322
|
### Getting the Current Page's URL
|
298
323
|
|
@@ -306,7 +331,7 @@ end
|
|
306
331
|
@account = Account.new
|
307
332
|
#...
|
308
333
|
@account.current_url #=> "http://www.example.com/account/123"
|
309
|
-
@account.current_url.
|
334
|
+
expect(@account.current_url).to include "example.com/account/"
|
310
335
|
```
|
311
336
|
|
312
337
|
### Page Title
|
@@ -336,7 +361,7 @@ end
|
|
336
361
|
@account = Account.new
|
337
362
|
#...
|
338
363
|
@account.secure? #=> true/false
|
339
|
-
@account.
|
364
|
+
expect(@account).to be_secure
|
340
365
|
```
|
341
366
|
|
342
367
|
## Elements
|
@@ -412,14 +437,14 @@ end
|
|
412
437
|
|
413
438
|
```ruby
|
414
439
|
Then /^the search field exists$/ do
|
415
|
-
@home.
|
440
|
+
expect(@home).to have_search_field
|
416
441
|
end
|
417
442
|
```
|
418
443
|
|
419
444
|
#### Testing that an element does not exist
|
420
445
|
|
421
446
|
To test that an element does not exist on the page, it is not possible to just call
|
422
|
-
`#
|
447
|
+
`#not_to have_search_field`. SitePrism supplies the `#has_no_<element>?` method
|
423
448
|
that should be used to test for non-existence. Using the above example:
|
424
449
|
|
425
450
|
```ruby
|
@@ -432,7 +457,7 @@ that should be used to test for non-existence. Using the above example:
|
|
432
457
|
|
433
458
|
```ruby
|
434
459
|
Then /^the search field exists$/ do
|
435
|
-
@home.
|
460
|
+
expect(@home).to have_no_search_field #NB: NOT => expect(@home).not_to_ have_search_field
|
436
461
|
end
|
437
462
|
```
|
438
463
|
|
@@ -575,9 +600,9 @@ arrays:
|
|
575
600
|
|
576
601
|
```ruby
|
577
602
|
@friends_page.names.each {|name| puts name.text}
|
578
|
-
@friends_page.names.map {|name| name.text}.
|
579
|
-
@friends_page.names.size.
|
580
|
-
@friends_page.
|
603
|
+
expect(@friends_page.names.map {|name| name.text}.to eq ["Alice", "Bob", "Fred"]
|
604
|
+
expect(@friends_page.names.size).to eq 3
|
605
|
+
expect(@friends_page).to have(3).names
|
581
606
|
```
|
582
607
|
|
583
608
|
#### Testing for the existence of the element collection
|
@@ -604,7 +629,7 @@ end
|
|
604
629
|
|
605
630
|
```ruby
|
606
631
|
Then /^there should be some names listed on the page$/ do
|
607
|
-
@friends_page.
|
632
|
+
expect(@friends_page).to have_names
|
608
633
|
end
|
609
634
|
```
|
610
635
|
|
@@ -641,7 +666,7 @@ to wait for:
|
|
641
666
|
Like the individual elements, calling the `elements` method will create
|
642
667
|
two methods: `wait_until_<elements_name>_visible` and
|
643
668
|
`wait_until_<elements_name>_invisible`. Calling these methods will cause
|
644
|
-
your test to wait for the elements to become visible or
|
669
|
+
your test to wait for the elements to become visible or invisible. Using
|
645
670
|
the above example:
|
646
671
|
|
647
672
|
```ruby
|
@@ -674,7 +699,7 @@ present in the browser, false if they're not all there.
|
|
674
699
|
# and...
|
675
700
|
|
676
701
|
Then /^the friends page contains all the expected elements$/ do
|
677
|
-
@friends_page.
|
702
|
+
expect(@friends_page).to be_all_there
|
678
703
|
end
|
679
704
|
|
680
705
|
```
|
@@ -760,7 +785,7 @@ selector that will be used to find the root element of the section; this
|
|
760
785
|
root node becomes the 'scope' of the section.
|
761
786
|
|
762
787
|
The following shows that though the same section can appear on multiple
|
763
|
-
pages, it can take a different root node:
|
788
|
+
pages, it can take a different root node:
|
764
789
|
|
765
790
|
```ruby
|
766
791
|
# define the section that appears on both pages
|
@@ -826,10 +851,10 @@ end
|
|
826
851
|
|
827
852
|
```ruby
|
828
853
|
Then /^the home page menu contains a link to the various search functions$/ do
|
829
|
-
@home.menu.
|
830
|
-
@home.menu.search['href'].
|
831
|
-
@home.menu.
|
832
|
-
@home.menu.
|
854
|
+
expect(@home.menu).to have_search
|
855
|
+
expect(@home.menu.search['href']).to include "google.com"
|
856
|
+
expect(@home.menu).to have_images
|
857
|
+
expect(@home.menu).to have_maps
|
833
858
|
end
|
834
859
|
```
|
835
860
|
|
@@ -917,8 +942,8 @@ end
|
|
917
942
|
Again, this allows pretty test code:
|
918
943
|
|
919
944
|
```ruby
|
920
|
-
@home.
|
921
|
-
@home.
|
945
|
+
expect(@home).to have_menu
|
946
|
+
expect(@home).not_to have_menu
|
922
947
|
```
|
923
948
|
|
924
949
|
#### Waiting for a section to exist
|
@@ -981,7 +1006,7 @@ sections within sections within sections within sections!
|
|
981
1006
|
|
982
1007
|
```ruby
|
983
1008
|
|
984
|
-
# define a page that contains an area that contains a section for both logging in and registration, then modelling each of the sub sections
|
1009
|
+
# define a page that contains an area that contains a section for both logging in and registration, then modelling each of the sub sections separately
|
985
1010
|
|
986
1011
|
class Login < SitePrism::Section
|
987
1012
|
element :username, "#username"
|
@@ -1010,8 +1035,8 @@ Then /^I sign in$/ do
|
|
1010
1035
|
@home = Home.new
|
1011
1036
|
@home.load
|
1012
1037
|
@home.wait_for_login_and_registration
|
1013
|
-
@home.
|
1014
|
-
@home.login_and_registration.
|
1038
|
+
expect(@home).to have_login_and_registration
|
1039
|
+
expect(@home.login_and_registration).to have_username
|
1015
1040
|
@home.login_and_registration.login.username.set "bob"
|
1016
1041
|
@home.login_and_registration.login.password.set "p4ssw0rd"
|
1017
1042
|
@home.login_and_registration.login.sign_in.click
|
@@ -1022,8 +1047,8 @@ end
|
|
1022
1047
|
When /^I enter my name into the home page's registration form$/ do
|
1023
1048
|
@home = Home.new
|
1024
1049
|
@home.load
|
1025
|
-
@home.login_and_registration.
|
1026
|
-
@home.login_and_registration.
|
1050
|
+
expect(@home.login_and_registration).to have_first_name
|
1051
|
+
expect(@home.login_and_registration).to have_last_name
|
1027
1052
|
@home.login_and_registration.first_name.set "Bob"
|
1028
1053
|
# ...
|
1029
1054
|
end
|
@@ -1049,7 +1074,7 @@ as an ordinary section:
|
|
1049
1074
|
|
1050
1075
|
```ruby
|
1051
1076
|
@home = Home.new
|
1052
|
-
@home.menu.
|
1077
|
+
expect(@home.menu).to have_title
|
1053
1078
|
```
|
1054
1079
|
|
1055
1080
|
### Section Collections
|
@@ -1097,10 +1122,10 @@ end
|
|
1097
1122
|
|
1098
1123
|
```ruby
|
1099
1124
|
Then /^there are lots of search_results$/ do
|
1100
|
-
@results_page.search_results.size.
|
1125
|
+
expect(@results_page.search_results.size).to eq 10
|
1101
1126
|
@results_page.search_results.each do |search_result|
|
1102
|
-
search_result.
|
1103
|
-
search_result.blurb.text.
|
1127
|
+
expect(search_result).to have_title
|
1128
|
+
expect(search_result.blurb.text).not_to be_nil
|
1104
1129
|
end
|
1105
1130
|
end
|
1106
1131
|
```
|
@@ -1121,7 +1146,7 @@ define a single anonymous section:
|
|
1121
1146
|
|
1122
1147
|
```ruby
|
1123
1148
|
class SearchResults < SitePrism::Page
|
1124
|
-
sections :search_results,
|
1149
|
+
sections :search_results, "#results li" do
|
1125
1150
|
element :title, "a.title"
|
1126
1151
|
element :blurb, "span.result-decription"
|
1127
1152
|
end
|
@@ -1159,7 +1184,7 @@ end
|
|
1159
1184
|
|
1160
1185
|
```ruby
|
1161
1186
|
Then /^there are search results on the page$/ do
|
1162
|
-
@results.page.
|
1187
|
+
expect(@results.page).to have_search_results
|
1163
1188
|
end
|
1164
1189
|
```
|
1165
1190
|
|
@@ -1217,7 +1242,7 @@ the page has not finished loading the element(s):
|
|
1217
1242
|
```ruby
|
1218
1243
|
@results_page = SearchResults.new
|
1219
1244
|
# ...
|
1220
|
-
@results_page.search_results.size.
|
1245
|
+
expect(@results_page.search_results.size).to == 25 # This may fail!
|
1221
1246
|
```
|
1222
1247
|
|
1223
1248
|
The above query can be rewritten to utilize the Capybara :count option when querying for
|
@@ -1239,7 +1264,7 @@ into our page and section classes:
|
|
1239
1264
|
|
1240
1265
|
```ruby
|
1241
1266
|
Then /^there are search results on the page$/ do
|
1242
|
-
@results.page.
|
1267
|
+
expect(@results.page).to have_search_results :count => 25
|
1243
1268
|
end
|
1244
1269
|
```
|
1245
1270
|
|
@@ -1267,6 +1292,30 @@ The following element methods allow Capybara options to be passed as arguments t
|
|
1267
1292
|
@results_page.wait_until_<element_or_section_name>_invisible :text => "Some ajaxy text disappears!"
|
1268
1293
|
```
|
1269
1294
|
|
1295
|
+
## Test views with Page objects
|
1296
|
+
|
1297
|
+
It's possible to use the same page objects of integration tests for view tests, too,
|
1298
|
+
just pass the rendered HTML to the ```load``` method:
|
1299
|
+
|
1300
|
+
```ruby
|
1301
|
+
require 'spec_helper'
|
1302
|
+
|
1303
|
+
describe 'admin/things/index' do
|
1304
|
+
let(:list_page) { AdminThingsListPage.new }
|
1305
|
+
let(:thing) { build(:thing, some_attribute: 'some attribute') }
|
1306
|
+
|
1307
|
+
it 'contains the things we expect' do
|
1308
|
+
assign(:things, [thing])
|
1309
|
+
|
1310
|
+
render template: 'admin/things/index'
|
1311
|
+
|
1312
|
+
list_page.load(rendered)
|
1313
|
+
|
1314
|
+
expect(list_page.rows.first.some_attribute).to have_text('some attribute')
|
1315
|
+
end
|
1316
|
+
end
|
1317
|
+
```
|
1318
|
+
|
1270
1319
|
## IFrames
|
1271
1320
|
|
1272
1321
|
SitePrism allows you to interact with iframes. An iframe is declared as
|
@@ -1311,7 +1360,7 @@ the above example, here's how it's done:
|
|
1311
1360
|
@page = PageContainingIframe.new
|
1312
1361
|
# ...
|
1313
1362
|
@page.has_my_iframe? #=> true
|
1314
|
-
@page.
|
1363
|
+
expect(@page).to have_my_iframe
|
1315
1364
|
```
|
1316
1365
|
|
1317
1366
|
### Waiting for an iframe
|
@@ -1416,12 +1465,12 @@ all over the place. Here's an example of this common problem:
|
|
1416
1465
|
@home.search_field.set "Sausages"
|
1417
1466
|
@home.search_field.search_button.click
|
1418
1467
|
@results_page = SearchResults.new # <-- noise
|
1419
|
-
@results_page.
|
1468
|
+
expect(@results_page).to have_search_result_items
|
1420
1469
|
```
|
1421
1470
|
|
1422
1471
|
The annoyance (and, later, maintenance nightmare) is having to create
|
1423
1472
|
`@home` and `@results_page`. It would be better to not have to create
|
1424
|
-
instances of pages all over your tests.
|
1473
|
+
instances of pages all over your tests.
|
1425
1474
|
|
1426
1475
|
The way I've dealt with this problem is to create a class containing
|
1427
1476
|
methods that return instances of the pages. Eg:
|
@@ -1471,7 +1520,7 @@ When /^I search for Sausages$/ do
|
|
1471
1520
|
end
|
1472
1521
|
|
1473
1522
|
Then /^I am on the results page$/ do
|
1474
|
-
@app.results_page.
|
1523
|
+
expect(@app.results_page).to be_displayed
|
1475
1524
|
end
|
1476
1525
|
|
1477
1526
|
# etc...
|
@@ -1480,4 +1529,3 @@ end
|
|
1480
1529
|
The only thing that needs instantiating is the App class - from then on
|
1481
1530
|
pages don't need to be initialized, they are now returned by methods on
|
1482
1531
|
@app. Maintenance win!
|
1483
|
-
|
data/lib/site_prism.rb
CHANGED
@@ -7,7 +7,8 @@ module SitePrism
|
|
7
7
|
autoload :Page, 'site_prism/page'
|
8
8
|
autoload :Section, 'site_prism/section'
|
9
9
|
autoload :Waiter, 'site_prism/waiter'
|
10
|
-
|
10
|
+
autoload :AddressableUrlMatcher, 'site_prism/addressable_url_matcher'
|
11
|
+
|
11
12
|
class << self
|
12
13
|
attr_accessor :use_implicit_waits
|
13
14
|
|
@@ -18,6 +19,5 @@ module SitePrism
|
|
18
19
|
|
19
20
|
private
|
20
21
|
|
21
|
-
|
22
|
+
@use_implicit_waits = false
|
22
23
|
end
|
23
|
-
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module SitePrism
|
5
|
+
class AddressableUrlMatcher
|
6
|
+
COMPONENT_NAMES = %w(scheme user password host port path query fragment).map(&:to_sym).freeze
|
7
|
+
COMPONENT_PREFIXES = {
|
8
|
+
query: '?',
|
9
|
+
fragment: '#'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_reader :pattern
|
13
|
+
|
14
|
+
def initialize(pattern)
|
15
|
+
@pattern = pattern
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return the hash of extracted mappings from parsing the provided URL according to our pattern,
|
19
|
+
# or nil if the URL doesn't conform to the matcher template.
|
20
|
+
def mappings(url)
|
21
|
+
uri = Addressable::URI.parse(url)
|
22
|
+
result = {}
|
23
|
+
COMPONENT_NAMES.each do |component|
|
24
|
+
component_result = component_matches(component, uri)
|
25
|
+
result = component_result ? result.merge!(component_result) : nil
|
26
|
+
break unless result
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
# Determine whether URL matches our pattern, and optionally whether the extracted mappings match
|
32
|
+
# a hash of expected values. You can specify values as strings, numbers or regular expressions.
|
33
|
+
def matches?(url, expected_mappings = {})
|
34
|
+
actual_mappings = mappings(url)
|
35
|
+
if actual_mappings
|
36
|
+
expected_mappings.empty? ? true : all_expected_mappings_match?(expected_mappings, actual_mappings)
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def all_expected_mappings_match?(expected_mappings, actual_mappings)
|
45
|
+
expected_mappings.all? do |key, expected_value|
|
46
|
+
actual_value = actual_mappings[key.to_s]
|
47
|
+
case expected_value
|
48
|
+
when Numeric
|
49
|
+
actual_value == expected_value.to_s
|
50
|
+
when Regexp
|
51
|
+
actual_value.match(expected_value)
|
52
|
+
else
|
53
|
+
expected_value == actual_value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Memoizes the extracted component templates
|
59
|
+
def component_templates
|
60
|
+
@component_templates ||= extract_component_templates
|
61
|
+
end
|
62
|
+
|
63
|
+
def extract_component_templates
|
64
|
+
COMPONENT_NAMES.each_with_object({}) do |component, component_templates|
|
65
|
+
component_url = to_substituted_uri.public_send(component).to_s
|
66
|
+
|
67
|
+
next unless component_url && component_url != ''
|
68
|
+
|
69
|
+
reverse_substitutions.each_pair do |substituted_value, template_value|
|
70
|
+
component_url = component_url.sub(substituted_value, template_value)
|
71
|
+
end
|
72
|
+
|
73
|
+
component_templates[component] = Addressable::Template.new(component_url.to_s)
|
74
|
+
end.freeze
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns empty hash if the template omits the component, a set of substitutions if the
|
78
|
+
# provided URI component matches the template component, or nil if the match fails.
|
79
|
+
def component_matches(component, uri)
|
80
|
+
extracted_mappings = {}
|
81
|
+
component_template = component_templates[component]
|
82
|
+
if component_template
|
83
|
+
component_url = uri.public_send(component).to_s
|
84
|
+
unless (extracted_mappings = component_template.extract(component_url))
|
85
|
+
# to support Addressable's expansion of queries, ensure it's parsing the fragment as appropriate (e.g. {?params*})
|
86
|
+
prefix = COMPONENT_PREFIXES[component]
|
87
|
+
return nil unless prefix && (extracted_mappings = component_template.extract(prefix + component_url))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
extracted_mappings
|
91
|
+
end
|
92
|
+
|
93
|
+
# Convert the pattern into an Addressable URI by substituting the template slugs with nonsense strings.
|
94
|
+
def to_substituted_uri
|
95
|
+
url = pattern
|
96
|
+
substitutions.each_pair do |slug, value|
|
97
|
+
url = url.sub(slug, value)
|
98
|
+
end
|
99
|
+
begin
|
100
|
+
Addressable::URI.parse(url)
|
101
|
+
rescue Addressable::URI::InvalidURIError
|
102
|
+
raise SitePrism::InvalidUrlMatcher, 'Could not automatically match your URL. Note: templated port numbers are not currently supported.'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def substitutions
|
107
|
+
@substitutions ||= slugs.each_with_index.reduce({}) do |memo, slugindex|
|
108
|
+
slug, index = slugindex
|
109
|
+
memo.merge(slug => slug_prefix(slug) + substitution_value(index))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def reverse_substitutions
|
114
|
+
@reverse_substitutions ||= slugs.each_with_index.reduce({}) do |memo, slugindex|
|
115
|
+
slug, index = slugindex
|
116
|
+
memo.merge(slug_prefix(slug) + substitution_value(index) => slug, substitution_value(index) => slug)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def slugs
|
121
|
+
pattern.scan(/\{[^}]+\}/)
|
122
|
+
end
|
123
|
+
|
124
|
+
# If a slug begins with non-alpha characters, it may denote the start of a new component (e.g. query or fragment).
|
125
|
+
# We emit this prefix as part of the substituted slug so that Addressable's URI parser can see it as such.
|
126
|
+
def slug_prefix(slug)
|
127
|
+
matches = slug.match(/\A\{([^A-Za-z]+)/)
|
128
|
+
matches && matches[1] || ''
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generate a repeatable 5 character uniform alphabetical nonsense string to allow parsing as a URI
|
132
|
+
def substitution_value(index)
|
133
|
+
Base64.urlsafe_encode64(Digest::SHA1.digest(index.to_s)).gsub(/[^A-Za-z]/, '')[0..5]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
module SitePrism
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module SitePrism
|
2
|
+
module ElementChecker
|
3
|
+
def all_there?
|
4
|
+
Capybara.using_wait_time(0) do
|
5
|
+
self.class.mapped_items.all? { |element| send "has_#{element}?" }
|
6
|
+
end
|
5
7
|
end
|
6
8
|
end
|
7
9
|
end
|
8
|
-
|
@@ -1,178 +1,185 @@
|
|
1
|
-
module SitePrism
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module SitePrism
|
2
|
+
module ElementContainer
|
3
|
+
attr_reader :mapped_items
|
4
|
+
|
5
|
+
def element(element_name, *find_args)
|
6
|
+
build element_name, *find_args do
|
7
|
+
define_method element_name.to_s do |*runtime_args, &element_block|
|
8
|
+
self.class.raise_if_block(self, element_name.to_s, !element_block.nil?)
|
9
|
+
find_first(*find_args, *runtime_args)
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
9
|
-
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
def elements(collection_name, *find_args)
|
15
|
+
build collection_name, *find_args do
|
16
|
+
define_method collection_name.to_s do |*runtime_args, &element_block|
|
17
|
+
self.class.raise_if_block(self, collection_name.to_s, !element_block.nil?)
|
18
|
+
find_all(*find_args, *runtime_args)
|
19
|
+
end
|
15
20
|
end
|
16
21
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
alias_method :collection, :elements
|
23
|
+
|
24
|
+
def section(section_name, *args, &block)
|
25
|
+
section_class, find_args = extract_section_options args, &block
|
26
|
+
build section_name, *find_args do
|
27
|
+
define_method section_name do |*runtime_args, &element_block|
|
28
|
+
self.class.raise_if_block(self, section_name.to_s, !element_block.nil?)
|
29
|
+
section_class.new self, find_first(*find_args, *runtime_args)
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
27
|
-
end
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
def sections(section_collection_name, *args, &block)
|
35
|
+
section_class, find_args = extract_section_options args, &block
|
36
|
+
build section_collection_name, *find_args do
|
37
|
+
define_method section_collection_name do |*runtime_args, &element_block|
|
38
|
+
self.class.raise_if_block(self, section_collection_name.to_s, !element_block.nil?)
|
39
|
+
find_all(*find_args, *runtime_args).map do |element|
|
40
|
+
section_class.new self, element
|
41
|
+
end
|
35
42
|
end
|
36
43
|
end
|
37
44
|
end
|
38
|
-
end
|
39
45
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
def iframe(iframe_name, iframe_page_class, selector)
|
47
|
+
element_selector = deduce_iframe_element_selector(selector)
|
48
|
+
scope_selector = deduce_iframe_scope_selector(selector)
|
49
|
+
add_to_mapped_items iframe_name
|
50
|
+
create_existence_checker iframe_name, element_selector
|
51
|
+
create_nonexistence_checker iframe_name, element_selector
|
52
|
+
create_waiter iframe_name, element_selector
|
53
|
+
define_method iframe_name do |&block|
|
54
|
+
within_frame scope_selector do
|
55
|
+
block.call iframe_page_class.new
|
56
|
+
end
|
50
57
|
end
|
51
58
|
end
|
52
|
-
end
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
def add_to_mapped_items(item)
|
61
|
+
@mapped_items ||= []
|
62
|
+
@mapped_items << item.to_s
|
63
|
+
end
|
58
64
|
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
def raise_if_block(obj, name, has_block)
|
66
|
+
return unless has_block
|
67
|
+
fail SitePrism::UnsupportedBlock, "#{obj.class}##{name} does not accept blocks, did you mean to define a (i)frame?"
|
68
|
+
end
|
62
69
|
|
63
|
-
|
70
|
+
private
|
64
71
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
72
|
+
def build(name, *find_args)
|
73
|
+
if find_args.empty?
|
74
|
+
create_no_selector name
|
75
|
+
else
|
76
|
+
add_to_mapped_items name
|
77
|
+
yield
|
78
|
+
end
|
79
|
+
add_helper_methods name, *find_args
|
71
80
|
end
|
72
|
-
add_helper_methods name, *find_args
|
73
|
-
end
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
+
def add_helper_methods(name, *find_args)
|
83
|
+
create_existence_checker name, *find_args
|
84
|
+
create_nonexistence_checker name, *find_args
|
85
|
+
create_waiter name, *find_args
|
86
|
+
create_visibility_waiter name, *find_args
|
87
|
+
create_invisibility_waiter name, *find_args
|
88
|
+
end
|
82
89
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
90
|
+
def create_helper_method(proposed_method_name, *find_args)
|
91
|
+
if find_args.empty?
|
92
|
+
create_no_selector proposed_method_name
|
93
|
+
else
|
94
|
+
yield
|
95
|
+
end
|
88
96
|
end
|
89
|
-
end
|
90
97
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
+
def create_existence_checker(element_name, *find_args)
|
99
|
+
method_name = "has_#{element_name}?"
|
100
|
+
create_helper_method method_name, *find_args do
|
101
|
+
define_method method_name do |*runtime_args|
|
102
|
+
wait_time = SitePrism.use_implicit_waits ? Capybara.default_wait_time : 0
|
103
|
+
Capybara.using_wait_time wait_time do
|
104
|
+
element_exists?(*find_args, *runtime_args)
|
105
|
+
end
|
98
106
|
end
|
99
107
|
end
|
100
108
|
end
|
101
|
-
end
|
102
109
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
+
def create_nonexistence_checker(element_name, *find_args)
|
111
|
+
method_name = "has_no_#{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_wait_time : 0
|
115
|
+
Capybara.using_wait_time wait_time do
|
116
|
+
element_does_not_exist?(*find_args, *runtime_args)
|
117
|
+
end
|
110
118
|
end
|
111
119
|
end
|
112
120
|
end
|
113
|
-
end
|
114
121
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
+
def create_waiter(element_name, *find_args)
|
123
|
+
method_name = "wait_for_#{element_name}"
|
124
|
+
create_helper_method method_name, *find_args do
|
125
|
+
define_method method_name do |timeout = nil, *runtime_args|
|
126
|
+
timeout = timeout.nil? ? Capybara.default_wait_time : timeout
|
127
|
+
Capybara.using_wait_time timeout do
|
128
|
+
element_exists?(*find_args, *runtime_args)
|
129
|
+
end
|
122
130
|
end
|
123
131
|
end
|
124
132
|
end
|
125
|
-
end
|
126
133
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
+
def create_visibility_waiter(element_name, *find_args)
|
135
|
+
method_name = "wait_until_#{element_name}_visible"
|
136
|
+
create_helper_method method_name, *find_args do
|
137
|
+
define_method method_name do |timeout = Capybara.default_wait_time, *runtime_args|
|
138
|
+
Timeout.timeout timeout, SitePrism::TimeOutWaitingForElementVisibility do
|
139
|
+
Capybara.using_wait_time 0 do
|
140
|
+
sleep 0.05 until element_exists?(*find_args, *runtime_args, visible: true)
|
141
|
+
end
|
134
142
|
end
|
135
143
|
end
|
136
144
|
end
|
137
145
|
end
|
138
|
-
end
|
139
146
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
+
def create_invisibility_waiter(element_name, *find_args)
|
148
|
+
method_name = "wait_until_#{element_name}_invisible"
|
149
|
+
create_helper_method method_name, *find_args do
|
150
|
+
define_method method_name do |timeout = Capybara.default_wait_time, *runtime_args|
|
151
|
+
Timeout.timeout timeout, SitePrism::TimeOutWaitingForElementInvisibility do
|
152
|
+
Capybara.using_wait_time 0 do
|
153
|
+
sleep 0.05 while element_exists?(*find_args, *runtime_args, visible: true)
|
154
|
+
end
|
147
155
|
end
|
148
156
|
end
|
149
157
|
end
|
150
158
|
end
|
151
|
-
end
|
152
159
|
|
153
|
-
|
154
|
-
|
155
|
-
|
160
|
+
def create_no_selector(method_name)
|
161
|
+
define_method method_name do
|
162
|
+
fail SitePrism::NoSelectorForElement.new, "#{self.class.name} => :#{method_name} needs a selector"
|
163
|
+
end
|
156
164
|
end
|
157
|
-
end
|
158
165
|
|
159
|
-
|
160
|
-
|
161
|
-
|
166
|
+
def deduce_iframe_scope_selector(selector)
|
167
|
+
selector.is_a?(Integer) ? selector : selector.split('#').last
|
168
|
+
end
|
162
169
|
|
163
|
-
|
164
|
-
|
165
|
-
|
170
|
+
def deduce_iframe_element_selector(selector)
|
171
|
+
selector.is_a?(Integer) ? "iframe:nth-of-type(#{selector + 1})" : selector
|
172
|
+
end
|
166
173
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
+
def extract_section_options(args, &block)
|
175
|
+
if args.first.is_a? Class
|
176
|
+
section_class = args.shift
|
177
|
+
elsif block_given?
|
178
|
+
section_class = Class.new SitePrism::Section, &block
|
179
|
+
else
|
180
|
+
fail ArgumentError, 'You should provide section class either as a block, or as the second argument'
|
181
|
+
end
|
182
|
+
return section_class, args
|
174
183
|
end
|
175
|
-
return section_class, args
|
176
184
|
end
|
177
185
|
end
|
178
|
-
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module SitePrism
|
2
2
|
class NoUrlForPage < StandardError; end
|
3
3
|
class NoUrlMatcherForPage < StandardError; end
|
4
|
+
class InvalidUrlMatcher < StandardError; end
|
4
5
|
class NoSelectorForElement < StandardError; end
|
5
6
|
class TimeoutException < StandardError; end
|
6
7
|
class TimeOutWaitingForElementVisibility < StandardError; end
|
7
8
|
class TimeOutWaitingForElementInvisibility < StandardError; end
|
9
|
+
class UnsupportedBlock < StandardError; end
|
8
10
|
end
|
9
|
-
|
data/lib/site_prism/page.rb
CHANGED
@@ -4,28 +4,54 @@ module SitePrism
|
|
4
4
|
include ElementChecker
|
5
5
|
extend ElementContainer
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
raise SitePrism::NoUrlForPage if expanded_url.nil?
|
10
|
-
visit expanded_url
|
7
|
+
def page
|
8
|
+
@page || Capybara.current_session
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
|
11
|
+
def load(expansion_or_html = {})
|
12
|
+
if expansion_or_html.is_a? String
|
13
|
+
@page = Capybara.string(expansion_or_html)
|
14
|
+
else
|
15
|
+
expanded_url = url(expansion_or_html)
|
16
|
+
fail SitePrism::NoUrlForPage if expanded_url.nil?
|
17
|
+
visit expanded_url
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def displayed?(*args)
|
22
|
+
expected_mappings = args.last.is_a?(::Hash) ? args.pop : {}
|
23
|
+
seconds = args.length > 0 ? args.first : Waiter.default_wait_time
|
24
|
+
fail SitePrism::NoUrlMatcherForPage if url_matcher.nil?
|
15
25
|
begin
|
16
|
-
Waiter.wait_until_true(seconds)
|
17
|
-
|
18
|
-
|
19
|
-
rescue SitePrism::TimeoutException=>e
|
20
|
-
return false
|
26
|
+
Waiter.wait_until_true(seconds) { url_matches?(expected_mappings) }
|
27
|
+
rescue SitePrism::TimeoutException
|
28
|
+
false
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
24
|
-
def
|
32
|
+
def url_matches(seconds = Waiter.default_wait_time)
|
33
|
+
return unless displayed?(seconds)
|
34
|
+
|
35
|
+
if url_matcher.is_a?(Regexp)
|
36
|
+
regexp_backed_matches
|
37
|
+
else
|
38
|
+
template_backed_matches
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def regexp_backed_matches
|
43
|
+
url_matcher.match(page.current_url)
|
44
|
+
end
|
45
|
+
|
46
|
+
def template_backed_matches
|
47
|
+
matcher_template.mappings(page.current_url)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.set_url(page_url)
|
25
51
|
@url = page_url.to_s
|
26
52
|
end
|
27
53
|
|
28
|
-
def self.set_url_matcher
|
54
|
+
def self.set_url_matcher(page_url_matcher)
|
29
55
|
@url_matcher = page_url_matcher
|
30
56
|
end
|
31
57
|
|
@@ -34,7 +60,7 @@ module SitePrism
|
|
34
60
|
end
|
35
61
|
|
36
62
|
def self.url_matcher
|
37
|
-
@url_matcher
|
63
|
+
@url_matcher || url
|
38
64
|
end
|
39
65
|
|
40
66
|
def url(expansion = {})
|
@@ -52,21 +78,42 @@ module SitePrism
|
|
52
78
|
|
53
79
|
private
|
54
80
|
|
55
|
-
def find_first
|
56
|
-
find
|
81
|
+
def find_first(*find_args)
|
82
|
+
find(*find_args)
|
57
83
|
end
|
58
84
|
|
59
|
-
def find_all
|
60
|
-
all
|
85
|
+
def find_all(*find_args)
|
86
|
+
all(*find_args)
|
61
87
|
end
|
62
88
|
|
63
|
-
def element_exists?
|
64
|
-
has_selector?
|
89
|
+
def element_exists?(*find_args)
|
90
|
+
has_selector?(*find_args)
|
65
91
|
end
|
66
92
|
|
67
|
-
def element_does_not_exist?
|
68
|
-
has_no_selector?
|
93
|
+
def element_does_not_exist?(*find_args)
|
94
|
+
has_no_selector?(*find_args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def url_matches?(expected_mappings = {})
|
98
|
+
if url_matcher.is_a?(Regexp)
|
99
|
+
url_matches_by_regexp?
|
100
|
+
elsif url_matcher.respond_to?(:to_str)
|
101
|
+
url_matches_by_template?(expected_mappings)
|
102
|
+
else
|
103
|
+
fail SitePrism::InvalidUrlMatcher
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def url_matches_by_regexp?
|
108
|
+
!regexp_backed_matches.nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
def url_matches_by_template?(expected_mappings)
|
112
|
+
matcher_template.matches?(page.current_url, expected_mappings)
|
113
|
+
end
|
114
|
+
|
115
|
+
def matcher_template
|
116
|
+
@addressable_url_matcher ||= AddressableUrlMatcher.new(url_matcher)
|
69
117
|
end
|
70
118
|
end
|
71
119
|
end
|
72
|
-
|
data/lib/site_prism/section.rb
CHANGED
@@ -6,7 +6,7 @@ module SitePrism
|
|
6
6
|
|
7
7
|
attr_reader :root_element, :parent
|
8
8
|
|
9
|
-
def initialize
|
9
|
+
def initialize(parent, root_element)
|
10
10
|
@parent, @root_element = parent, root_element
|
11
11
|
end
|
12
12
|
|
@@ -14,16 +14,16 @@ module SitePrism
|
|
14
14
|
root_element.visible?
|
15
15
|
end
|
16
16
|
|
17
|
-
def execute_script
|
17
|
+
def execute_script(input)
|
18
18
|
Capybara.current_session.execute_script input
|
19
19
|
end
|
20
20
|
|
21
|
-
def evaluate_script
|
21
|
+
def evaluate_script(input)
|
22
22
|
Capybara.current_session.evaluate_script input
|
23
23
|
end
|
24
24
|
|
25
25
|
def parent_page
|
26
|
-
candidate_page =
|
26
|
+
candidate_page = parent
|
27
27
|
until candidate_page.is_a?(SitePrism::Page)
|
28
28
|
candidate_page = candidate_page.parent
|
29
29
|
end
|
@@ -32,25 +32,20 @@ module SitePrism
|
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
-
def find_first
|
36
|
-
root_element.find
|
35
|
+
def find_first(*find_args)
|
36
|
+
root_element.find(*find_args)
|
37
37
|
end
|
38
38
|
|
39
|
-
def find_all
|
40
|
-
root_element.all
|
39
|
+
def find_all(*find_args)
|
40
|
+
root_element.all(*find_args)
|
41
41
|
end
|
42
42
|
|
43
|
-
def element_exists?
|
44
|
-
unless root_element.nil?
|
45
|
-
root_element.has_selector? *find_args
|
46
|
-
end
|
43
|
+
def element_exists?(*find_args)
|
44
|
+
root_element.has_selector?(*find_args) unless root_element.nil?
|
47
45
|
end
|
48
46
|
|
49
|
-
def element_does_not_exist?
|
50
|
-
unless root_element.nil?
|
51
|
-
root_element.has_no_selector? *find_args
|
52
|
-
end
|
47
|
+
def element_does_not_exist?(*find_args)
|
48
|
+
root_element.has_no_selector?(*find_args) unless root_element.nil?
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
56
|
-
|
data/lib/site_prism/version.rb
CHANGED
data/lib/site_prism/waiter.rb
CHANGED
@@ -1,21 +1,17 @@
|
|
1
1
|
module SitePrism
|
2
2
|
class Waiter
|
3
|
-
def self.wait_until_true(wait_time_seconds=default_wait_time)
|
3
|
+
def self.wait_until_true(wait_time_seconds = default_wait_time)
|
4
4
|
start_time = Time.now
|
5
5
|
loop do
|
6
6
|
return true if yield
|
7
7
|
break unless Time.now - start_time <= wait_time_seconds
|
8
8
|
sleep(0.05)
|
9
9
|
end
|
10
|
-
|
10
|
+
fail SitePrism::TimeoutException, 'Timed out while waiting for block to return true'
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.default_wait_time
|
14
|
-
|
14
|
+
Capybara.default_wait_time
|
15
15
|
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
@@default_wait_time = Capybara.default_wait_time
|
20
16
|
end
|
21
|
-
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,61 +1,67 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: site_prism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.7'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nat Ritmeyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '2.1'
|
20
|
-
- - <
|
20
|
+
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '3.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '2.1'
|
30
|
-
- - <
|
30
|
+
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '3.0'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: addressable
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- -
|
37
|
+
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: 2.3.3
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '3.0'
|
40
43
|
type: :runtime
|
41
44
|
prerelease: false
|
42
45
|
version_requirements: !ruby/object:Gem::Requirement
|
43
46
|
requirements:
|
44
|
-
- -
|
47
|
+
- - ">="
|
45
48
|
- !ruby/object:Gem::Version
|
46
49
|
version: 2.3.3
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '3.0'
|
47
53
|
- !ruby/object:Gem::Dependency
|
48
54
|
name: rspec
|
49
55
|
requirement: !ruby/object:Gem::Requirement
|
50
56
|
requirements:
|
51
|
-
- - <
|
57
|
+
- - "<"
|
52
58
|
- !ruby/object:Gem::Version
|
53
59
|
version: '4.0'
|
54
60
|
type: :development
|
55
61
|
prerelease: false
|
56
62
|
version_requirements: !ruby/object:Gem::Requirement
|
57
63
|
requirements:
|
58
|
-
- - <
|
64
|
+
- - "<"
|
59
65
|
- !ruby/object:Gem::Version
|
60
66
|
version: '4.0'
|
61
67
|
description: SitePrism gives you a simple, clean and semantic DSL for describing your
|
@@ -65,6 +71,10 @@ executables: []
|
|
65
71
|
extensions: []
|
66
72
|
extra_rdoc_files: []
|
67
73
|
files:
|
74
|
+
- LICENSE
|
75
|
+
- README.md
|
76
|
+
- lib/site_prism.rb
|
77
|
+
- lib/site_prism/addressable_url_matcher.rb
|
68
78
|
- lib/site_prism/element_checker.rb
|
69
79
|
- lib/site_prism/element_container.rb
|
70
80
|
- lib/site_prism/exceptions.rb
|
@@ -72,9 +82,6 @@ files:
|
|
72
82
|
- lib/site_prism/section.rb
|
73
83
|
- lib/site_prism/version.rb
|
74
84
|
- lib/site_prism/waiter.rb
|
75
|
-
- lib/site_prism.rb
|
76
|
-
- LICENSE
|
77
|
-
- README.md
|
78
85
|
homepage: http://github.com/natritmeyer/site_prism
|
79
86
|
licenses:
|
80
87
|
- BSD3
|
@@ -85,17 +92,17 @@ require_paths:
|
|
85
92
|
- lib
|
86
93
|
required_ruby_version: !ruby/object:Gem::Requirement
|
87
94
|
requirements:
|
88
|
-
- -
|
95
|
+
- - ">="
|
89
96
|
- !ruby/object:Gem::Version
|
90
97
|
version: 1.9.3
|
91
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
99
|
requirements:
|
93
|
-
- -
|
100
|
+
- - ">="
|
94
101
|
- !ruby/object:Gem::Version
|
95
102
|
version: '0'
|
96
103
|
requirements: []
|
97
104
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
105
|
+
rubygems_version: 2.4.5
|
99
106
|
signing_key:
|
100
107
|
specification_version: 4
|
101
108
|
summary: A Page Object Model DSL for Capybara
|