playwright-ruby-client 1.37.1 → 1.39.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -7
- data/documentation/docs/api/browser.md +13 -9
- data/documentation/docs/api/browser_context.md +51 -32
- data/documentation/docs/api/browser_type.md +12 -7
- data/documentation/docs/api/dialog.md +18 -15
- data/documentation/docs/api/download.md +17 -7
- data/documentation/docs/api/element_handle.md +7 -19
- data/documentation/docs/api/frame.md +55 -30
- data/documentation/docs/api/keyboard.md +4 -0
- data/documentation/docs/api/locator.md +57 -16
- data/documentation/docs/api/page.md +102 -54
- data/documentation/docs/api/playwright.md +23 -20
- data/documentation/docs/api/request.md +17 -0
- data/documentation/docs/api/selectors.md +34 -29
- data/documentation/docs/include/api_coverage.md +1 -0
- data/lib/playwright/channel.rb +8 -0
- data/lib/playwright/channel_owner.rb +7 -2
- data/lib/playwright/channel_owners/browser_context.rb +16 -1
- data/lib/playwright/channel_owners/local_utils.rb +27 -0
- data/lib/playwright/channel_owners/page.rb +2 -0
- data/lib/playwright/channel_owners/playwright.rb +1 -24
- data/lib/playwright/channel_owners/request.rb +17 -1
- data/lib/playwright/channel_owners/route.rb +5 -1
- data/lib/playwright/connection.rb +1 -1
- data/lib/playwright/console_message_impl.rb +29 -0
- data/lib/playwright/errors.rb +13 -2
- data/lib/playwright/events.rb +1 -0
- data/lib/playwright/javascript/value_parser.rb +8 -0
- data/lib/playwright/javascript/value_serializer.rb +10 -4
- data/lib/playwright/locator_impl.rb +4 -0
- data/lib/playwright/utils.rb +4 -0
- data/lib/playwright/version.rb +2 -2
- data/lib/playwright_api/browser.rb +2 -2
- data/lib/playwright_api/browser_context.rb +4 -4
- data/lib/playwright_api/browser_type.rb +2 -2
- data/lib/playwright_api/console_message.rb +0 -22
- data/lib/playwright_api/dialog.rb +2 -2
- data/lib/playwright_api/download.rb +12 -3
- data/lib/playwright_api/element_handle.rb +2 -15
- data/lib/playwright_api/frame.rb +8 -13
- data/lib/playwright_api/keyboard.rb +4 -0
- data/lib/playwright_api/locator.rb +52 -16
- data/lib/playwright_api/page.rb +24 -29
- data/lib/playwright_api/playwright.rb +4 -4
- data/lib/playwright_api/request.rb +17 -0
- data/lib/playwright_api/selectors.rb +2 -2
- data/lib/playwright_api/worker.rb +4 -4
- data/sig/playwright.rbs +1 -0
- metadata +4 -4
- data/lib/playwright/channel_owners/console_message.rb +0 -25
@@ -12,12 +12,21 @@ instance might have multiple [Page](./page) instances.
|
|
12
12
|
|
13
13
|
This example creates a page, navigates it to a URL, and then saves a screenshot:
|
14
14
|
|
15
|
-
```
|
16
|
-
playwright.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
```python sync title=example_94e620cdbdfd41e2c9b14d561052ffa89535fc346038c4584ea4dd8520f5401c.py
|
16
|
+
from playwright.sync_api import sync_playwright, Playwright
|
17
|
+
|
18
|
+
def run(playwright: Playwright):
|
19
|
+
webkit = playwright.webkit
|
20
|
+
browser = webkit.launch()
|
21
|
+
context = browser.new_context()
|
22
|
+
page = context.new_page()
|
23
|
+
page.goto("https://example.com")
|
24
|
+
page.screenshot(path="screenshot.png")
|
25
|
+
browser.close()
|
26
|
+
|
27
|
+
with sync_playwright() as playwright:
|
28
|
+
run(playwright)
|
29
|
+
|
21
30
|
```
|
22
31
|
|
23
32
|
The Page class emits various events (described below) which can be handled using any of Node's native
|
@@ -462,18 +471,29 @@ See [BrowserContext#expose_binding](./browser_context#expose_binding) for the co
|
|
462
471
|
|
463
472
|
An example of exposing page URL to all frames in a page:
|
464
473
|
|
465
|
-
```
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
474
|
+
```python sync title=example_4f7d99a72aaea957cc5678ed8728965338d78598d7772f47fbf23c28f0eba52d.py
|
475
|
+
from playwright.sync_api import sync_playwright, Playwright
|
476
|
+
|
477
|
+
def run(playwright: Playwright):
|
478
|
+
webkit = playwright.webkit
|
479
|
+
browser = webkit.launch(headless=false)
|
480
|
+
context = browser.new_context()
|
481
|
+
page = context.new_page()
|
482
|
+
page.expose_binding("pageURL", lambda source: source["page"].url)
|
483
|
+
page.set_content("""
|
484
|
+
<script>
|
485
|
+
async function onClick() {
|
486
|
+
document.querySelector('div').textContent = await window.pageURL();
|
487
|
+
}
|
488
|
+
</script>
|
489
|
+
<button onclick="onClick()">Click me</button>
|
490
|
+
<div></div>
|
491
|
+
""")
|
492
|
+
page.click("button")
|
493
|
+
|
494
|
+
with sync_playwright() as playwright:
|
495
|
+
run(playwright)
|
496
|
+
|
477
497
|
```
|
478
498
|
|
479
499
|
An example of passing an element handle:
|
@@ -517,24 +537,35 @@ See [BrowserContext#expose_function](./browser_context#expose_function) for cont
|
|
517
537
|
|
518
538
|
An example of adding a `sha256` function to the page:
|
519
539
|
|
520
|
-
```
|
521
|
-
|
540
|
+
```python sync title=example_0f68a39bdff02a3df161c74e81cabb8a2ff1f09f0d09f6ef9b799a6f2f19a280.py
|
541
|
+
import hashlib
|
542
|
+
from playwright.sync_api import sync_playwright, Playwright
|
522
543
|
|
523
|
-
def
|
524
|
-
|
525
|
-
|
544
|
+
def sha256(text):
|
545
|
+
m = hashlib.sha256()
|
546
|
+
m.update(bytes(text, "utf8"))
|
547
|
+
return m.hexdigest()
|
548
|
+
|
549
|
+
|
550
|
+
def run(playwright: Playwright):
|
551
|
+
webkit = playwright.webkit
|
552
|
+
browser = webkit.launch(headless=False)
|
553
|
+
page = browser.new_page()
|
554
|
+
page.expose_function("sha256", sha256)
|
555
|
+
page.set_content("""
|
556
|
+
<script>
|
557
|
+
async function onClick() {
|
558
|
+
document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');
|
559
|
+
}
|
560
|
+
</script>
|
561
|
+
<button onclick="onClick()">Click me</button>
|
562
|
+
<div></div>
|
563
|
+
""")
|
564
|
+
page.click("button")
|
565
|
+
|
566
|
+
with sync_playwright() as playwright:
|
567
|
+
run(playwright)
|
526
568
|
|
527
|
-
page.expose_function("sha256", method(:sha256))
|
528
|
-
page.content = <<~HTML
|
529
|
-
<script>
|
530
|
-
async function onClick() {
|
531
|
-
document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');
|
532
|
-
}
|
533
|
-
</script>
|
534
|
-
<button onclick="onClick()">Click me</button>
|
535
|
-
<div></div>
|
536
|
-
HTML
|
537
|
-
page.locator("button").click
|
538
569
|
```
|
539
570
|
|
540
571
|
## fill
|
@@ -554,7 +585,7 @@ This method waits for an element matching `selector`, waits for [actionability](
|
|
554
585
|
|
555
586
|
If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an error. However, if the element is inside the `<label>` element that has an associated [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead.
|
556
587
|
|
557
|
-
To send fine-grained keyboard events, use [
|
588
|
+
To send fine-grained keyboard events, use [Locator#press_sequentially](./locator#press_sequentially).
|
558
589
|
|
559
590
|
## focus
|
560
591
|
|
@@ -1324,13 +1355,14 @@ Triggers a `change` and `input` event once all the provided options have been se
|
|
1324
1355
|
|
1325
1356
|
**Usage**
|
1326
1357
|
|
1327
|
-
```
|
1328
|
-
#
|
1329
|
-
page.select_option("select#colors",
|
1358
|
+
```python sync title=example_8260034c740933903e5a39d30a4f4e388bdffa9e82acd9a5fe1fb774752a505a.py
|
1359
|
+
# Single selection matching the value or label
|
1360
|
+
page.select_option("select#colors", "blue")
|
1330
1361
|
# single selection matching both the label
|
1331
|
-
page.select_option("select#colors", label
|
1362
|
+
page.select_option("select#colors", label="blue")
|
1332
1363
|
# multiple selection
|
1333
|
-
page.select_option("select#colors", value
|
1364
|
+
page.select_option("select#colors", value=["red", "green", "blue"])
|
1365
|
+
|
1334
1366
|
```
|
1335
1367
|
|
1336
1368
|
## set_checked
|
@@ -1519,11 +1551,6 @@ To press a special key, like `Control` or `ArrowDown`, use [Keyboard#press](./ke
|
|
1519
1551
|
|
1520
1552
|
**Usage**
|
1521
1553
|
|
1522
|
-
```ruby
|
1523
|
-
page.type("#mytextarea", "hello") # types instantly
|
1524
|
-
page.type("#mytextarea", "world", delay: 100) # types slower, like a user
|
1525
|
-
```
|
1526
|
-
|
1527
1554
|
## uncheck
|
1528
1555
|
|
1529
1556
|
```
|
@@ -1649,9 +1676,20 @@ Returns when the `expression` returns a truthy value. It resolves to a JSHandle
|
|
1649
1676
|
|
1650
1677
|
The [Page#wait_for_function](./page#wait_for_function) can be used to observe viewport size change:
|
1651
1678
|
|
1652
|
-
```
|
1653
|
-
|
1654
|
-
|
1679
|
+
```python sync title=example_83eed1f1f00ad73f641bf4a49f672e81c4faf1ca098a4a5070afeeabb88312f5.py
|
1680
|
+
from playwright.sync_api import sync_playwright, Playwright
|
1681
|
+
|
1682
|
+
def run(playwright: Playwright):
|
1683
|
+
webkit = playwright.webkit
|
1684
|
+
browser = webkit.launch()
|
1685
|
+
page = browser.new_page()
|
1686
|
+
page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
|
1687
|
+
page.wait_for_function("() => window.x > 0")
|
1688
|
+
browser.close()
|
1689
|
+
|
1690
|
+
with sync_playwright() as playwright:
|
1691
|
+
run(playwright)
|
1692
|
+
|
1655
1693
|
```
|
1656
1694
|
|
1657
1695
|
To pass an argument to the predicate of [Page#wait_for_function](./page#wait_for_function) function:
|
@@ -1819,12 +1857,22 @@ function will throw.
|
|
1819
1857
|
|
1820
1858
|
This method works across navigations:
|
1821
1859
|
|
1822
|
-
```
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1860
|
+
```python sync title=example_903c7325fd65fcdf6f22c77fc159922a568841abce60ae1b7c54ab5837401862.py
|
1861
|
+
from playwright.sync_api import sync_playwright, Playwright
|
1862
|
+
|
1863
|
+
def run(playwright: Playwright):
|
1864
|
+
chromium = playwright.chromium
|
1865
|
+
browser = chromium.launch()
|
1866
|
+
page = browser.new_page()
|
1867
|
+
for current_url in ["https://google.com", "https://bbc.com"]:
|
1868
|
+
page.goto(current_url, wait_until="domcontentloaded")
|
1869
|
+
element = page.wait_for_selector("img")
|
1870
|
+
print("Loaded image: " + str(element.get_attribute("src")))
|
1871
|
+
browser.close()
|
1872
|
+
|
1873
|
+
with sync_playwright() as playwright:
|
1874
|
+
run(playwright)
|
1875
|
+
|
1828
1876
|
```
|
1829
1877
|
|
1830
1878
|
## wait_for_timeout
|
@@ -8,19 +8,20 @@ sidebar_position: 10
|
|
8
8
|
Playwright module provides a method to launch a browser instance. The following is a typical example of using Playwright
|
9
9
|
to drive automation:
|
10
10
|
|
11
|
-
```
|
12
|
-
|
11
|
+
```python sync title=example_6647e5a44b0440884026a6142606dfddad75ba1e643919b015457df4ed2e198f.py
|
12
|
+
from playwright.sync_api import sync_playwright, Playwright
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
page = browser.new_page
|
18
|
-
page.goto(
|
14
|
+
def run(playwright: Playwright):
|
15
|
+
chromium = playwright.chromium # or "firefox" or "webkit".
|
16
|
+
browser = chromium.launch()
|
17
|
+
page = browser.new_page()
|
18
|
+
page.goto("http://example.com")
|
19
|
+
# other actions...
|
20
|
+
browser.close()
|
19
21
|
|
20
|
-
|
22
|
+
with sync_playwright() as playwright:
|
23
|
+
run(playwright)
|
21
24
|
|
22
|
-
end
|
23
|
-
end
|
24
25
|
```
|
25
26
|
|
26
27
|
## chromium
|
@@ -33,20 +34,22 @@ This object can be used to launch or connect to Chromium, returning instances of
|
|
33
34
|
|
34
35
|
Returns a dictionary of devices to be used with [Browser#new_context](./browser#new_context) or [Browser#new_page](./browser#new_page).
|
35
36
|
|
36
|
-
```
|
37
|
-
|
37
|
+
```python sync title=example_14d627977a4ad16a605ec5472d768a3324812fa8e7c57685561408fa6601e352.py
|
38
|
+
from playwright.sync_api import sync_playwright, Playwright
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
def run(playwright: Playwright):
|
41
|
+
webkit = playwright.webkit
|
42
|
+
iphone = playwright.devices["iPhone 6"]
|
43
|
+
browser = webkit.launch()
|
42
44
|
context = browser.new_context(**iphone)
|
43
|
-
page = context.new_page
|
44
|
-
page.goto(
|
45
|
+
page = context.new_page()
|
46
|
+
page.goto("http://example.com")
|
47
|
+
# other actions...
|
48
|
+
browser.close()
|
45
49
|
|
46
|
-
|
50
|
+
with sync_playwright() as playwright:
|
51
|
+
run(playwright)
|
47
52
|
|
48
|
-
end
|
49
|
-
end
|
50
53
|
```
|
51
54
|
|
52
55
|
## firefox
|
@@ -54,6 +54,20 @@ def frame
|
|
54
54
|
|
55
55
|
Returns the [Frame](./frame) that initiated this request.
|
56
56
|
|
57
|
+
**Usage**
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
frame_url = request.frame.url
|
61
|
+
```
|
62
|
+
|
63
|
+
**Details**
|
64
|
+
|
65
|
+
Note that in some cases the frame is not available, and this method will throw.
|
66
|
+
- When request originates in the Service Worker. You can use `request.serviceWorker()` to check that.
|
67
|
+
- When navigation request is issued before the corresponding frame is created. You can use [Request#navigation_request?](./request#navigation_request?) to check that.
|
68
|
+
|
69
|
+
Here is an example that handles all the cases:
|
70
|
+
|
57
71
|
## headers
|
58
72
|
|
59
73
|
```
|
@@ -93,6 +107,9 @@ def navigation_request?
|
|
93
107
|
|
94
108
|
Whether this request is driving frame's navigation.
|
95
109
|
|
110
|
+
Some navigation requests are issued before the corresponding frame is created, and therefore
|
111
|
+
do not have [Request#frame](./request#frame) available.
|
112
|
+
|
96
113
|
## method
|
97
114
|
|
98
115
|
```
|
@@ -21,33 +21,38 @@ Selectors must be registered before creating the page.
|
|
21
21
|
|
22
22
|
An example of registering selector engine that queries elements based on a tag name:
|
23
23
|
|
24
|
-
```
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
24
|
+
```python sync title=example_3e739d4f0e30e20a6a698e0e17605a841c35e65e75aa3c2642f8bfc368b33f9e.py
|
25
|
+
from playwright.sync_api import sync_playwright, Playwright
|
26
|
+
|
27
|
+
def run(playwright: Playwright):
|
28
|
+
tag_selector = """
|
29
|
+
{
|
30
|
+
// Returns the first element matching given selector in the root's subtree.
|
31
|
+
query(root, selector) {
|
32
|
+
return root.querySelector(selector);
|
33
|
+
},
|
34
|
+
// Returns all elements matching given selector in the root's subtree.
|
35
|
+
queryAll(root, selector) {
|
36
|
+
return Array.from(root.querySelectorAll(selector));
|
37
|
+
}
|
38
|
+
}"""
|
39
|
+
|
40
|
+
# Register the engine. Selectors will be prefixed with "tag=".
|
41
|
+
playwright.selectors.register("tag", tag_selector)
|
42
|
+
browser = playwright.chromium.launch()
|
43
|
+
page = browser.new_page()
|
44
|
+
page.set_content('<div><button>Click me</button></div>')
|
45
|
+
|
46
|
+
# Use the selector prefixed with its name.
|
47
|
+
button = page.locator('tag=button')
|
48
|
+
# Combine it with built-in locators.
|
49
|
+
page.locator('tag=div').get_by_text('Click me').click()
|
50
|
+
# Can use it in any methods supporting selectors.
|
51
|
+
button_count = page.locator('tag=button').count()
|
52
|
+
print(button_count)
|
53
|
+
browser.close()
|
54
|
+
|
55
|
+
with sync_playwright() as playwright:
|
56
|
+
run(playwright)
|
57
|
+
|
53
58
|
```
|
data/lib/playwright/channel.rb
CHANGED
@@ -30,6 +30,7 @@ module Playwright
|
|
30
30
|
# @param params [Hash]
|
31
31
|
# @return [Hash]
|
32
32
|
def send_message_to_server_result(method, params)
|
33
|
+
check_not_collected
|
33
34
|
with_logging do |metadata|
|
34
35
|
@connection.send_message_to_server(@guid, method, params, metadata: metadata)
|
35
36
|
end
|
@@ -39,6 +40,7 @@ module Playwright
|
|
39
40
|
# @param params [Hash]
|
40
41
|
# @returns [Concurrent::Promises::Future]
|
41
42
|
def async_send_message_to_server(method, params = {})
|
43
|
+
check_not_collected
|
42
44
|
with_logging do |metadata|
|
43
45
|
@connection.async_send_message_to_server(@guid, method, params, metadata: metadata)
|
44
46
|
end
|
@@ -74,5 +76,11 @@ module Playwright
|
|
74
76
|
end,
|
75
77
|
}
|
76
78
|
end
|
79
|
+
|
80
|
+
private def check_not_collected
|
81
|
+
if @object.was_collected?
|
82
|
+
raise "The object has been collected to prevent unbounded heap growth."
|
83
|
+
end
|
84
|
+
end
|
77
85
|
end
|
78
86
|
end
|
@@ -50,13 +50,18 @@ module Playwright
|
|
50
50
|
child.send(:update_parent, self)
|
51
51
|
end
|
52
52
|
|
53
|
+
def was_collected?
|
54
|
+
@was_collected
|
55
|
+
end
|
56
|
+
|
53
57
|
# used only from Connection. Not intended for public use. So keep private.
|
54
|
-
private def dispose!
|
58
|
+
private def dispose!(reason: nil)
|
55
59
|
# Clean up from parent and connection.
|
56
60
|
@connection.send(:delete_object_from_channel_owner, @guid)
|
61
|
+
@was_collected = reason == 'gc'
|
57
62
|
|
58
63
|
# Dispose all children.
|
59
|
-
@objects.each_value { |object| object.send(:dispose
|
64
|
+
@objects.each_value { |object| object.send(:dispose!, reason: reason) }
|
60
65
|
@objects.clear
|
61
66
|
end
|
62
67
|
|
@@ -36,7 +36,13 @@ module Playwright
|
|
36
36
|
on_service_worker(ChannelOwners::Worker.from(params['worker']))
|
37
37
|
})
|
38
38
|
@channel.on('console', ->(params) {
|
39
|
-
on_console_message(
|
39
|
+
on_console_message(ConsoleMessageImpl.new(params))
|
40
|
+
})
|
41
|
+
@channel.on('pageError', ->(params) {
|
42
|
+
on_page_error(
|
43
|
+
Error.parse(params['error']['error']),
|
44
|
+
ChannelOwners::Page.from_nullable(params['page']),
|
45
|
+
)
|
40
46
|
})
|
41
47
|
@channel.on('dialog', ->(params) {
|
42
48
|
on_dialog(ChannelOwners::Dialog.from(params['dialog']))
|
@@ -97,6 +103,8 @@ module Playwright
|
|
97
103
|
end
|
98
104
|
|
99
105
|
private def on_route(route)
|
106
|
+
route.send(:update_context, self)
|
107
|
+
|
100
108
|
# It is not desired to use PlaywrightApi.wrap directly.
|
101
109
|
# However it is a little difficult to define wrapper for `handler` parameter in generate_api.
|
102
110
|
# Just a workaround...
|
@@ -177,6 +185,13 @@ module Playwright
|
|
177
185
|
end
|
178
186
|
end
|
179
187
|
|
188
|
+
private def on_page_error(error, page)
|
189
|
+
emit(Events::BrowserContext::WebError, WebError.new(error, page))
|
190
|
+
if page
|
191
|
+
page.emit(Events::Page::PageError, error)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
180
195
|
private def on_request(request, page)
|
181
196
|
emit(Events::BrowserContext::Request, request)
|
182
197
|
page&.emit(Events::Page::Request, request)
|
@@ -1,5 +1,11 @@
|
|
1
1
|
module Playwright
|
2
2
|
define_channel_owner :LocalUtils do
|
3
|
+
def devices
|
4
|
+
@devices ||= @initializer['deviceDescriptors'].map do |device|
|
5
|
+
[device['name'], parse_device_descriptor(device['descriptor'])]
|
6
|
+
end.to_h
|
7
|
+
end
|
8
|
+
|
3
9
|
# @param zip_file [String]
|
4
10
|
def zip(params)
|
5
11
|
@channel.send_message_to_server('zip', params)
|
@@ -51,5 +57,26 @@ module Playwright
|
|
51
57
|
@channel.async_send_message_to_server('addStackToTracingNoReply', callData: { id: id, stack: stack })
|
52
58
|
nil
|
53
59
|
end
|
60
|
+
|
61
|
+
private def parse_device_descriptor(descriptor)
|
62
|
+
# This return value can be passed into Browser#new_context as it is.
|
63
|
+
# ex:
|
64
|
+
# ```
|
65
|
+
# iPhone = playwright.devices['iPhone 6']
|
66
|
+
# context = browser.new_context(**iPhone)
|
67
|
+
# page = context.new_page
|
68
|
+
#
|
69
|
+
# ```
|
70
|
+
{
|
71
|
+
userAgent: descriptor['userAgent'],
|
72
|
+
viewport: {
|
73
|
+
width: descriptor['viewport']['width'],
|
74
|
+
height: descriptor['viewport']['height'],
|
75
|
+
},
|
76
|
+
deviceScaleFactor: descriptor['deviceScaleFactor'],
|
77
|
+
isMobile: descriptor['isMobile'],
|
78
|
+
hasTouch: descriptor['hasTouch'],
|
79
|
+
}
|
80
|
+
end
|
54
81
|
end
|
55
82
|
end
|
@@ -96,6 +96,8 @@ module Playwright
|
|
96
96
|
end
|
97
97
|
|
98
98
|
private def on_route(route)
|
99
|
+
route.send(:update_context, self)
|
100
|
+
|
99
101
|
# It is not desired to use PlaywrightApi.wrap directly.
|
100
102
|
# However it is a little difficult to define wrapper for `handler` parameter in generate_api.
|
101
103
|
# Just a workaround...
|
@@ -25,9 +25,7 @@ module Playwright
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def devices
|
28
|
-
@devices
|
29
|
-
[item['name'], parse_device_descriptor(item['descriptor'])]
|
30
|
-
end.to_h
|
28
|
+
@connection.local_utils.devices
|
31
29
|
end
|
32
30
|
|
33
31
|
# used only from Playwright#connect_to_browser_server
|
@@ -44,26 +42,5 @@ module Playwright
|
|
44
42
|
end
|
45
43
|
::Playwright::ChannelOwners::AndroidDevice.from(@initializer['preConnectedAndroidDevice'])
|
46
44
|
end
|
47
|
-
|
48
|
-
private def parse_device_descriptor(descriptor)
|
49
|
-
# This return value can be passed into Browser#new_context as it is.
|
50
|
-
# ex:
|
51
|
-
# ```
|
52
|
-
# iPhone = playwright.devices['iPhone 6']
|
53
|
-
# context = browser.new_context(**iPhone)
|
54
|
-
# page = context.new_page
|
55
|
-
#
|
56
|
-
# ```
|
57
|
-
{
|
58
|
-
userAgent: descriptor['userAgent'],
|
59
|
-
viewport: {
|
60
|
-
width: descriptor['viewport']['width'],
|
61
|
-
height: descriptor['viewport']['height'],
|
62
|
-
},
|
63
|
-
deviceScaleFactor: descriptor['deviceScaleFactor'],
|
64
|
-
isMobile: descriptor['isMobile'],
|
65
|
-
hasTouch: descriptor['hasTouch'],
|
66
|
-
}
|
67
|
-
end
|
68
45
|
end
|
69
46
|
end
|
@@ -86,8 +86,24 @@ module Playwright
|
|
86
86
|
ChannelOwners::Response.from_nullable(resp)
|
87
87
|
end
|
88
88
|
|
89
|
+
class FramePageNotReadyError < StandardError
|
90
|
+
MESSAGE = [
|
91
|
+
'Frame for this navigation request is not available, because the request',
|
92
|
+
'was issued before the frame is created. You can check whether the request',
|
93
|
+
'is a navigation request by calling isNavigationRequest() method.',
|
94
|
+
].join('\n').freeze
|
95
|
+
|
96
|
+
def initialize
|
97
|
+
super(MESSAGE)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
89
101
|
def frame
|
90
|
-
ChannelOwners::Frame.from(@initializer['frame'])
|
102
|
+
ChannelOwners::Frame.from(@initializer['frame']).tap do |result|
|
103
|
+
unless result.page
|
104
|
+
raise FramePageNotReadyError.new
|
105
|
+
end
|
106
|
+
end
|
91
107
|
end
|
92
108
|
|
93
109
|
def navigation_request?
|
@@ -111,7 +111,7 @@ module Playwright
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def fetch(headers: nil, method: nil, postData: nil, url: nil, maxRedirects: nil, timeout: nil)
|
114
|
-
api_request_context =
|
114
|
+
api_request_context = @context.request
|
115
115
|
api_request_context.send(:_inner_fetch,
|
116
116
|
request,
|
117
117
|
url,
|
@@ -172,5 +172,9 @@ module Playwright
|
|
172
172
|
mime_types = MIME::Types.type_for(filepath)
|
173
173
|
mime_types.first.to_s || 'application/octet-stream'
|
174
174
|
end
|
175
|
+
|
176
|
+
private def update_context(context)
|
177
|
+
@context = context
|
178
|
+
end
|
175
179
|
end
|
176
180
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_api_implementation :ConsoleMessageImpl do
|
3
|
+
def initialize(event)
|
4
|
+
@event = event
|
5
|
+
end
|
6
|
+
|
7
|
+
def page
|
8
|
+
@page ||= ChannelOwners::Page.from_nullable(@event['page'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def type
|
12
|
+
@event['type']
|
13
|
+
end
|
14
|
+
|
15
|
+
def text
|
16
|
+
@event['text']
|
17
|
+
end
|
18
|
+
|
19
|
+
def args
|
20
|
+
@event['args']&.map do |arg|
|
21
|
+
ChannelOwner.from(arg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def location
|
26
|
+
@event['location']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|