playwright-ruby-client 1.60.0 → 1.62.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -17
  3. data/documentation/docs/api/api_response.md +30 -0
  4. data/documentation/docs/api/browser_context.md +11 -3
  5. data/documentation/docs/api/browser_type.md +3 -0
  6. data/documentation/docs/api/credentials.md +136 -0
  7. data/documentation/docs/api/element_handle.md +7 -0
  8. data/documentation/docs/api/frame.md +12 -4
  9. data/documentation/docs/api/locator.md +29 -0
  10. data/documentation/docs/api/locator_assertions.md +4 -2
  11. data/documentation/docs/api/page.md +26 -2
  12. data/documentation/docs/api/playwright.md +2 -2
  13. data/documentation/docs/api/touchscreen.md +1 -1
  14. data/documentation/docs/api/web_storage.md +65 -0
  15. data/documentation/docs/article/getting_started.md +12 -11
  16. data/documentation/docs/article/guides/download_playwright_driver.md +11 -32
  17. data/documentation/docs/article/guides/launch_browser.md +5 -4
  18. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +6 -5
  19. data/documentation/docs/article/guides/rails_integration.md +72 -4
  20. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +4 -3
  21. data/documentation/docs/article/guides/semi_automation.md +1 -1
  22. data/documentation/docs/article/guides/use_storage_state.md +1 -1
  23. data/documentation/docs/include/api_coverage.md +22 -0
  24. data/documentation/docusaurus.config.js +31 -0
  25. data/documentation/package.json +3 -0
  26. data/documentation/yarn.lock +159 -174
  27. data/lib/playwright/api_response_impl.rb +24 -0
  28. data/lib/playwright/channel_owners/browser_context.rb +8 -3
  29. data/lib/playwright/channel_owners/browser_type.rb +2 -1
  30. data/lib/playwright/channel_owners/element_handle.rb +14 -4
  31. data/lib/playwright/channel_owners/frame.rb +50 -17
  32. data/lib/playwright/channel_owners/page.rb +31 -10
  33. data/lib/playwright/connection.rb +16 -2
  34. data/lib/playwright/credentials_impl.rb +35 -0
  35. data/lib/playwright/errors.rb +4 -1
  36. data/lib/playwright/locator_assertions_impl.rb +14 -1
  37. data/lib/playwright/locator_impl.rb +28 -5
  38. data/lib/playwright/locator_utils.rb +9 -1
  39. data/lib/playwright/screencast.rb +22 -6
  40. data/lib/playwright/screenshot_utils.rb +24 -0
  41. data/lib/playwright/test.rb +12 -2
  42. data/lib/playwright/url_matcher.rb +120 -4
  43. data/lib/playwright/version.rb +2 -2
  44. data/lib/playwright/waiter.rb +9 -41
  45. data/lib/playwright/web_storage_impl.rb +34 -0
  46. data/lib/playwright.rb +2 -2
  47. data/lib/playwright_api/api_request_context.rb +6 -6
  48. data/lib/playwright_api/api_response.rb +21 -0
  49. data/lib/playwright_api/browser.rb +6 -6
  50. data/lib/playwright_api/browser_context.rb +27 -18
  51. data/lib/playwright_api/browser_type.rb +10 -7
  52. data/lib/playwright_api/cdp_session.rb +6 -6
  53. data/lib/playwright_api/credentials.rb +124 -0
  54. data/lib/playwright_api/dialog.rb +6 -6
  55. data/lib/playwright_api/element_handle.rb +20 -13
  56. data/lib/playwright_api/frame.rb +36 -28
  57. data/lib/playwright_api/js_handle.rb +6 -6
  58. data/lib/playwright_api/locator.rb +37 -11
  59. data/lib/playwright_api/locator_assertions.rb +6 -4
  60. data/lib/playwright_api/page.rb +55 -29
  61. data/lib/playwright_api/playwright.rb +6 -6
  62. data/lib/playwright_api/request.rb +6 -6
  63. data/lib/playwright_api/response.rb +6 -6
  64. data/lib/playwright_api/route.rb +6 -6
  65. data/lib/playwright_api/touchscreen.rb +1 -1
  66. data/lib/playwright_api/tracing.rb +6 -6
  67. data/lib/playwright_api/web_socket.rb +6 -6
  68. data/lib/playwright_api/web_storage.rb +48 -0
  69. data/lib/playwright_api/worker.rb +6 -6
  70. data/playwright.gemspec +5 -0
  71. data/sig/playwright.rbs +62 -40
  72. metadata +13 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac9256b441d66c5a19585735b23567b6dc1eedca695812ee3388f48918054970
4
- data.tar.gz: 9a13c1dcfc540d7bc1120743af1806a338185a2bd6eb41331e2273425af790c6
3
+ metadata.gz: 7c78e6a1f2ee45aadb2b8a027d08a0264560172b843419e23376c80f74095ccd
4
+ data.tar.gz: b31e8c1c972865e68312f65ee23a821535f14592affadcc65580b10169819b81
5
5
  SHA512:
6
- metadata.gz: f8fae6bb376c4586a90464a2db4d06b3362a822d191456dcf5ad75f74ae67ed99462403aa4cdca4d817eb58d59ec30380a9b72c5d6cee2f5475a0fda4e2c05a8
7
- data.tar.gz: b59cc6913b92826850611eab5967f72c2545549f2ac83c8268d59d6ef61152523a1fcb62759278487661369449a817222ecd514a0c066ea913b3e4607a4b0463
6
+ metadata.gz: de6ea998c746c0fa3107850695144b7204105c01daa6209f646c2a172bd59eb906e169b4b7ed7dca436e8084611496def05fc81bb23134d8475bedbc480d6fd5
7
+ data.tar.gz: 907612d4a87117af9d0c57ada0ec4144f7451f7a75176b2b93d3cf896ff6ce8492f7e72ff8a2c04ce1d5d2871fdf9af2f967de9e5175010574d09c276b160300
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:
@@ -77,6 +77,24 @@ def ok
77
77
 
78
78
  Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
79
79
 
80
+ ## security_details
81
+
82
+ ```
83
+ def security_details
84
+ ```
85
+
86
+
87
+ Returns SSL and other security information. Resolves to `null` for non-HTTPS responses. For redirected requests, returns the information for the last request in the redirect chain.
88
+
89
+ ## server_addr
90
+
91
+ ```
92
+ def server_addr
93
+ ```
94
+
95
+
96
+ Returns the IP address and port of the server. Resolves to `null` if the server address is not available. For redirected requests, returns the information for the last request in the redirect chain.
97
+
80
98
  ## status
81
99
 
82
100
  ```
@@ -104,6 +122,18 @@ def text
104
122
 
105
123
  Returns the text representation of response body.
106
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
+
107
137
  ## url
108
138
 
109
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, 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 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 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
 
@@ -521,6 +523,12 @@ Will throw an error if the context closes before new [Page](./page) is created.
521
523
 
522
524
  Playwright has ability to mock clock and passage of time.
523
525
 
526
+ ## credentials
527
+
528
+
529
+ Virtual WebAuthn authenticator for this context. Lets tests seed credentials and intercept
530
+ `navigator.credentials.create()` / `navigator.credentials.get()` ceremonies.
531
+
524
532
  ## request
525
533
 
526
534
 
@@ -41,6 +41,7 @@ This method attaches Playwright to an existing browser instance created via `Bro
41
41
  ```
42
42
  def connect_over_cdp(
43
43
  endpointURL,
44
+ artifactsDir: nil,
44
45
  headers: nil,
45
46
  isLocal: nil,
46
47
  noDefaults: nil,
@@ -58,6 +59,8 @@ The default browser context is accessible via [Browser#contexts](./browser#conte
58
59
 
59
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).
60
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
+
61
64
  **Usage**
62
65
 
63
66
  ```ruby
@@ -0,0 +1,136 @@
1
+ ---
2
+ sidebar_position: 10
3
+ ---
4
+
5
+ # Credentials
6
+
7
+
8
+ [Credentials](./credentials) is a virtual WebAuthn authenticator scoped to a [BrowserContext](./browser_context). It lets tests
9
+ register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()`
10
+ ceremonies in the page, without a real authenticator or hardware security key.
11
+
12
+ There are three common ways to use it:
13
+
14
+ **Usage: seed a known credential**
15
+
16
+ ```ruby
17
+ context = browser.new_context
18
+
19
+ # A passkey your backend already provisioned for a test user.
20
+ context.credentials.create(
21
+ "example.com",
22
+ id: known_credential_id, # base64url
23
+ userHandle: known_user_handle, # base64url
24
+ privateKey: known_private_key, # base64url PKCS#8 (DER)
25
+ publicKey: known_public_key, # base64url SPKI (DER)
26
+ )
27
+ context.credentials.install
28
+
29
+ page = context.new_page
30
+ page.goto("https://example.com/login")
31
+ # The page's navigator.credentials.get() is answered with the seeded passkey.
32
+ ```
33
+
34
+ **Usage: capture a credential, then reuse it**
35
+
36
+ ```ruby
37
+ # setup test: let the app register a passkey, then save it.
38
+ context = browser.new_context
39
+ context.credentials.install
40
+
41
+ page = context.new_page
42
+ page.goto("https://example.com/register")
43
+ page.get_by_role("button", name: "Create a passkey").click
44
+
45
+ # Read back the passkey the page registered - it includes the private key.
46
+ credential = context.credentials.get(rpId: "example.com").first
47
+ File.write("playwright/.auth/passkey.json", JSON.generate(credential))
48
+ ```
49
+
50
+ ```ruby
51
+ # later test: seed the captured passkey so the app starts already enrolled.
52
+ credential = JSON.parse(File.read("playwright/.auth/passkey.json"))
53
+ context = browser.new_context
54
+ context.credentials.create(
55
+ credential["rpId"],
56
+ id: credential["id"],
57
+ userHandle: credential["userHandle"],
58
+ privateKey: credential["privateKey"],
59
+ publicKey: credential["publicKey"],
60
+ )
61
+ context.credentials.install
62
+
63
+ page = context.new_page
64
+ page.goto("https://example.com/login")
65
+ # navigator.credentials.get() resolves the captured passkey - already signed in.
66
+ ```
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
+
72
+ **Defaults**
73
+
74
+ ## install
75
+
76
+ ```
77
+ def install
78
+ ```
79
+
80
+
81
+ Installs the virtual WebAuthn authenticator into the context, overriding
82
+ `navigator.credentials.create()` and `navigator.credentials.get()` in all current
83
+ and future pages. Call this before the page first touches `navigator.credentials`.
84
+
85
+ Required: until [Credentials#install](./credentials#install) is called, no interception is in place and the page sees
86
+ the platform's native (or absent) WebAuthn behaviour. Seeding credentials with
87
+ [Credentials#create](./credentials#create) without installing populates the authenticator, but the
88
+ page will never see those credentials.
89
+
90
+ ## create
91
+
92
+ ```
93
+ def create(
94
+ rpId,
95
+ id: nil,
96
+ privateKey: nil,
97
+ publicKey: nil,
98
+ userHandle: nil)
99
+ ```
100
+
101
+
102
+ Seeds a virtual WebAuthn credential and returns it.
103
+
104
+ With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The
105
+ seeded credential is discoverable (resident), so the page can resolve it from both
106
+ username-then-passkey and usernameless passkey flows. The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test.
107
+
108
+ To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and
109
+ `publicKey` together.
110
+
111
+ Call [Credentials#install](./credentials#install) before navigating to a page that uses WebAuthn.
112
+
113
+ ## delete
114
+
115
+ ```
116
+ def delete(id)
117
+ ```
118
+
119
+
120
+ Removes a credential from the authenticator by its id. Works for any credential currently held —
121
+ both those seeded with [Credentials#create](./credentials#create) and those the page registered itself by
122
+ calling `navigator.credentials.create()`.
123
+
124
+ ## get
125
+
126
+ ```
127
+ def get(id: nil, rpId: nil)
128
+ ```
129
+
130
+
131
+ Returns every credential currently held by the authenticator, optionally filtered by `rpId` or
132
+ `id`. This includes both credentials seeded with [Credentials#create](./credentials#create) and credentials
133
+ the page registered itself by calling `navigator.credentials.create()`.
134
+
135
+ Each returned credential includes its private and public keys, so a passkey the app just
136
+ registered can be saved and re-seeded into a later test with [Credentials#create](./credentials#create) — see the second example in the class overview.
@@ -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,
@@ -291,7 +295,7 @@ puts frame.evaluate("1 + #{x}") # => "11"
291
295
  [ElementHandle](./element_handle) instances can be passed as an argument to the [Frame#evaluate](./frame#evaluate):
292
296
 
293
297
  ```ruby
294
- body_handle = frame.query_selector("body")
298
+ body_handle = frame.evaluate_handle("document.body")
295
299
  html = frame.evaluate("([body, suffix]) => body.innerHTML + suffix", arg: [body_handle, "hello"])
296
300
  body_handle.dispose
297
301
  ```
@@ -321,14 +325,14 @@ a_window_handle # handle for the window object.
321
325
  A string can also be passed in instead of a function.
322
326
 
323
327
  ```ruby
324
- a_handle = page.evaluate_handle("document") # handle for the "document"
328
+ a_handle = frame.evaluate_handle("document") # handle for the "document"
325
329
  ```
326
330
 
327
331
  [JSHandle](./js_handle) instances can be passed as an argument to the [Frame#evaluate_handle](./frame#evaluate_handle):
328
332
 
329
333
  ```ruby
330
- body_handle = page.evaluate_handle("document.body")
331
- result_handle = page.evaluate_handle("body => body.innerHTML", arg: body_handle)
334
+ a_handle = frame.evaluate_handle("document.body")
335
+ result_handle = frame.evaluate_handle("body => body.innerHTML", arg: a_handle)
332
336
  puts result_handle.json_value
333
337
  result_handle.dispose
334
338
  ```
@@ -671,6 +675,7 @@ def hover(
671
675
  modifiers: nil,
672
676
  noWaitAfter: nil,
673
677
  position: nil,
678
+ scroll: nil,
674
679
  strict: nil,
675
680
  timeout: nil,
676
681
  trial: nil)
@@ -932,6 +937,7 @@ def set_checked(
932
937
  force: nil,
933
938
  noWaitAfter: nil,
934
939
  position: nil,
940
+ scroll: nil,
935
941
  strict: nil,
936
942
  timeout: nil,
937
943
  trial: nil)
@@ -987,6 +993,7 @@ def tap_point(
987
993
  modifiers: nil,
988
994
  noWaitAfter: nil,
989
995
  position: nil,
996
+ scroll: nil,
990
997
  strict: nil,
991
998
  timeout: nil,
992
999
  trial: nil)
@@ -1056,6 +1063,7 @@ def uncheck(
1056
1063
  force: nil,
1057
1064
  noWaitAfter: nil,
1058
1065
  position: nil,
1066
+ scroll: nil,
1059
1067
  strict: nil,
1060
1068
  timeout: nil,
1061
1069
  trial: nil)
@@ -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
  ```
@@ -1541,3 +1549,24 @@ If target element already satisfies the condition, the method returns immediatel
1541
1549
  order_sent = page.locator("#order-sent")
1542
1550
  order_sent.wait_for
1543
1551
  ```
1552
+
1553
+ ## wait_for_function
1554
+
1555
+ ```
1556
+ def wait_for_function(expression, arg: nil, timeout: nil)
1557
+ ```
1558
+
1559
+
1560
+ Returns when `expression` returns a truthy value, called with the matching element as a first argument, and `arg` as a second argument.
1561
+
1562
+ 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.
1563
+
1564
+ 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.
1565
+
1566
+ If `expression` throws or rejects, this method throws.
1567
+
1568
+ **Usage**
1569
+
1570
+ Wait for an attribute to appear:
1571
+
1572
+ 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,
@@ -413,7 +417,7 @@ puts page.evaluate("1 + #{x}") # => "11"
413
417
  [ElementHandle](./element_handle) instances can be passed as an argument to the [Page#evaluate](./page#evaluate):
414
418
 
415
419
  ```ruby
416
- body_handle = page.query_selector("body")
420
+ body_handle = page.evaluate_handle("document.body")
417
421
  html = page.evaluate("([body, suffix]) => body.innerHTML + suffix", arg: [body_handle, "hello"])
418
422
  body_handle.dispose
419
423
  ```
@@ -851,6 +855,9 @@ last redirect. If cannot go back, returns `null`.
851
855
 
852
856
  Navigate to the previous page in history.
853
857
 
858
+ **NOTE**: **Testing Back/Forward Cache (BFCache) is not supported.**
859
+ 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.
860
+
854
861
  ## go_forward
855
862
 
856
863
  ```
@@ -863,6 +870,9 @@ last redirect. If cannot go forward, returns `null`.
863
870
 
864
871
  Navigate to the next page in history.
865
872
 
873
+ **NOTE**: **Testing Back/Forward Cache (BFCache) is not supported.**
874
+ 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.
875
+
866
876
  ## goto
867
877
 
868
878
  ```
@@ -908,6 +918,7 @@ def hover(
908
918
  modifiers: nil,
909
919
  noWaitAfter: nil,
910
920
  position: nil,
921
+ scroll: nil,
911
922
  strict: nil,
912
923
  timeout: nil,
913
924
  trial: nil)
@@ -1416,6 +1427,7 @@ def set_checked(
1416
1427
  force: nil,
1417
1428
  noWaitAfter: nil,
1418
1429
  position: nil,
1430
+ scroll: nil,
1419
1431
  strict: nil,
1420
1432
  timeout: nil,
1421
1433
  trial: nil)
@@ -1546,6 +1558,7 @@ def tap_point(
1546
1558
  modifiers: nil,
1547
1559
  noWaitAfter: nil,
1548
1560
  position: nil,
1561
+ scroll: nil,
1549
1562
  strict: nil,
1550
1563
  timeout: nil,
1551
1564
  trial: nil)
@@ -1561,7 +1574,7 @@ This method taps an element matching `selector` by performing the following step
1561
1574
  When all steps combined have not finished during the specified `timeout`, this method throws a
1562
1575
  `TimeoutError`. Passing zero timeout disables this.
1563
1576
 
1564
- **NOTE**: [Page#tap_point](./page#tap_point) the method will throw if `hasTouch` option of the browser context is false.
1577
+ **NOTE**: [Page#tap_point](./page#tap_point) will throw if the `hasTouch` option of the browser context is false.
1565
1578
 
1566
1579
  ## text_content
1567
1580
 
@@ -1615,6 +1628,7 @@ def uncheck(
1615
1628
  force: nil,
1616
1629
  noWaitAfter: nil,
1617
1630
  position: nil,
1631
+ scroll: nil,
1618
1632
  strict: nil,
1619
1633
  timeout: nil,
1620
1634
  trial: nil)
@@ -2001,6 +2015,16 @@ Playwright has ability to mock clock and passage of time.
2001
2015
 
2002
2016
  ## keyboard
2003
2017
 
2018
+ ## local_storage
2019
+
2020
+
2021
+ Provides access to the page's `localStorage` for the current origin. See [WebStorage](./web_storage).
2022
+
2023
+ ## session_storage
2024
+
2025
+
2026
+ Provides access to the page's `sessionStorage` for the current origin. See [WebStorage](./web_storage).
2027
+
2004
2028
  ## mouse
2005
2029
 
2006
2030
  ## request