playwright-ruby-client 1.61.0 → 1.63.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -17
  3. data/documentation/docs/api/api_request_context.md +3 -0
  4. data/documentation/docs/api/api_response.md +12 -0
  5. data/documentation/docs/api/browser_context.md +5 -3
  6. data/documentation/docs/api/browser_type.md +2 -0
  7. data/documentation/docs/api/credentials.md +6 -2
  8. data/documentation/docs/api/element_handle.md +7 -0
  9. data/documentation/docs/api/frame.md +20 -1
  10. data/documentation/docs/api/frame_locator.md +20 -0
  11. data/documentation/docs/api/locator.md +57 -0
  12. data/documentation/docs/api/locator_assertions.md +4 -2
  13. data/documentation/docs/api/page.md +26 -1
  14. data/documentation/docs/api/playwright.md +2 -2
  15. data/documentation/docs/api/tracing.md +3 -1
  16. data/documentation/docs/article/getting_started.md +12 -11
  17. data/documentation/docs/article/guides/download_playwright_driver.md +11 -32
  18. data/documentation/docs/article/guides/launch_browser.md +5 -4
  19. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +6 -5
  20. data/documentation/docs/article/guides/rails_integration.md +72 -4
  21. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +4 -3
  22. data/documentation/docs/article/guides/semi_automation.md +1 -1
  23. data/documentation/docs/article/guides/use_storage_state.md +1 -1
  24. data/documentation/docs/include/api_coverage.md +3 -0
  25. data/documentation/docusaurus.config.js +31 -0
  26. data/documentation/package.json +3 -0
  27. data/documentation/yarn.lock +159 -174
  28. data/lib/playwright/api_request_impl.rb +3 -1
  29. data/lib/playwright/api_response_impl.rb +16 -0
  30. data/lib/playwright/channel.rb +2 -1
  31. data/lib/playwright/channel_owners/browser.rb +1 -0
  32. data/lib/playwright/channel_owners/browser_context.rb +22 -3
  33. data/lib/playwright/channel_owners/element_handle.rb +14 -4
  34. data/lib/playwright/channel_owners/frame.rb +16 -2
  35. data/lib/playwright/channel_owners/page.rb +21 -9
  36. data/lib/playwright/channel_owners/tracing.rb +43 -13
  37. data/lib/playwright/connection.rb +9 -1
  38. data/lib/playwright/events.rb +2 -0
  39. data/lib/playwright/frame_locator_impl.rb +20 -5
  40. data/lib/playwright/locator_assertions_impl.rb +14 -1
  41. data/lib/playwright/locator_impl.rb +32 -5
  42. data/lib/playwright/screencast.rb +20 -6
  43. data/lib/playwright/screenshot_utils.rb +24 -0
  44. data/lib/playwright/test.rb +12 -2
  45. data/lib/playwright/url_matcher.rb +120 -4
  46. data/lib/playwright/utils.rb +12 -0
  47. data/lib/playwright/version.rb +2 -2
  48. data/lib/playwright.rb +2 -2
  49. data/lib/playwright_api/api_request_context.rb +3 -1
  50. data/lib/playwright_api/api_response.rb +9 -0
  51. data/lib/playwright_api/browser_context.rb +16 -14
  52. data/lib/playwright_api/browser_type.rb +2 -0
  53. data/lib/playwright_api/credentials.rb +6 -2
  54. data/lib/playwright_api/element_handle.rb +14 -7
  55. data/lib/playwright_api/frame.rb +28 -9
  56. data/lib/playwright_api/frame_locator.rb +20 -0
  57. data/lib/playwright_api/locator.rb +64 -13
  58. data/lib/playwright_api/locator_assertions.rb +6 -4
  59. data/lib/playwright_api/page.rb +46 -21
  60. data/lib/playwright_api/tracing.rb +4 -2
  61. data/playwright.gemspec +5 -0
  62. data/sig/playwright.rbs +49 -46
  63. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c45763ab237c198b8f2aa3afa3114ae0bf821481e57711516f5014547079f183
4
- data.tar.gz: ed8d42f43cb28c8ae2a45755332582b6e51ac993a751de94fb1a25fff43f5532
3
+ metadata.gz: 275e62d0e3c798dd08cdb59a52299af990f93b7fd5fe1cf2a1f9b1c5441a1680
4
+ data.tar.gz: 1711ed5665344b5c52b6f45c2621e03e2ac8983d698e6717303cad3bbb80659b
5
5
  SHA512:
6
- metadata.gz: 90ac4618f354ca98c4d5a5c7284b23d752a098bf7128a246076964381f36d93680b78c89599b15b175afdb7274e6cea1882cfab64f9d9eab6dcb1317b0f0f894
7
- data.tar.gz: 91cdfb64498f4e76f0f3a6f726fd49fa3d36dd7c6b692e843455bf8a9160a94216f13e324ffe90a2472c0b9ad573bd1d1ab241590629357d01562991ea299c63
6
+ metadata.gz: 9180d7ceb51f46c828e8a08aaffdd1e813bc57b842f268556990030a9003686841842cd8d3f5c98ea8b5c2ea3e6fdb63a4ff45afb6e44adb2b2a9a6c7c6ef861
7
+ data.tar.gz: 5efd8cd40f2e8b5d2cf2e22943ae6df4e3791b66874ca3cb0f72bbb66a1bd50442e8e481c990eed9fe958f4bfe82102e58634c5bfd2bc1823a0ff25b16036bbf
data/README.md CHANGED
@@ -12,25 +12,22 @@ gem 'playwright-ruby-client'
12
12
 
13
13
  and then 'bundle install'.
14
14
 
15
- Since playwright-ruby-client doesn't include the playwright driver, **we have to install [playwright](https://github.com/microsoft/playwright) in advance**.
15
+ Since playwright-ruby-client doesn't include Playwright, **we have to install Node.js and a compatible version of [playwright-core](https://www.npmjs.com/package/playwright-core) in advance**.
16
16
 
17
+ ```sh
18
+ PLAYWRIGHT_CLI_VERSION=$(bundle exec ruby -e 'require "playwright/version"; puts Playwright::COMPATIBLE_PLAYWRIGHT_VERSION')
19
+ npm install "playwright-core@$PLAYWRIGHT_CLI_VERSION"
20
+ ./node_modules/.bin/playwright-core install
17
21
  ```
18
- npm install playwright
19
- ./node_modules/.bin/playwright install
20
- ```
21
-
22
- And set `playwright_cli_executable_path: './node_modules/.bin/playwright'`
23
22
 
24
- **Prefer playwrighting without Node.js?**
25
-
26
- Instead of npm, you can also directly download playwright driver from playwright.azureedge.net/builds/. The URL can be easily detected from [here](https://github.com/microsoft/playwright-python/blob/cb5409934629adaabc0cff1891080de2052fa778/setup.py#L73-L77)
23
+ And set `playwright_cli_executable_path: './node_modules/.bin/playwright-core'`.
27
24
 
28
25
  ### Capture a site
29
26
 
30
27
  ```ruby
31
28
  require 'playwright'
32
29
 
33
- Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright|
30
+ Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright-core') do |playwright|
34
31
  playwright.chromium.launch(headless: false) do |browser|
35
32
  page = browser.new_page
36
33
  page.goto('https://github.com/YusukeIwaki')
@@ -46,7 +43,7 @@ end
46
43
  ```ruby
47
44
  require 'playwright'
48
45
 
49
- Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright|
46
+ Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright-core') do |playwright|
50
47
  playwright.chromium.launch(headless: false) do |browser|
51
48
  page = browser.new_page
52
49
  page.goto('https://github.com/')
@@ -94,7 +91,7 @@ $ bundle exec ruby main.rb
94
91
  ```ruby
95
92
  require 'playwright'
96
93
 
97
- Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright|
94
+ Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright-core') do |playwright|
98
95
  devices = playwright.android.devices
99
96
  unless devices.empty?
100
97
  device = devices.last
@@ -128,12 +125,10 @@ end
128
125
  We have to download android-driver for Playwright in advance.
129
126
 
130
127
  ```
131
- wget https://github.com/microsoft/playwright/raw/master/bin/android-driver-target.apk -O /path/to/playwright-driver/package/bin/android-driver-target.apk
132
- wget https://github.com/microsoft/playwright/raw/master/bin/android-driver.apk -O /path/to/playwright-driver/package/bin/android-driver.apk
128
+ wget https://github.com/microsoft/playwright/raw/master/bin/android-driver-target.apk -O ./node_modules/playwright-core/bin/android-driver-target.apk
129
+ wget https://github.com/microsoft/playwright/raw/master/bin/android-driver.apk -O ./node_modules/playwright-core/bin/android-driver.apk
133
130
  ```
134
131
 
135
- (If you downloaded Playwright via npm, replace `/path/to/playwright-driver/package/` with `./node_modules/playwright/` above.)
136
-
137
132
  ```ruby
138
133
  require 'playwright'
139
134
 
@@ -168,7 +163,7 @@ If your environment doesn't accept installing browser or creating browser proces
168
163
  For launching Playwright server, just execute:
169
164
 
170
165
  ```
171
- npx playwright install && npx playwright run-server --port 8080 --path /ws
166
+ ./node_modules/.bin/playwright-core install && ./node_modules/.bin/playwright-core run-server --port 8080 --path /ws
172
167
  ```
173
168
 
174
169
  and we can connect to the server with the code like this:
@@ -298,3 +298,6 @@ The method will populate request cookies from the context and update
298
298
  context cookies from the response. The method will automatically follow redirects.
299
299
 
300
300
  ## tracing
301
+
302
+
303
+ Tracing recorder for requests made through this API request context.
@@ -122,6 +122,18 @@ def text
122
122
 
123
123
  Returns the text representation of response body.
124
124
 
125
+ ## timing
126
+
127
+ ```
128
+ def timing
129
+ ```
130
+
131
+
132
+ Returns resource timing information for given response. For redirected requests, returns the information for the last
133
+ request in the redirect chain. When the response is served [from the HAR file](https://playwright.dev/python/docs/mock#replaying-from-har), timing
134
+ information is not available and all the values are -1. Find more information at
135
+ [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).
136
+
125
137
  ## url
126
138
 
127
139
  ```
@@ -434,11 +434,11 @@ alias: `offline=`
434
434
  ## storage_state
435
435
 
436
436
  ```
437
- def storage_state(indexedDB: nil, path: nil)
437
+ def storage_state(credentials: nil, indexedDB: nil, opfs: nil, path: nil)
438
438
  ```
439
439
 
440
440
 
441
- Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
441
+ Returns storage state for this browser context, contains current cookies, local storage snapshot, IndexedDB snapshot, origin private file system snapshot and virtual WebAuthn credentials.
442
442
 
443
443
  ## set_storage_state
444
444
 
@@ -448,7 +448,9 @@ def set_storage_state(storageState)
448
448
  alias: `storage_state=`
449
449
 
450
450
 
451
- Clears the existing cookies, local storage and IndexedDB entries for all origins and sets the new storage state.
451
+ Clears the existing cookies, local storage, IndexedDB entries, origin private file system entries and virtual WebAuthn credentials, and sets the new storage
452
+ state. When the storage state contains credentials, the virtual WebAuthn authenticator is installed (equivalent to
453
+ [Credentials#install](./credentials#install)), preventing all real authenticators from working in this context.
452
454
 
453
455
  **Usage**
454
456
 
@@ -59,6 +59,8 @@ The default browser context is accessible via [Browser#contexts](./browser#conte
59
59
 
60
60
  **NOTE**: This connection is significantly lower fidelity than the Playwright protocol connection via [BrowserType#connect](./browser_type#connect). If you are experiencing issues or attempting to use advanced functionality, you probably want to use [BrowserType#connect](./browser_type#connect).
61
61
 
62
+ **NOTE**: Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser.
63
+
62
64
  **Usage**
63
65
 
64
66
  ```ruby
@@ -9,7 +9,7 @@ sidebar_position: 10
9
9
  register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()`
10
10
  ceremonies in the page, without a real authenticator or hardware security key.
11
11
 
12
- There are two common ways to use it:
12
+ There are three common ways to use it:
13
13
 
14
14
  **Usage: seed a known credential**
15
15
 
@@ -31,7 +31,7 @@ page.goto("https://example.com/login")
31
31
  # The page's navigator.credentials.get() is answered with the seeded passkey.
32
32
  ```
33
33
 
34
- **Usage: capture a passkey, then reuse it**
34
+ **Usage: capture a credential, then reuse it**
35
35
 
36
36
  ```ruby
37
37
  # setup test: let the app register a passkey, then save it.
@@ -65,6 +65,10 @@ page.goto("https://example.com/login")
65
65
  # navigator.credentials.get() resolves the captured passkey - already signed in.
66
66
  ```
67
67
 
68
+ **Usage: save credentials in the storage state, restore later**
69
+
70
+ See [authentication guide](https://playwright.dev/python/docs/auth) for examples of using saving and resotring the storage state.
71
+
68
72
  **Defaults**
69
73
 
70
74
  ## install
@@ -75,6 +75,7 @@ def check(
75
75
  force: nil,
76
76
  noWaitAfter: nil,
77
77
  position: nil,
78
+ scroll: nil,
78
79
  timeout: nil,
79
80
  trial: nil)
80
81
  ```
@@ -103,6 +104,7 @@ def click(
103
104
  modifiers: nil,
104
105
  noWaitAfter: nil,
105
106
  position: nil,
107
+ scroll: nil,
106
108
  steps: nil,
107
109
  timeout: nil,
108
110
  trial: nil)
@@ -139,6 +141,7 @@ def dblclick(
139
141
  modifiers: nil,
140
142
  noWaitAfter: nil,
141
143
  position: nil,
144
+ scroll: nil,
142
145
  steps: nil,
143
146
  timeout: nil,
144
147
  trial: nil)
@@ -290,6 +293,7 @@ def hover(
290
293
  modifiers: nil,
291
294
  noWaitAfter: nil,
292
295
  position: nil,
296
+ scroll: nil,
293
297
  timeout: nil,
294
298
  trial: nil)
295
299
  ```
@@ -538,6 +542,7 @@ def set_checked(
538
542
  force: nil,
539
543
  noWaitAfter: nil,
540
544
  position: nil,
545
+ scroll: nil,
541
546
  timeout: nil,
542
547
  trial: nil)
543
548
  ```
@@ -578,6 +583,7 @@ def tap_point(
578
583
  modifiers: nil,
579
584
  noWaitAfter: nil,
580
585
  position: nil,
586
+ scroll: nil,
581
587
  timeout: nil,
582
588
  trial: nil)
583
589
  ```
@@ -630,6 +636,7 @@ def uncheck(
630
636
  force: nil,
631
637
  noWaitAfter: nil,
632
638
  position: nil,
639
+ scroll: nil,
633
640
  timeout: nil,
634
641
  trial: nil)
635
642
  ```
@@ -58,6 +58,7 @@ def check(
58
58
  force: nil,
59
59
  noWaitAfter: nil,
60
60
  position: nil,
61
+ scroll: nil,
61
62
  strict: nil,
62
63
  timeout: nil,
63
64
  trial: nil)
@@ -95,6 +96,7 @@ def click(
95
96
  modifiers: nil,
96
97
  noWaitAfter: nil,
97
98
  position: nil,
99
+ scroll: nil,
98
100
  strict: nil,
99
101
  timeout: nil,
100
102
  trial: nil)
@@ -131,6 +133,7 @@ def dblclick(
131
133
  modifiers: nil,
132
134
  noWaitAfter: nil,
133
135
  position: nil,
136
+ scroll: nil,
134
137
  strict: nil,
135
138
  timeout: nil,
136
139
  trial: nil)
@@ -202,6 +205,7 @@ def drag_and_drop(
202
205
  target,
203
206
  force: nil,
204
207
  noWaitAfter: nil,
208
+ scroll: nil,
205
209
  sourcePosition: nil,
206
210
  steps: nil,
207
211
  strict: nil,
@@ -387,13 +391,17 @@ puts frame == content_frame # => true
387
391
  ## frame_locator
388
392
 
389
393
  ```
390
- def frame_locator(selector)
394
+ def frame_locator(selector = nil)
391
395
  ```
392
396
 
393
397
 
394
398
  When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements
395
399
  in that iframe.
396
400
 
401
+ When called without `selector`, the search starts in this frame or in any of the iframes inside it,
402
+ so that you don't need to locate each iframe first. Note that the rest of the locator is resolved inside a single
403
+ frame, just like any other locator. If it matches elements inside multiple frames, an error is thrown.
404
+
397
405
  **Usage**
398
406
 
399
407
  Following snippet locates element with text "Submit" in the iframe with id `my-frame`, like `<iframe id="my-frame">`:
@@ -403,6 +411,13 @@ locator = frame.frame_locator("#my-iframe").get_by_text("Submit")
403
411
  locator.click
404
412
  ```
405
413
 
414
+ Following snippet locates a button, either in the frame or in one of the iframes inside it:
415
+
416
+ ```ruby
417
+ locator = frame.frame_locator.get_by_role('button')
418
+ locator.click
419
+ ```
420
+
406
421
  ## get_attribute
407
422
 
408
423
  ```
@@ -671,6 +686,7 @@ def hover(
671
686
  modifiers: nil,
672
687
  noWaitAfter: nil,
673
688
  position: nil,
689
+ scroll: nil,
674
690
  strict: nil,
675
691
  timeout: nil,
676
692
  trial: nil)
@@ -932,6 +948,7 @@ def set_checked(
932
948
  force: nil,
933
949
  noWaitAfter: nil,
934
950
  position: nil,
951
+ scroll: nil,
935
952
  strict: nil,
936
953
  timeout: nil,
937
954
  trial: nil)
@@ -987,6 +1004,7 @@ def tap_point(
987
1004
  modifiers: nil,
988
1005
  noWaitAfter: nil,
989
1006
  position: nil,
1007
+ scroll: nil,
990
1008
  strict: nil,
991
1009
  timeout: nil,
992
1010
  trial: nil)
@@ -1056,6 +1074,7 @@ def uncheck(
1056
1074
  force: nil,
1057
1075
  noWaitAfter: nil,
1058
1076
  position: nil,
1077
+ scroll: nil,
1059
1078
  strict: nil,
1060
1079
  timeout: nil,
1061
1080
  trial: nil)
@@ -24,6 +24,26 @@ page.locator('.result-frame').content_frame.get_by_role('button').click
24
24
  page.locator('.result-frame').first.content_frame.get_by_role('button').click
25
25
  ```
26
26
 
27
+ **Any frame**
28
+
29
+ Calling [Page#frame_locator](./page#frame_locator) or [Frame#frame_locator](./frame#frame_locator) without a selector creates a frame locator that
30
+ starts the search in any frame of the subtree - so that you don't
31
+ need to locate the iframe first.
32
+
33
+ ```ruby
34
+ # Finds the button in any frame on the page:
35
+ page.frame_locator.get_by_role('button').click
36
+
37
+ # Finds the iframe anywhere on the page and clicks the button inside it:
38
+ page.frame_locator.locator('#my-frame').content_frame.get_by_role('button').click
39
+ ```
40
+
41
+ Only the start of the search is affected - the rest of the locator is resolved inside a single frame, just like any
42
+ other locator. Following the strictness rules above, an error is thrown when elements are matched in multiple frames.
43
+
44
+ Such a frame locator does not point to a particular `iframe`, so [FrameLocator#owner](./frame_locator#owner),
45
+ [FrameLocator#first](./frame_locator#first), [FrameLocator#last](./frame_locator#last) and [FrameLocator#nth](./frame_locator#nth) are not supported on it.
46
+
27
47
  **Converting Locator to FrameLocator**
28
48
 
29
49
  If you have a [Locator](./locator) object pointing to an `iframe` it can be converted to [FrameLocator](./frame_locator) using [Locator#content_frame](./locator#content_frame).
@@ -179,6 +179,7 @@ def check(
179
179
  force: nil,
180
180
  noWaitAfter: nil,
181
181
  position: nil,
182
+ scroll: nil,
182
183
  timeout: nil,
183
184
  trial: nil)
184
185
  ```
@@ -238,6 +239,7 @@ def click(
238
239
  modifiers: nil,
239
240
  noWaitAfter: nil,
240
241
  position: nil,
242
+ scroll: nil,
241
243
  steps: nil,
242
244
  timeout: nil,
243
245
  trial: nil)
@@ -300,6 +302,7 @@ def dblclick(
300
302
  modifiers: nil,
301
303
  noWaitAfter: nil,
302
304
  position: nil,
305
+ scroll: nil,
303
306
  steps: nil,
304
307
  timeout: nil,
305
308
  trial: nil)
@@ -410,6 +413,7 @@ def drag_to(
410
413
  target,
411
414
  force: nil,
412
415
  noWaitAfter: nil,
416
+ scroll: nil,
413
417
  sourcePosition: nil,
414
418
  steps: nil,
415
419
  targetPosition: nil,
@@ -921,6 +925,7 @@ def hover(
921
925
  modifiers: nil,
922
926
  noWaitAfter: nil,
923
927
  position: nil,
928
+ scroll: nil,
924
929
  timeout: nil,
925
930
  trial: nil)
926
931
  ```
@@ -1367,6 +1372,7 @@ def set_checked(
1367
1372
  force: nil,
1368
1373
  noWaitAfter: nil,
1369
1374
  position: nil,
1375
+ scroll: nil,
1370
1376
  timeout: nil,
1371
1377
  trial: nil)
1372
1378
  ```
@@ -1438,6 +1444,7 @@ def tap_point(
1438
1444
  modifiers: nil,
1439
1445
  noWaitAfter: nil,
1440
1446
  position: nil,
1447
+ scroll: nil,
1441
1448
  timeout: nil,
1442
1449
  trial: nil)
1443
1450
  ```
@@ -1496,6 +1503,7 @@ def uncheck(
1496
1503
  force: nil,
1497
1504
  noWaitAfter: nil,
1498
1505
  position: nil,
1506
+ scroll: nil,
1499
1507
  timeout: nil,
1500
1508
  trial: nil)
1501
1509
  ```
@@ -1523,6 +1531,34 @@ If the element is detached from the DOM at any moment during the action, this me
1523
1531
  When all steps combined have not finished during the specified `timeout`, this method throws a
1524
1532
  `TimeoutError`. Passing zero timeout disables this.
1525
1533
 
1534
+ ## visible
1535
+
1536
+ ```
1537
+ def visible
1538
+ ```
1539
+
1540
+
1541
+ Returns a locator that matches only [visible](https://playwright.dev/python/docs/actionability#visible) elements, ignoring the invisible ones. This is the recommended way to distinguish elements by visibility, as opposed to the `:visible` CSS pseudo-class.
1542
+
1543
+ Note that visibility is checked every time the locator is used, and not at the moment of the [Locator#visible](./locator#visible) call.
1544
+
1545
+ **Usage**
1546
+
1547
+ Consider a page with two buttons, the first invisible and the second visible.
1548
+
1549
+ ```html
1550
+ <button style='display: none'>Invisible</button>
1551
+ <button>Visible</button>
1552
+ ```
1553
+
1554
+ This will only find the second button, because it is visible, and then click it.
1555
+
1556
+ ```ruby
1557
+ page.locator('button').visible.click
1558
+ ```
1559
+
1560
+ To match invisible elements instead, use [Locator#filter](./locator#filter) with the `visible` option set to `false`.
1561
+
1526
1562
  ## wait_for
1527
1563
 
1528
1564
  ```
@@ -1541,3 +1577,24 @@ If target element already satisfies the condition, the method returns immediatel
1541
1577
  order_sent = page.locator("#order-sent")
1542
1578
  order_sent.wait_for
1543
1579
  ```
1580
+
1581
+ ## wait_for_function
1582
+
1583
+ ```
1584
+ def wait_for_function(expression, arg: nil, timeout: nil)
1585
+ ```
1586
+
1587
+
1588
+ Returns when `expression` returns a truthy value, called with the matching element as a first argument, and `arg` as a second argument.
1589
+
1590
+ This is a generic way to wait for an element to reach a custom condition without asserting it. The locator is re-resolved on each retry, so it tolerates the element being re-rendered while waiting.
1591
+
1592
+ If `expression` returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), this method will wait for the promise to resolve before checking its value.
1593
+
1594
+ If `expression` throws or rejects, this method throws.
1595
+
1596
+ **Usage**
1597
+
1598
+ Wait for an attribute to appear:
1599
+
1600
+ Passing argument to `expression`:
@@ -164,7 +164,7 @@ The opposite of [LocatorAssertions#to_have_accessible_name](./locator_assertions
164
164
  ## not_to_have_attribute
165
165
 
166
166
  ```ruby
167
- expect(locator).not_to have_attribute(name, value, ignoreCase: nil, timeout: nil)
167
+ expect(locator).not_to have_attribute(name, value: nil, ignoreCase: nil, timeout: nil)
168
168
  ```
169
169
 
170
170
 
@@ -587,7 +587,7 @@ expect(locator).to have_accessible_name("Save to disk")
587
587
  ## to_have_attribute
588
588
 
589
589
  ```ruby
590
- expect(locator).to have_attribute(name, value, ignoreCase: nil, timeout: nil)
590
+ expect(locator).to have_attribute(name, value: nil, ignoreCase: nil, timeout: nil)
591
591
  ```
592
592
 
593
593
 
@@ -598,6 +598,8 @@ Ensures the [Locator](./locator) points to an element with given attribute.
598
598
  ```ruby
599
599
  locator = page.locator("input")
600
600
  expect(locator).to have_attribute("type", "text")
601
+ expect(locator).to have_attribute("disabled")
602
+ expect(locator).not_to have_attribute("readonly")
601
603
  ```
602
604
 
603
605
  ## to_have_class
@@ -112,6 +112,7 @@ def check(
112
112
  force: nil,
113
113
  noWaitAfter: nil,
114
114
  position: nil,
115
+ scroll: nil,
115
116
  strict: nil,
116
117
  timeout: nil,
117
118
  trial: nil)
@@ -141,6 +142,7 @@ def click(
141
142
  modifiers: nil,
142
143
  noWaitAfter: nil,
143
144
  position: nil,
145
+ scroll: nil,
144
146
  strict: nil,
145
147
  timeout: nil,
146
148
  trial: nil)
@@ -201,6 +203,7 @@ def dblclick(
201
203
  modifiers: nil,
202
204
  noWaitAfter: nil,
203
205
  position: nil,
206
+ scroll: nil,
204
207
  strict: nil,
205
208
  timeout: nil,
206
209
  trial: nil)
@@ -275,6 +278,7 @@ def drag_and_drop(
275
278
  target,
276
279
  force: nil,
277
280
  noWaitAfter: nil,
281
+ scroll: nil,
278
282
  sourcePosition: nil,
279
283
  steps: nil,
280
284
  strict: nil,
@@ -581,13 +585,17 @@ frame = page.frame(url: /.*domain.*/)
581
585
  ## frame_locator
582
586
 
583
587
  ```
584
- def frame_locator(selector)
588
+ def frame_locator(selector = nil)
585
589
  ```
586
590
 
587
591
 
588
592
  When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements
589
593
  in that iframe.
590
594
 
595
+ When called without `selector`, the search starts in any frame on the page - the main frame or any of
596
+ the iframes - so that you don't need to locate each iframe first. Note that the rest of the locator is resolved
597
+ inside a single frame, just like any other locator. If it matches elements inside multiple frames, an error is thrown.
598
+
591
599
  **Usage**
592
600
 
593
601
  Following snippet locates element with text "Submit" in the iframe with id `my-frame`,
@@ -598,6 +606,13 @@ locator = page.frame_locator("#my-iframe").get_by_text("Submit")
598
606
  locator.click
599
607
  ```
600
608
 
609
+ Following snippet locates a button, either in the main frame or in one of the iframes:
610
+
611
+ ```ruby
612
+ locator = page.frame_locator.get_by_role('button')
613
+ locator.click
614
+ ```
615
+
601
616
  ## frames
602
617
 
603
618
  ```
@@ -851,6 +866,9 @@ last redirect. If cannot go back, returns `null`.
851
866
 
852
867
  Navigate to the previous page in history.
853
868
 
869
+ **NOTE**: **Testing Back/Forward Cache (BFCache) is not supported.**
870
+ By default, Playwright disables the Back/Forward Cache across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events. Because BFCache restores unfreeze the DOM without firing these events, using `page.goBack()` or `page.goForward()` to trigger a BFCache restore will result in timeouts and a desynchronized [Page](./page) state.
871
+
854
872
  ## go_forward
855
873
 
856
874
  ```
@@ -863,6 +881,9 @@ last redirect. If cannot go forward, returns `null`.
863
881
 
864
882
  Navigate to the next page in history.
865
883
 
884
+ **NOTE**: **Testing Back/Forward Cache (BFCache) is not supported.**
885
+ By default, Playwright disables the Back/Forward Cache across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events. Because BFCache restores unfreeze the DOM without firing these events, using `page.goBack()` or `page.goForward()` to trigger a BFCache restore will result in timeouts and a desynchronized [Page](./page) state.
886
+
866
887
  ## goto
867
888
 
868
889
  ```
@@ -908,6 +929,7 @@ def hover(
908
929
  modifiers: nil,
909
930
  noWaitAfter: nil,
910
931
  position: nil,
932
+ scroll: nil,
911
933
  strict: nil,
912
934
  timeout: nil,
913
935
  trial: nil)
@@ -1416,6 +1438,7 @@ def set_checked(
1416
1438
  force: nil,
1417
1439
  noWaitAfter: nil,
1418
1440
  position: nil,
1441
+ scroll: nil,
1419
1442
  strict: nil,
1420
1443
  timeout: nil,
1421
1444
  trial: nil)
@@ -1546,6 +1569,7 @@ def tap_point(
1546
1569
  modifiers: nil,
1547
1570
  noWaitAfter: nil,
1548
1571
  position: nil,
1572
+ scroll: nil,
1549
1573
  strict: nil,
1550
1574
  timeout: nil,
1551
1575
  trial: nil)
@@ -1615,6 +1639,7 @@ def uncheck(
1615
1639
  force: nil,
1616
1640
  noWaitAfter: nil,
1617
1641
  position: nil,
1642
+ scroll: nil,
1618
1643
  strict: nil,
1619
1644
  timeout: nil,
1620
1645
  trial: nil)
@@ -11,7 +11,7 @@ to drive automation:
11
11
  ```ruby
12
12
  require 'playwright'
13
13
 
14
- Playwright.create(playwright_cli_executable_path: 'npx playwright') do |playwright|
14
+ Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright-core') do |playwright|
15
15
  chromium = playwright.chromium # or "firefox" or "webkit".
16
16
  chromium.launch do |browser|
17
17
  page = browser.new_page
@@ -36,7 +36,7 @@ Returns a dictionary of devices to be used with [Browser#new_context](./browser#
36
36
  ```ruby
37
37
  require 'playwright'
38
38
 
39
- Playwright.create(playwright_cli_executable_path: 'npx playwright') do |playwright|
39
+ Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright-core') do |playwright|
40
40
  iphone = playwright.devices["iPhone 6"]
41
41
  playwright.webkit.launch do |browser|
42
42
  context = browser.new_context(**iphone)
@@ -26,9 +26,11 @@ end
26
26
 
27
27
  ```
28
28
  def start(
29
+ ariaSnapshots: nil,
29
30
  live: nil,
30
31
  name: nil,
31
32
  screenshots: nil,
33
+ screenSnapshots: nil,
32
34
  snapshots: nil,
33
35
  sources: nil,
34
36
  title: nil)
@@ -86,7 +88,7 @@ def start_har(path, content: nil, mode: nil, urlFilter: nil)
86
88
 
87
89
  Start recording a HAR (HTTP Archive) of network activity in this context. The HAR file is written to disk when [Tracing#stop_har](./tracing#stop_har) is called, or when the returned `Disposable` is disposed.
88
90
 
89
- Only one HAR recording can be active at a time per [BrowserContext](./browser_context).
91
+ Only one HAR recording can be active at a time per [Tracing](./tracing) instance.
90
92
 
91
93
  **Usage**
92
94