playwright-ruby-client 1.38.0 → 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.
@@ -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
@@ -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
  ```
@@ -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,7 @@ 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
40
  })
41
41
  @channel.on('pageError', ->(params) {
42
42
  on_page_error(
@@ -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
@@ -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
@@ -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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '1.38.0'
5
- COMPATIBLE_PLAYWRIGHT_VERSION = '1.38.0'
4
+ VERSION = '1.39.0'
5
+ COMPATIBLE_PLAYWRIGHT_VERSION = '1.39.0'
6
6
  end
@@ -4,9 +4,9 @@ module Playwright
4
4
  # A Browser is created via [`method: BrowserType.launch`]. An example of using a `Browser` to create a `Page`:
5
5
  #
6
6
  # ```python sync
7
- # from playwright.sync_api import sync_playwright
7
+ # from playwright.sync_api import sync_playwright, Playwright
8
8
  #
9
- # def run(playwright):
9
+ # def run(playwright: Playwright):
10
10
  # firefox = playwright.firefox
11
11
  # browser = firefox.launch()
12
12
  # page = browser.new_page()
@@ -131,9 +131,9 @@ module Playwright
131
131
  # An example of exposing page URL to all frames in all pages in the context:
132
132
  #
133
133
  # ```python sync
134
- # from playwright.sync_api import sync_playwright
134
+ # from playwright.sync_api import sync_playwright, Playwright
135
135
  #
136
- # def run(playwright):
136
+ # def run(playwright: Playwright):
137
137
  # webkit = playwright.webkit
138
138
  # browser = webkit.launch(headless=false)
139
139
  # context = browser.new_context()
@@ -190,13 +190,13 @@ module Playwright
190
190
  # import hashlib
191
191
  # from playwright.sync_api import sync_playwright
192
192
  #
193
- # def sha256(text):
193
+ # def sha256(text: str) -> str:
194
194
  # m = hashlib.sha256()
195
195
  # m.update(bytes(text, "utf8"))
196
196
  # return m.hexdigest()
197
197
  #
198
198
  #
199
- # def run(playwright):
199
+ # def run(playwright: Playwright):
200
200
  # webkit = playwright.webkit
201
201
  # browser = webkit.launch(headless=False)
202
202
  # context = browser.new_context()
@@ -4,9 +4,9 @@ module Playwright
4
4
  # typical example of using Playwright to drive automation:
5
5
  #
6
6
  # ```python sync
7
- # from playwright.sync_api import sync_playwright
7
+ # from playwright.sync_api import sync_playwright, Playwright
8
8
  #
9
- # def run(playwright):
9
+ # def run(playwright: Playwright):
10
10
  # chromium = playwright.chromium
11
11
  # browser = chromium.launch()
12
12
  # page = browser.new_page()
@@ -52,27 +52,5 @@ module Playwright
52
52
  def type
53
53
  wrap_impl(@impl.type)
54
54
  end
55
-
56
- # -- inherited from EventEmitter --
57
- # @nodoc
58
- def off(event, callback)
59
- event_emitter_proxy.off(event, callback)
60
- end
61
-
62
- # -- inherited from EventEmitter --
63
- # @nodoc
64
- def once(event, callback)
65
- event_emitter_proxy.once(event, callback)
66
- end
67
-
68
- # -- inherited from EventEmitter --
69
- # @nodoc
70
- def on(event, callback)
71
- event_emitter_proxy.on(event, callback)
72
- end
73
-
74
- private def event_emitter_proxy
75
- @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
76
- end
77
55
  end
78
56
  end
@@ -5,13 +5,13 @@ module Playwright
5
5
  # An example of using `Dialog` class:
6
6
  #
7
7
  # ```python sync
8
- # from playwright.sync_api import sync_playwright
8
+ # from playwright.sync_api import sync_playwright, Playwright
9
9
  #
10
10
  # def handle_dialog(dialog):
11
11
  # print(dialog.message)
12
12
  # dialog.dismiss()
13
13
  #
14
- # def run(playwright):
14
+ # def run(playwright: Playwright):
15
15
  # chromium = playwright.chromium
16
16
  # browser = chromium.launch()
17
17
  # page = browser.new_page()
@@ -407,7 +407,7 @@ module Playwright
407
407
  # **Usage**
408
408
  #
409
409
  # ```python sync
410
- # # single selection matching the value
410
+ # # Single selection matching the value or label
411
411
  # handle.select_option("blue")
412
412
  # # single selection matching both the label
413
413
  # handle.select_option(label="blue")
@@ -11,9 +11,9 @@ module Playwright
11
11
  # An example of dumping frame tree:
12
12
  #
13
13
  # ```python sync
14
- # from playwright.sync_api import sync_playwright
14
+ # from playwright.sync_api import sync_playwright, Playwright
15
15
  #
16
- # def run(playwright):
16
+ # def run(playwright: Playwright):
17
17
  # firefox = playwright.firefox
18
18
  # browser = firefox.launch()
19
19
  # page = browser.new_page()
@@ -757,7 +757,7 @@ module Playwright
757
757
  # **Usage**
758
758
  #
759
759
  # ```python sync
760
- # # single selection matching the value
760
+ # # Single selection matching the value or label
761
761
  # frame.select_option("select#colors", "blue")
762
762
  # # single selection matching both the label
763
763
  # frame.select_option("select#colors", label="blue")
@@ -914,9 +914,9 @@ module Playwright
914
914
  # The [`method: Frame.waitForFunction`] can be used to observe viewport size change:
915
915
  #
916
916
  # ```python sync
917
- # from playwright.sync_api import sync_playwright
917
+ # from playwright.sync_api import sync_playwright, Playwright
918
918
  #
919
- # def run(playwright):
919
+ # def run(playwright: Playwright):
920
920
  # webkit = playwright.webkit
921
921
  # browser = webkit.launch()
922
922
  # page = browser.new_page()
@@ -993,9 +993,9 @@ module Playwright
993
993
  # This method works across navigations:
994
994
  #
995
995
  # ```python sync
996
- # from playwright.sync_api import sync_playwright
996
+ # from playwright.sync_api import sync_playwright, Playwright
997
997
  #
998
- # def run(playwright):
998
+ # def run(playwright: Playwright):
999
999
  # chromium = playwright.chromium
1000
1000
  # browser = chromium.launch()
1001
1001
  # page = browser.new_page()
@@ -28,6 +28,8 @@ module Playwright
28
28
  #
29
29
  # Returns an array of `node.innerText` values for all matching nodes.
30
30
  #
31
+ # **NOTE**: If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with `useInnerText` option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
32
+ #
31
33
  # **Usage**
32
34
  #
33
35
  # ```python sync
@@ -40,6 +42,8 @@ module Playwright
40
42
  #
41
43
  # Returns an array of `node.textContent` values for all matching nodes.
42
44
  #
45
+ # **NOTE**: If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
46
+ #
43
47
  # **Usage**
44
48
  #
45
49
  # ```python sync
@@ -192,6 +196,8 @@ module Playwright
192
196
  #
193
197
  # Returns the number of elements matching the locator.
194
198
  #
199
+ # **NOTE**: If you need to assert the number of elements on the page, prefer [`method: LocatorAssertions.toHaveCount`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
200
+ #
195
201
  # **Usage**
196
202
  #
197
203
  # ```python sync
@@ -441,6 +447,8 @@ module Playwright
441
447
 
442
448
  #
443
449
  # Returns the matching element's attribute value.
450
+ #
451
+ # **NOTE**: If you need to assert an element's attribute, prefer [`method: LocatorAssertions.toHaveAttribute`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
444
452
  def get_attribute(name, timeout: nil)
445
453
  wrap_impl(@impl.get_attribute(unwrap_impl(name), timeout: unwrap_impl(timeout)))
446
454
  end
@@ -681,6 +689,8 @@ module Playwright
681
689
 
682
690
  #
683
691
  # Returns the [`element.innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText).
692
+ #
693
+ # **NOTE**: If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with `useInnerText` option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
684
694
  def inner_text(timeout: nil)
685
695
  wrap_impl(@impl.inner_text(timeout: unwrap_impl(timeout)))
686
696
  end
@@ -688,6 +698,8 @@ module Playwright
688
698
  #
689
699
  # Returns the value for the matching `<input>` or `<textarea>` or `<select>` element.
690
700
  #
701
+ # **NOTE**: If you need to assert input value, prefer [`method: LocatorAssertions.toHaveValue`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
702
+ #
691
703
  # **Usage**
692
704
  #
693
705
  # ```python sync
@@ -704,6 +716,8 @@ module Playwright
704
716
  #
705
717
  # Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
706
718
  #
719
+ # **NOTE**: If you need to assert that checkbox is checked, prefer [`method: LocatorAssertions.toBeChecked`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
720
+ #
707
721
  # **Usage**
708
722
  #
709
723
  # ```python sync
@@ -716,6 +730,8 @@ module Playwright
716
730
  #
717
731
  # Returns whether the element is disabled, the opposite of [enabled](../actionability.md#enabled).
718
732
  #
733
+ # **NOTE**: If you need to assert that an element is disabled, prefer [`method: LocatorAssertions.toBeDisabled`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
734
+ #
719
735
  # **Usage**
720
736
  #
721
737
  # ```python sync
@@ -728,6 +744,8 @@ module Playwright
728
744
  #
729
745
  # Returns whether the element is [editable](../actionability.md#editable).
730
746
  #
747
+ # **NOTE**: If you need to assert that an element is editable, prefer [`method: LocatorAssertions.toBeEditable`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
748
+ #
731
749
  # **Usage**
732
750
  #
733
751
  # ```python sync
@@ -740,6 +758,8 @@ module Playwright
740
758
  #
741
759
  # Returns whether the element is [enabled](../actionability.md#enabled).
742
760
  #
761
+ # **NOTE**: If you need to assert that an element is enabled, prefer [`method: LocatorAssertions.toBeEnabled`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
762
+ #
743
763
  # **Usage**
744
764
  #
745
765
  # ```python sync
@@ -752,6 +772,8 @@ module Playwright
752
772
  #
753
773
  # Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible).
754
774
  #
775
+ # **NOTE**: If you need to assert that element is hidden, prefer [`method: LocatorAssertions.toBeHidden`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
776
+ #
755
777
  # **Usage**
756
778
  #
757
779
  # ```python sync
@@ -764,6 +786,8 @@ module Playwright
764
786
  #
765
787
  # Returns whether the element is [visible](../actionability.md#visible).
766
788
  #
789
+ # **NOTE**: If you need to assert that element is visible, prefer [`method: LocatorAssertions.toBeVisible`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
790
+ #
767
791
  # **Usage**
768
792
  #
769
793
  # ```python sync
@@ -1087,6 +1111,8 @@ module Playwright
1087
1111
 
1088
1112
  #
1089
1113
  # Returns the [`node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent).
1114
+ #
1115
+ # **NOTE**: If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
1090
1116
  def text_content(timeout: nil)
1091
1117
  wrap_impl(@impl.text_content(timeout: unwrap_impl(timeout)))
1092
1118
  end