playwright-ruby-client 1.60.0 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/documentation/docs/api/api_response.md +18 -0
- data/documentation/docs/api/browser_context.md +6 -0
- data/documentation/docs/api/browser_type.md +1 -0
- data/documentation/docs/api/credentials.md +132 -0
- data/documentation/docs/api/frame.md +4 -4
- data/documentation/docs/api/page.md +12 -2
- data/documentation/docs/api/touchscreen.md +1 -1
- data/documentation/docs/api/web_storage.md +65 -0
- data/documentation/docs/include/api_coverage.md +20 -0
- data/lib/playwright/api_response_impl.rb +8 -0
- data/lib/playwright/channel_owners/browser_context.rb +2 -1
- data/lib/playwright/channel_owners/browser_type.rb +2 -1
- data/lib/playwright/channel_owners/frame.rb +36 -17
- data/lib/playwright/channel_owners/page.rb +12 -2
- data/lib/playwright/connection.rb +7 -1
- data/lib/playwright/credentials_impl.rb +35 -0
- data/lib/playwright/errors.rb +4 -1
- data/lib/playwright/locator_utils.rb +9 -1
- data/lib/playwright/screencast.rb +3 -1
- data/lib/playwright/version.rb +2 -2
- data/lib/playwright/waiter.rb +9 -41
- data/lib/playwright/web_storage_impl.rb +34 -0
- data/lib/playwright_api/api_request_context.rb +6 -6
- data/lib/playwright_api/api_response.rb +12 -0
- data/lib/playwright_api/browser.rb +6 -6
- data/lib/playwright_api/browser_context.rb +23 -16
- data/lib/playwright_api/browser_type.rb +8 -7
- data/lib/playwright_api/cdp_session.rb +6 -6
- data/lib/playwright_api/credentials.rb +120 -0
- data/lib/playwright_api/dialog.rb +6 -6
- data/lib/playwright_api/element_handle.rb +6 -6
- data/lib/playwright_api/frame.rb +14 -14
- data/lib/playwright_api/js_handle.rb +6 -6
- data/lib/playwright_api/locator.rb +5 -5
- data/lib/playwright_api/page.rb +32 -20
- data/lib/playwright_api/playwright.rb +6 -6
- data/lib/playwright_api/request.rb +8 -8
- data/lib/playwright_api/response.rb +6 -6
- data/lib/playwright_api/route.rb +6 -6
- data/lib/playwright_api/touchscreen.rb +1 -1
- data/lib/playwright_api/tracing.rb +6 -6
- data/lib/playwright_api/web_socket.rb +6 -6
- data/lib/playwright_api/web_storage.rb +48 -0
- data/lib/playwright_api/worker.rb +8 -8
- data/sig/playwright.rbs +21 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c45763ab237c198b8f2aa3afa3114ae0bf821481e57711516f5014547079f183
|
|
4
|
+
data.tar.gz: ed8d42f43cb28c8ae2a45755332582b6e51ac993a751de94fb1a25fff43f5532
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90ac4618f354ca98c4d5a5c7284b23d752a098bf7128a246076964381f36d93680b78c89599b15b175afdb7274e6cea1882cfab64f9d9eab6dcb1317b0f0f894
|
|
7
|
+
data.tar.gz: 91cdfb64498f4e76f0f3a6f726fd49fa3d36dd7c6b692e843455bf8a9160a94216f13e324ffe90a2472c0b9ad573bd1d1ab241590629357d01562991ea299c63
|
|
@@ -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
|
```
|
|
@@ -521,6 +521,12 @@ Will throw an error if the context closes before new [Page](./page) is created.
|
|
|
521
521
|
|
|
522
522
|
Playwright has ability to mock clock and passage of time.
|
|
523
523
|
|
|
524
|
+
## credentials
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
Virtual WebAuthn authenticator for this context. Lets tests seed credentials and intercept
|
|
528
|
+
`navigator.credentials.create()` / `navigator.credentials.get()` ceremonies.
|
|
529
|
+
|
|
524
530
|
## request
|
|
525
531
|
|
|
526
532
|
|
|
@@ -0,0 +1,132 @@
|
|
|
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 two 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 passkey, 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
|
+
**Defaults**
|
|
69
|
+
|
|
70
|
+
## install
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
def install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
Installs the virtual WebAuthn authenticator into the context, overriding
|
|
78
|
+
`navigator.credentials.create()` and `navigator.credentials.get()` in all current
|
|
79
|
+
and future pages. Call this before the page first touches `navigator.credentials`.
|
|
80
|
+
|
|
81
|
+
Required: until [Credentials#install](./credentials#install) is called, no interception is in place and the page sees
|
|
82
|
+
the platform's native (or absent) WebAuthn behaviour. Seeding credentials with
|
|
83
|
+
[Credentials#create](./credentials#create) without installing populates the authenticator, but the
|
|
84
|
+
page will never see those credentials.
|
|
85
|
+
|
|
86
|
+
## create
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
def create(
|
|
90
|
+
rpId,
|
|
91
|
+
id: nil,
|
|
92
|
+
privateKey: nil,
|
|
93
|
+
publicKey: nil,
|
|
94
|
+
userHandle: nil)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Seeds a virtual WebAuthn credential and returns it.
|
|
99
|
+
|
|
100
|
+
With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The
|
|
101
|
+
seeded credential is discoverable (resident), so the page can resolve it from both
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and
|
|
105
|
+
`publicKey` together.
|
|
106
|
+
|
|
107
|
+
Call [Credentials#install](./credentials#install) before navigating to a page that uses WebAuthn.
|
|
108
|
+
|
|
109
|
+
## delete
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
def delete(id)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Removes a credential from the authenticator by its id. Works for any credential currently held —
|
|
117
|
+
both those seeded with [Credentials#create](./credentials#create) and those the page registered itself by
|
|
118
|
+
calling `navigator.credentials.create()`.
|
|
119
|
+
|
|
120
|
+
## get
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
def get(id: nil, rpId: nil)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
Returns every credential currently held by the authenticator, optionally filtered by `rpId` or
|
|
128
|
+
`id`. This includes both credentials seeded with [Credentials#create](./credentials#create) and credentials
|
|
129
|
+
the page registered itself by calling `navigator.credentials.create()`.
|
|
130
|
+
|
|
131
|
+
Each returned credential includes its private and public keys, so a passkey the app just
|
|
132
|
+
registered can be saved and re-seeded into a later test with [Credentials#create](./credentials#create) — see the second example in the class overview.
|
|
@@ -291,7 +291,7 @@ puts frame.evaluate("1 + #{x}") # => "11"
|
|
|
291
291
|
[ElementHandle](./element_handle) instances can be passed as an argument to the [Frame#evaluate](./frame#evaluate):
|
|
292
292
|
|
|
293
293
|
```ruby
|
|
294
|
-
body_handle = frame.
|
|
294
|
+
body_handle = frame.evaluate_handle("document.body")
|
|
295
295
|
html = frame.evaluate("([body, suffix]) => body.innerHTML + suffix", arg: [body_handle, "hello"])
|
|
296
296
|
body_handle.dispose
|
|
297
297
|
```
|
|
@@ -321,14 +321,14 @@ a_window_handle # handle for the window object.
|
|
|
321
321
|
A string can also be passed in instead of a function.
|
|
322
322
|
|
|
323
323
|
```ruby
|
|
324
|
-
a_handle =
|
|
324
|
+
a_handle = frame.evaluate_handle("document") # handle for the "document"
|
|
325
325
|
```
|
|
326
326
|
|
|
327
327
|
[JSHandle](./js_handle) instances can be passed as an argument to the [Frame#evaluate_handle](./frame#evaluate_handle):
|
|
328
328
|
|
|
329
329
|
```ruby
|
|
330
|
-
|
|
331
|
-
result_handle =
|
|
330
|
+
a_handle = frame.evaluate_handle("document.body")
|
|
331
|
+
result_handle = frame.evaluate_handle("body => body.innerHTML", arg: a_handle)
|
|
332
332
|
puts result_handle.json_value
|
|
333
333
|
result_handle.dispose
|
|
334
334
|
```
|
|
@@ -413,7 +413,7 @@ puts page.evaluate("1 + #{x}") # => "11"
|
|
|
413
413
|
[ElementHandle](./element_handle) instances can be passed as an argument to the [Page#evaluate](./page#evaluate):
|
|
414
414
|
|
|
415
415
|
```ruby
|
|
416
|
-
body_handle = page.
|
|
416
|
+
body_handle = page.evaluate_handle("document.body")
|
|
417
417
|
html = page.evaluate("([body, suffix]) => body.innerHTML + suffix", arg: [body_handle, "hello"])
|
|
418
418
|
body_handle.dispose
|
|
419
419
|
```
|
|
@@ -1561,7 +1561,7 @@ This method taps an element matching `selector` by performing the following step
|
|
|
1561
1561
|
When all steps combined have not finished during the specified `timeout`, this method throws a
|
|
1562
1562
|
`TimeoutError`. Passing zero timeout disables this.
|
|
1563
1563
|
|
|
1564
|
-
**NOTE**: [Page#tap_point](./page#tap_point)
|
|
1564
|
+
**NOTE**: [Page#tap_point](./page#tap_point) will throw if the `hasTouch` option of the browser context is false.
|
|
1565
1565
|
|
|
1566
1566
|
## text_content
|
|
1567
1567
|
|
|
@@ -2001,6 +2001,16 @@ Playwright has ability to mock clock and passage of time.
|
|
|
2001
2001
|
|
|
2002
2002
|
## keyboard
|
|
2003
2003
|
|
|
2004
|
+
## local_storage
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
Provides access to the page's `localStorage` for the current origin. See [WebStorage](./web_storage).
|
|
2008
|
+
|
|
2009
|
+
## session_storage
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
Provides access to the page's `sessionStorage` for the current origin. See [WebStorage](./web_storage).
|
|
2013
|
+
|
|
2004
2014
|
## mouse
|
|
2005
2015
|
|
|
2006
2016
|
## request
|
|
@@ -19,4 +19,4 @@ def tap_point(x, y)
|
|
|
19
19
|
|
|
20
20
|
Dispatches a `touchstart` and `touchend` event with a single touch at the position (`x`,`y`).
|
|
21
21
|
|
|
22
|
-
**NOTE**: [
|
|
22
|
+
**NOTE**: [Touchscreen#tap_point](./touchscreen#tap_point) will throw if the `hasTouch` option of the browser context is false.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# WebStorage
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
WebStorage exposes the page's `localStorage` or `sessionStorage` for the current origin via an async,
|
|
9
|
+
[browser-consistent](https://developer.mozilla.org/en-US/docs/Web/API/Storage) API.
|
|
10
|
+
|
|
11
|
+
Instances are accessed through [Page#local_storage](./page#local_storage) and [Page#session_storage](./page#session_storage).
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
page.goto("https://example.com")
|
|
15
|
+
page.local_storage.set_item("token", "abc")
|
|
16
|
+
token = page.local_storage.get_item("token")
|
|
17
|
+
all = page.local_storage.items
|
|
18
|
+
page.local_storage.remove_item("token")
|
|
19
|
+
page.local_storage.clear
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## items
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
def items
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Returns all items in the storage as name/value pairs.
|
|
30
|
+
|
|
31
|
+
## get_item
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
def get_item(name)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Returns the value for the given `name` if present.
|
|
39
|
+
|
|
40
|
+
## set_item
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
def set_item(name, value)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Sets the value for the given `name`. Overwrites any existing value for that name.
|
|
48
|
+
|
|
49
|
+
## remove_item
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
def remove_item(name)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Removes the item with the given `name`. No-op if the item is absent.
|
|
57
|
+
|
|
58
|
+
## clear
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
def clear
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
Removes all items from the storage.
|
|
@@ -365,6 +365,8 @@
|
|
|
365
365
|
* ~~wait_for_event~~
|
|
366
366
|
* clock
|
|
367
367
|
* keyboard
|
|
368
|
+
* local_storage
|
|
369
|
+
* session_storage
|
|
368
370
|
* mouse
|
|
369
371
|
* request
|
|
370
372
|
* screencast
|
|
@@ -405,6 +407,7 @@
|
|
|
405
407
|
* expect_page
|
|
406
408
|
* ~~wait_for_event~~
|
|
407
409
|
* clock
|
|
410
|
+
* credentials
|
|
408
411
|
* ~~debugger~~
|
|
409
412
|
* request
|
|
410
413
|
* tracing
|
|
@@ -553,6 +556,8 @@
|
|
|
553
556
|
* headers_array
|
|
554
557
|
* json
|
|
555
558
|
* ok
|
|
559
|
+
* security_details
|
|
560
|
+
* server_addr
|
|
556
561
|
* status
|
|
557
562
|
* status_text
|
|
558
563
|
* text
|
|
@@ -638,3 +643,18 @@
|
|
|
638
643
|
* not_to_match_aria_snapshot
|
|
639
644
|
* to_have_title
|
|
640
645
|
* to_have_url
|
|
646
|
+
|
|
647
|
+
## WebStorage
|
|
648
|
+
|
|
649
|
+
* items
|
|
650
|
+
* get_item
|
|
651
|
+
* set_item
|
|
652
|
+
* remove_item
|
|
653
|
+
* clear
|
|
654
|
+
|
|
655
|
+
## Credentials
|
|
656
|
+
|
|
657
|
+
* install
|
|
658
|
+
* create
|
|
659
|
+
* delete
|
|
660
|
+
* get
|
|
@@ -39,6 +39,14 @@ module Playwright
|
|
|
39
39
|
@headers.headers_array
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def security_details
|
|
43
|
+
@initializer['securityDetails']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def server_addr
|
|
47
|
+
@initializer['serverAddr']
|
|
48
|
+
end
|
|
49
|
+
|
|
42
50
|
class AlreadyDisposedError < StandardError
|
|
43
51
|
def initialize
|
|
44
52
|
super('Response has been disposed')
|
|
@@ -5,7 +5,7 @@ module Playwright
|
|
|
5
5
|
|
|
6
6
|
attr_accessor :browser
|
|
7
7
|
attr_writer :owner_page, :options
|
|
8
|
-
attr_reader :clock, :tracing, :request
|
|
8
|
+
attr_reader :clock, :tracing, :request, :credentials
|
|
9
9
|
|
|
10
10
|
private def after_initialize
|
|
11
11
|
@options = @initializer['options']
|
|
@@ -20,6 +20,7 @@ module Playwright
|
|
|
20
20
|
@request = ChannelOwners::APIRequestContext.from(@initializer['requestContext'])
|
|
21
21
|
@request.send(:_update_timeout_settings, @timeout_settings)
|
|
22
22
|
@clock = ClockImpl.new(self)
|
|
23
|
+
@credentials = CredentialsImpl.new(self)
|
|
23
24
|
|
|
24
25
|
@channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
|
|
25
26
|
@channel.once('close', ->(_) { on_close })
|
|
@@ -92,11 +92,12 @@ module Playwright
|
|
|
92
92
|
raise
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
def connect_over_cdp(endpointURL, headers: nil, isLocal: nil, noDefaults: nil, slowMo: nil, timeout: nil, &block)
|
|
95
|
+
def connect_over_cdp(endpointURL, artifactsDir: nil, headers: nil, isLocal: nil, noDefaults: nil, slowMo: nil, timeout: nil, &block)
|
|
96
96
|
raise 'Connecting over CDP is only supported in Chromium.' unless name == 'chromium'
|
|
97
97
|
|
|
98
98
|
params = {
|
|
99
99
|
endpointURL: endpointURL,
|
|
100
|
+
artifactsDir: artifactsDir,
|
|
100
101
|
headers: headers,
|
|
101
102
|
isLocal: isLocal,
|
|
102
103
|
noDefaults: noDefaults,
|
|
@@ -725,25 +725,44 @@ module Playwright
|
|
|
725
725
|
.serialize
|
|
726
726
|
end
|
|
727
727
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
728
|
+
is_not = options[:isNot] || false
|
|
729
|
+
|
|
730
|
+
# Since Playwright 1.61, the `expect` command resolves on a successful
|
|
731
|
+
# match and rejects with FrameExpectErrorDetails on a mismatch/timeout.
|
|
732
|
+
begin
|
|
733
|
+
@channel.send_message_to_server_result(
|
|
734
|
+
title, # title
|
|
735
|
+
"expect", # method
|
|
736
|
+
{ # params
|
|
737
|
+
selector: selector,
|
|
738
|
+
expression: expression,
|
|
739
|
+
**options,
|
|
740
|
+
}.compact
|
|
741
|
+
)
|
|
742
|
+
{ 'matches' => !is_not }
|
|
743
|
+
rescue ::Playwright::Error => err
|
|
744
|
+
details = err.details || {}
|
|
745
|
+
received = details['received']
|
|
746
|
+
if received.is_a?(Hash)
|
|
747
|
+
received = received.dup
|
|
748
|
+
if received.key?('value')
|
|
749
|
+
received['value'] = JavaScript::ValueParser.new(received['value']).parse
|
|
750
|
+
end
|
|
743
751
|
end
|
|
744
|
-
end
|
|
745
752
|
|
|
746
|
-
|
|
753
|
+
error_message =
|
|
754
|
+
if details['customErrorMessage']
|
|
755
|
+
"Error: #{details['customErrorMessage']}"
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
{
|
|
759
|
+
'matches' => is_not,
|
|
760
|
+
'received' => received,
|
|
761
|
+
'log' => err.raw_log,
|
|
762
|
+
'timedOut' => details['timedOut'],
|
|
763
|
+
'errorMessage' => error_message,
|
|
764
|
+
}.compact
|
|
765
|
+
end
|
|
747
766
|
end
|
|
748
767
|
|
|
749
768
|
private def drop_payload_params(payload)
|
|
@@ -86,6 +86,14 @@ module Playwright
|
|
|
86
86
|
:viewport_size,
|
|
87
87
|
:main_frame
|
|
88
88
|
|
|
89
|
+
def local_storage
|
|
90
|
+
@local_storage ||= WebStorageImpl.new(self, 'local')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def session_storage
|
|
94
|
+
@session_storage ||= WebStorageImpl.new(self, 'session')
|
|
95
|
+
end
|
|
96
|
+
|
|
89
97
|
private def on_frame_attached(frame)
|
|
90
98
|
frame.send(:update_page_from_page, self)
|
|
91
99
|
@frames << frame
|
|
@@ -513,13 +521,15 @@ module Playwright
|
|
|
513
521
|
end
|
|
514
522
|
if @owned_context
|
|
515
523
|
@owned_context.close
|
|
524
|
+
elsif runBeforeUnload
|
|
525
|
+
@channel.send_message_to_server('runBeforeUnload')
|
|
516
526
|
else
|
|
517
|
-
options = {
|
|
527
|
+
options = { reason: reason }.compact
|
|
518
528
|
@channel.send_message_to_server('close', options)
|
|
519
529
|
end
|
|
520
530
|
nil
|
|
521
531
|
rescue => err
|
|
522
|
-
raise if !target_closed_error?(err) ||
|
|
532
|
+
raise if !target_closed_error?(err) || runBeforeUnload
|
|
523
533
|
end
|
|
524
534
|
|
|
525
535
|
def closed?
|
|
@@ -74,14 +74,18 @@ module Playwright
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def async_send_message_to_server(guid, method, params, metadata: nil)
|
|
77
|
+
if method == '__waitInfo__' && @closed_error
|
|
78
|
+
return Concurrent::Promises.fulfilled_future(nil)
|
|
79
|
+
end
|
|
77
80
|
return if @closed_error
|
|
78
81
|
|
|
79
82
|
callback = Concurrent::Promises.resolvable_future
|
|
83
|
+
fire_and_forget = method == '__waitInfo__'
|
|
80
84
|
|
|
81
85
|
with_generated_id do |id|
|
|
82
86
|
# register callback promise object first.
|
|
83
87
|
# @see https://github.com/YusukeIwaki/puppeteer-ruby/pull/34
|
|
84
|
-
@callbacks_mutex.synchronize { @callbacks[id] = callback }
|
|
88
|
+
@callbacks_mutex.synchronize { @callbacks[id] = callback } unless fire_and_forget
|
|
85
89
|
|
|
86
90
|
_metadata = {}
|
|
87
91
|
frames = []
|
|
@@ -112,6 +116,7 @@ module Playwright
|
|
|
112
116
|
callback.reject(err)
|
|
113
117
|
raise unless err.is_a?(Transport::AlreadyDisconnectedError)
|
|
114
118
|
end
|
|
119
|
+
callback.fulfill(nil) if fire_and_forget
|
|
115
120
|
|
|
116
121
|
if @tracing_count > 0 && !frames.empty? && guid != 'localUtils' && !remote?
|
|
117
122
|
@local_utils.add_stack_to_tracing_no_reply(id, frames)
|
|
@@ -168,6 +173,7 @@ module Playwright
|
|
|
168
173
|
if error && !msg['result']
|
|
169
174
|
parsed_error = ::Playwright::Error.parse(error['error'])
|
|
170
175
|
parsed_error.log = msg['log']
|
|
176
|
+
parsed_error.details = msg['errorDetails']
|
|
171
177
|
callback.reject(parsed_error)
|
|
172
178
|
else
|
|
173
179
|
result = replace_guids_with_channels(msg['result'])
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Playwright
|
|
2
|
+
# ref: https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/client/credentials.ts
|
|
3
|
+
define_api_implementation :CredentialsImpl do
|
|
4
|
+
# @param browser_context [ChannelOwners::BrowserContext]
|
|
5
|
+
def initialize(browser_context)
|
|
6
|
+
@browser_context = browser_context
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def install
|
|
10
|
+
@browser_context.channel.send_message_to_server('credentialsInstall')
|
|
11
|
+
nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create(rpId, id: nil, userHandle: nil, privateKey: nil, publicKey: nil)
|
|
15
|
+
params = {
|
|
16
|
+
rpId: rpId,
|
|
17
|
+
id: id,
|
|
18
|
+
userHandle: userHandle,
|
|
19
|
+
privateKey: privateKey,
|
|
20
|
+
publicKey: publicKey,
|
|
21
|
+
}.compact
|
|
22
|
+
@browser_context.channel.send_message_to_server('credentialsCreate', params)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def get(id: nil, rpId: nil)
|
|
26
|
+
params = { id: id, rpId: rpId }.compact
|
|
27
|
+
@browser_context.channel.send_message_to_server('credentialsGet', params)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def delete(id)
|
|
31
|
+
@browser_context.channel.send_message_to_server('credentialsDelete', id: id)
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/playwright/errors.rb
CHANGED
|
@@ -31,10 +31,13 @@ module Playwright
|
|
|
31
31
|
@stack = stack
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
attr_reader :name, :message, :stack
|
|
34
|
+
attr_reader :name, :message, :stack, :raw_log
|
|
35
|
+
# Error details for `expect` failures (received value, timedOut, customErrorMessage).
|
|
36
|
+
attr_accessor :details
|
|
35
37
|
|
|
36
38
|
def log=(log)
|
|
37
39
|
return unless log
|
|
40
|
+
@raw_log = log
|
|
38
41
|
format_call_log = log.join("\n - ")
|
|
39
42
|
@message = "#{@message}\nCall log:\n#{format_call_log}\n"
|
|
40
43
|
end
|
|
@@ -39,7 +39,15 @@ module Playwright
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
private def get_by_test_id_selector(test_id_attribute_name, test_id)
|
|
42
|
-
"internal:testid=[#{test_id_attribute_name}=#{escape_for_attribute_selector_or_regex(test_id, true)}]"
|
|
42
|
+
"internal:testid=[#{encode_test_id_attribute_name(test_id_attribute_name)}=#{escape_for_attribute_selector_or_regex(test_id, true)}]"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private def encode_test_id_attribute_name(test_id_attribute_name)
|
|
46
|
+
if test_id_attribute_name.include?(',')
|
|
47
|
+
JSON.generate(test_id_attribute_name)
|
|
48
|
+
else
|
|
49
|
+
test_id_attribute_name
|
|
50
|
+
end
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
private def get_by_label_selector(text, exact:)
|
|
@@ -63,11 +63,12 @@ module Playwright
|
|
|
63
63
|
@page.send(:channel).send_message_to_server('screencastChapter', params)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def show_actions(duration: nil, fontSize: nil, position: nil)
|
|
66
|
+
def show_actions(duration: nil, fontSize: nil, position: nil, cursor: nil)
|
|
67
67
|
params = {}
|
|
68
68
|
params[:duration] = duration if duration
|
|
69
69
|
params[:fontSize] = fontSize if fontSize
|
|
70
70
|
params[:position] = position if position
|
|
71
|
+
params[:cursor] = cursor if cursor
|
|
71
72
|
@page.send(:channel).send_message_to_server('screencastShowActions', params)
|
|
72
73
|
DisposableStub.new { hide_actions }
|
|
73
74
|
end
|
|
@@ -87,6 +88,7 @@ module Playwright
|
|
|
87
88
|
private def handle_screencast_frame(event)
|
|
88
89
|
@on_frame&.call({
|
|
89
90
|
data: Base64.strict_decode64(event['data']),
|
|
91
|
+
timestamp: event['timestamp'],
|
|
90
92
|
viewportWidth: event['viewportWidth'],
|
|
91
93
|
viewportHeight: event['viewportHeight'],
|
|
92
94
|
})
|
data/lib/playwright/version.rb
CHANGED