playwright-ruby-client 1.37.1 → 1.39.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -7
  3. data/documentation/docs/api/browser.md +13 -9
  4. data/documentation/docs/api/browser_context.md +51 -32
  5. data/documentation/docs/api/browser_type.md +12 -7
  6. data/documentation/docs/api/dialog.md +18 -15
  7. data/documentation/docs/api/download.md +17 -7
  8. data/documentation/docs/api/element_handle.md +7 -19
  9. data/documentation/docs/api/frame.md +55 -30
  10. data/documentation/docs/api/keyboard.md +4 -0
  11. data/documentation/docs/api/locator.md +57 -16
  12. data/documentation/docs/api/page.md +102 -54
  13. data/documentation/docs/api/playwright.md +23 -20
  14. data/documentation/docs/api/request.md +17 -0
  15. data/documentation/docs/api/selectors.md +34 -29
  16. data/documentation/docs/include/api_coverage.md +1 -0
  17. data/lib/playwright/channel.rb +8 -0
  18. data/lib/playwright/channel_owner.rb +7 -2
  19. data/lib/playwright/channel_owners/browser_context.rb +16 -1
  20. data/lib/playwright/channel_owners/local_utils.rb +27 -0
  21. data/lib/playwright/channel_owners/page.rb +2 -0
  22. data/lib/playwright/channel_owners/playwright.rb +1 -24
  23. data/lib/playwright/channel_owners/request.rb +17 -1
  24. data/lib/playwright/channel_owners/route.rb +5 -1
  25. data/lib/playwright/connection.rb +1 -1
  26. data/lib/playwright/console_message_impl.rb +29 -0
  27. data/lib/playwright/errors.rb +13 -2
  28. data/lib/playwright/events.rb +1 -0
  29. data/lib/playwright/javascript/value_parser.rb +8 -0
  30. data/lib/playwright/javascript/value_serializer.rb +10 -4
  31. data/lib/playwright/locator_impl.rb +4 -0
  32. data/lib/playwright/utils.rb +4 -0
  33. data/lib/playwright/version.rb +2 -2
  34. data/lib/playwright_api/browser.rb +2 -2
  35. data/lib/playwright_api/browser_context.rb +4 -4
  36. data/lib/playwright_api/browser_type.rb +2 -2
  37. data/lib/playwright_api/console_message.rb +0 -22
  38. data/lib/playwright_api/dialog.rb +2 -2
  39. data/lib/playwright_api/download.rb +12 -3
  40. data/lib/playwright_api/element_handle.rb +2 -15
  41. data/lib/playwright_api/frame.rb +8 -13
  42. data/lib/playwright_api/keyboard.rb +4 -0
  43. data/lib/playwright_api/locator.rb +52 -16
  44. data/lib/playwright_api/page.rb +24 -29
  45. data/lib/playwright_api/playwright.rb +4 -4
  46. data/lib/playwright_api/request.rb +17 -0
  47. data/lib/playwright_api/selectors.rb +2 -2
  48. data/lib/playwright_api/worker.rb +4 -4
  49. data/sig/playwright.rbs +1 -0
  50. metadata +4 -4
  51. 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
- ```ruby
16
- playwright.webkit.launch do |browser|
17
- page = browser.new_page
18
- page.goto('https://example.com/')
19
- page.screenshot(path: 'screenshot.png')
20
- end
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
- ```ruby
466
- page.expose_binding("pageURL", ->(source) { source[:page].url })
467
- page.content = <<~HTML
468
- <script>
469
- async function onClick() {
470
- document.querySelector('div').textContent = await window.pageURL();
471
- }
472
- </script>
473
- <button onclick="onClick()">Click me</button>
474
- <div></div>
475
- HTML
476
- page.locator("button").click
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
- ```ruby
521
- require 'digest'
540
+ ```python sync title=example_0f68a39bdff02a3df161c74e81cabb8a2ff1f09f0d09f6ef9b799a6f2f19a280.py
541
+ import hashlib
542
+ from playwright.sync_api import sync_playwright, Playwright
522
543
 
523
- def sha1(text)
524
- Digest::SHA256.hexdigest(text)
525
- end
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 [Page#type](./page#type).
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
- ```ruby
1328
- # single selection matching the value
1329
- page.select_option("select#colors", value: "blue")
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: "blue")
1362
+ page.select_option("select#colors", label="blue")
1332
1363
  # multiple selection
1333
- page.select_option("select#colors", value: ["red", "green", "blue"])
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
- ```ruby
1653
- page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
1654
- page.wait_for_function("() => window.x > 0")
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
- ```ruby
1823
- %w[https://google.com https://bbc.com].each do |current_url|
1824
- page.goto(current_url, waitUntil: "domcontentloaded")
1825
- element = page.wait_for_selector("img")
1826
- puts "Loaded image: #{element["src"]}"
1827
- end
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
- ```ruby
12
- require 'playwright'
11
+ ```python sync title=example_6647e5a44b0440884026a6142606dfddad75ba1e643919b015457df4ed2e198f.py
12
+ from playwright.sync_api import sync_playwright, Playwright
13
13
 
14
- Playwright.create(playwright_cli_executable_path: 'npx playwright') do |playwright|
15
- chromium = playwright.chromium # or "firefox" or "webkit".
16
- chromium.launch do |browser|
17
- page = browser.new_page
18
- page.goto('https://example.com/')
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
- # other actions
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
- ```ruby
37
- require 'playwright'
37
+ ```python sync title=example_14d627977a4ad16a605ec5472d768a3324812fa8e7c57685561408fa6601e352.py
38
+ from playwright.sync_api import sync_playwright, Playwright
38
39
 
39
- Playwright.create(playwright_cli_executable_path: 'npx playwright') do |playwright|
40
- iphone = playwright.devices["iPhone 6"]
41
- playwright.webkit.launch do |browser|
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('https://example.com/')
45
+ page = context.new_page()
46
+ page.goto("http://example.com")
47
+ # other actions...
48
+ browser.close()
45
49
 
46
- # other actions
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
- ```ruby
25
- tag_selector = <<~JAVASCRIPT
26
- {
27
- // Returns the first element matching given selector in the root's subtree.
28
- query(root, selector) {
29
- return root.querySelector(selector);
30
- },
31
- // Returns all elements matching given selector in the root's subtree.
32
- queryAll(root, selector) {
33
- return Array.from(root.querySelectorAll(selector));
34
- }
35
- }
36
- JAVASCRIPT
37
-
38
- # Register the engine. Selectors will be prefixed with "tag=".
39
- playwright.selectors.register("tag", script: tag_selector)
40
- playwright.chromium.launch do |browser|
41
- page = browser.new_page
42
- page.content = '<div><button>Click me</button></div>'
43
-
44
- # Use the selector prefixed with its name.
45
- button = page.locator('tag=button')
46
- # Combine it with other selector engines.
47
- page.locator('tag=div').get_by_text('Click me').click
48
-
49
- # Can use it in any methods supporting selectors.
50
- button_count = page.locator('tag=button').count
51
- button_count # => 1
52
- end
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
  ```
@@ -470,6 +470,7 @@
470
470
  * or
471
471
  * page
472
472
  * press
473
+ * press_sequentially
473
474
  * screenshot
474
475
  * scroll_into_view_if_needed
475
476
  * select_option
@@ -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(ChannelOwners::ConsoleMessage.from(params['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 ||= @initializer['deviceDescriptors'].map do |item|
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 = request.frame.page.context.request
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
@@ -181,7 +181,7 @@ module Playwright
181
181
  end
182
182
 
183
183
  if method == "__dispose__"
184
- object.send(:dispose!)
184
+ object.send(:dispose!, reason: params["reason"])
185
185
  return
186
186
  end
187
187
 
@@ -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