playwright-ruby-client 0.0.5 → 0.1.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -2
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +7 -1
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +3 -2
  9. data/lib/playwright/channel_owners/android.rb +10 -1
  10. data/lib/playwright/channel_owners/android_device.rb +163 -0
  11. data/lib/playwright/channel_owners/browser.rb +13 -13
  12. data/lib/playwright/channel_owners/browser_context.rb +9 -1
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  15. data/lib/playwright/channel_owners/frame.rb +371 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +416 -19
  18. data/lib/playwright/channel_owners/request.rb +98 -0
  19. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  20. data/lib/playwright/connection.rb +9 -6
  21. data/lib/playwright/errors.rb +2 -2
  22. data/lib/playwright/event_emitter.rb +8 -1
  23. data/lib/playwright/event_emitter_proxy.rb +49 -0
  24. data/lib/playwright/file_chooser_impl.rb +23 -0
  25. data/lib/playwright/http_headers.rb +20 -0
  26. data/lib/playwright/input_files.rb +42 -0
  27. data/lib/playwright/javascript/expression.rb +37 -0
  28. data/lib/playwright/javascript/function.rb +37 -0
  29. data/lib/playwright/javascript/value_parser.rb +1 -1
  30. data/lib/playwright/javascript/value_serializer.rb +11 -11
  31. data/lib/playwright/keyboard_impl.rb +36 -0
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +84 -29
  34. data/lib/playwright/select_option_values.rb +32 -0
  35. data/lib/playwright/timeout_settings.rb +2 -2
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/url_matcher.rb +19 -0
  38. data/lib/playwright/utils.rb +18 -0
  39. data/lib/playwright/version.rb +1 -1
  40. data/lib/playwright/wait_helper.rb +1 -1
  41. data/lib/playwright_api/accessibility.rb +46 -6
  42. data/lib/playwright_api/android.rb +37 -0
  43. data/lib/playwright_api/android_device.rb +82 -0
  44. data/lib/playwright_api/android_input.rb +25 -0
  45. data/lib/playwright_api/binding_call.rb +10 -6
  46. data/lib/playwright_api/browser.rb +85 -18
  47. data/lib/playwright_api/browser_context.rb +269 -37
  48. data/lib/playwright_api/browser_type.rb +60 -11
  49. data/lib/playwright_api/cdp_session.rb +23 -1
  50. data/lib/playwright_api/chromium_browser_context.rb +18 -6
  51. data/lib/playwright_api/console_message.rb +14 -15
  52. data/lib/playwright_api/dialog.rb +48 -2
  53. data/lib/playwright_api/download.rb +47 -10
  54. data/lib/playwright_api/element_handle.rb +269 -110
  55. data/lib/playwright_api/file_chooser.rb +23 -7
  56. data/lib/playwright_api/frame.rb +439 -154
  57. data/lib/playwright_api/js_handle.rb +69 -24
  58. data/lib/playwright_api/keyboard.rb +99 -9
  59. data/lib/playwright_api/mouse.rb +22 -0
  60. data/lib/playwright_api/page.rb +856 -229
  61. data/lib/playwright_api/playwright.rb +108 -20
  62. data/lib/playwright_api/request.rb +77 -29
  63. data/lib/playwright_api/response.rb +10 -13
  64. data/lib/playwright_api/route.rb +49 -0
  65. data/lib/playwright_api/selectors.rb +20 -8
  66. data/lib/playwright_api/video.rb +8 -0
  67. data/lib/playwright_api/web_socket.rb +0 -8
  68. data/lib/playwright_api/worker.rb +25 -13
  69. data/playwright.gemspec +1 -0
  70. metadata +33 -2
@@ -14,6 +14,39 @@ module Playwright
14
14
  # await browser.close();
15
15
  # })();
16
16
  # ```
17
+ #
18
+ # ```python async
19
+ # import asyncio
20
+ # from playwright.async_api import async_playwright
21
+ #
22
+ # async def run(playwright):
23
+ # chromium = playwright.chromium
24
+ # browser = await chromium.launch()
25
+ # page = await browser.new_page()
26
+ # await page.goto("https://example.com")
27
+ # # other actions...
28
+ # await browser.close()
29
+ #
30
+ # async def main():
31
+ # async with async_playwright() as playwright:
32
+ # await run(playwright)
33
+ # asyncio.run(main())
34
+ # ```
35
+ #
36
+ # ```python sync
37
+ # from playwright.sync_api import sync_playwright
38
+ #
39
+ # def run(playwright):
40
+ # chromium = playwright.chromium
41
+ # browser = chromium.launch()
42
+ # page = browser.new_page()
43
+ # page.goto("https://example.com")
44
+ # # other actions...
45
+ # browser.close()
46
+ #
47
+ # with sync_playwright() as playwright:
48
+ # run(playwright)
49
+ # ```
17
50
  class BrowserType < PlaywrightApi
18
51
 
19
52
  # This methods attaches Playwright to an existing browser instance.
@@ -23,7 +56,7 @@ module Playwright
23
56
 
24
57
  # A path where Playwright expects to find a bundled browser executable.
25
58
  def executable_path
26
- wrap_channel_owner(@channel_owner.executable_path)
59
+ wrap_impl(@impl.executable_path)
27
60
  end
28
61
 
29
62
  # Returns the browser instance.
@@ -37,6 +70,18 @@ module Playwright
37
70
  # });
38
71
  # ```
39
72
  #
73
+ # ```python async
74
+ # browser = await playwright.chromium.launch( # or "firefox" or "webkit".
75
+ # ignore_default_args=["--mute-audio"]
76
+ # )
77
+ # ```
78
+ #
79
+ # ```python sync
80
+ # browser = playwright.chromium.launch( # or "firefox" or "webkit".
81
+ # ignore_default_args=["--mute-audio"]
82
+ # )
83
+ # ```
84
+ #
40
85
  # > **Chromium-only** Playwright can also be used to control the Chrome browser, but it works best with the version of
41
86
  # Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` option with
42
87
  # extreme caution.
@@ -63,13 +108,14 @@ module Playwright
63
108
  handleSIGINT: nil,
64
109
  handleSIGTERM: nil,
65
110
  headless: nil,
111
+ ignoreAllDefaultArgs: nil,
66
112
  ignoreDefaultArgs: nil,
67
113
  logger: nil,
68
114
  proxy: nil,
69
115
  slowMo: nil,
70
116
  timeout: nil,
71
117
  &block)
72
- wrap_channel_owner(@channel_owner.launch(args: args, chromiumSandbox: chromiumSandbox, devtools: devtools, downloadsPath: downloadsPath, env: env, executablePath: executablePath, firefoxUserPrefs: firefoxUserPrefs, handleSIGHUP: handleSIGHUP, handleSIGINT: handleSIGINT, handleSIGTERM: handleSIGTERM, headless: headless, ignoreDefaultArgs: ignoreDefaultArgs, logger: logger, proxy: proxy, slowMo: slowMo, timeout: timeout, &wrap_block_call(block)))
118
+ wrap_impl(@impl.launch(args: unwrap_impl(args), chromiumSandbox: unwrap_impl(chromiumSandbox), devtools: unwrap_impl(devtools), downloadsPath: unwrap_impl(downloadsPath), env: unwrap_impl(env), executablePath: unwrap_impl(executablePath), firefoxUserPrefs: unwrap_impl(firefoxUserPrefs), handleSIGHUP: unwrap_impl(handleSIGHUP), handleSIGINT: unwrap_impl(handleSIGINT), handleSIGTERM: unwrap_impl(handleSIGTERM), headless: unwrap_impl(headless), ignoreAllDefaultArgs: unwrap_impl(ignoreAllDefaultArgs), ignoreDefaultArgs: unwrap_impl(ignoreDefaultArgs), logger: unwrap_impl(logger), proxy: unwrap_impl(proxy), slowMo: unwrap_impl(slowMo), timeout: unwrap_impl(timeout), &wrap_block_call(block)))
73
119
  end
74
120
 
75
121
  # Returns the persistent browser context instance.
@@ -96,6 +142,7 @@ module Playwright
96
142
  hasTouch: nil,
97
143
  headless: nil,
98
144
  httpCredentials: nil,
145
+ ignoreAllDefaultArgs: nil,
99
146
  ignoreDefaultArgs: nil,
100
147
  ignoreHTTPSErrors: nil,
101
148
  isMobile: nil,
@@ -140,14 +187,12 @@ module Playwright
140
187
  chromiumSandbox: nil,
141
188
  devtools: nil,
142
189
  downloadsPath: nil,
143
- env: nil,
144
190
  executablePath: nil,
145
191
  firefoxUserPrefs: nil,
146
192
  handleSIGHUP: nil,
147
193
  handleSIGINT: nil,
148
194
  handleSIGTERM: nil,
149
195
  headless: nil,
150
- ignoreDefaultArgs: nil,
151
196
  logger: nil,
152
197
  port: nil,
153
198
  proxy: nil,
@@ -157,25 +202,29 @@ module Playwright
157
202
 
158
203
  # Returns browser name. For example: `'chromium'`, `'webkit'` or `'firefox'`.
159
204
  def name
160
- wrap_channel_owner(@channel_owner.name)
205
+ wrap_impl(@impl.name)
161
206
  end
162
207
 
163
208
  # -- inherited from EventEmitter --
164
209
  # @nodoc
165
- def off(event, callback)
166
- wrap_channel_owner(@channel_owner.off(event, callback))
210
+ def once(event, callback)
211
+ event_emitter_proxy.once(event, callback)
167
212
  end
168
213
 
169
214
  # -- inherited from EventEmitter --
170
215
  # @nodoc
171
- def once(event, callback)
172
- wrap_channel_owner(@channel_owner.once(event, callback))
216
+ def on(event, callback)
217
+ event_emitter_proxy.on(event, callback)
173
218
  end
174
219
 
175
220
  # -- inherited from EventEmitter --
176
221
  # @nodoc
177
- def on(event, callback)
178
- wrap_channel_owner(@channel_owner.on(event, callback))
222
+ def off(event, callback)
223
+ event_emitter_proxy.off(event, callback)
224
+ end
225
+
226
+ private def event_emitter_proxy
227
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
179
228
  end
180
229
  end
181
230
  end
@@ -1,5 +1,5 @@
1
1
  module Playwright
2
- # - extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
2
+ # - extends: [EventEmitter]
3
3
  #
4
4
  # The `CDPSession` instances are used to talk raw Chrome Devtools Protocol:
5
5
  # - protocol methods can be called with `session.send` method.
@@ -22,6 +22,28 @@ module Playwright
22
22
  # playbackRate: response.playbackRate / 2
23
23
  # });
24
24
  # ```
25
+ #
26
+ # ```python async
27
+ # client = await page.context().new_cdp_session(page)
28
+ # await client.send("animation.enable")
29
+ # client.on("animation.animation_created", lambda: print("animation created!"))
30
+ # response = await client.send("animation.get_playback_rate")
31
+ # print("playback rate is " + response["playback_rate"])
32
+ # await client.send("animation.set_playback_rate", {
33
+ # playback_rate: response["playback_rate"] / 2
34
+ # })
35
+ # ```
36
+ #
37
+ # ```python sync
38
+ # client = page.context().new_cdp_session(page)
39
+ # client.send("animation.enable")
40
+ # client.on("animation.animation_created", lambda: print("animation created!"))
41
+ # response = client.send("animation.get_playback_rate")
42
+ # print("playback rate is " + response["playback_rate"])
43
+ # client.send("animation.set_playback_rate", {
44
+ # playback_rate: response["playback_rate"] / 2
45
+ # })
46
+ # ```
25
47
  class CDPSession < PlaywrightApi
26
48
 
27
49
  # Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be used to
@@ -9,6 +9,14 @@ module Playwright
9
9
  # ```js
10
10
  # const backgroundPage = await context.waitForEvent('backgroundpage');
11
11
  # ```
12
+ #
13
+ # ```python async
14
+ # background_page = await context.wait_for_event("backgroundpage")
15
+ # ```
16
+ #
17
+ # ```python sync
18
+ # background_page = context.wait_for_event("backgroundpage")
19
+ # ```
12
20
  class ChromiumBrowserContext < BrowserContext
13
21
 
14
22
  # All existing background pages in the context.
@@ -28,20 +36,24 @@ module Playwright
28
36
 
29
37
  # -- inherited from EventEmitter --
30
38
  # @nodoc
31
- def off(event, callback)
32
- wrap_channel_owner(@channel_owner.off(event, callback))
39
+ def once(event, callback)
40
+ event_emitter_proxy.once(event, callback)
33
41
  end
34
42
 
35
43
  # -- inherited from EventEmitter --
36
44
  # @nodoc
37
- def once(event, callback)
38
- wrap_channel_owner(@channel_owner.once(event, callback))
45
+ def on(event, callback)
46
+ event_emitter_proxy.on(event, callback)
39
47
  end
40
48
 
41
49
  # -- inherited from EventEmitter --
42
50
  # @nodoc
43
- def on(event, callback)
44
- wrap_channel_owner(@channel_owner.on(event, callback))
51
+ def off(event, callback)
52
+ event_emitter_proxy.off(event, callback)
53
+ end
54
+
55
+ private def event_emitter_proxy
56
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
45
57
  end
46
58
  end
47
59
  end
@@ -3,45 +3,44 @@ module Playwright
3
3
  class ConsoleMessage < PlaywrightApi
4
4
 
5
5
  def args
6
- wrap_channel_owner(@channel_owner.args)
6
+ wrap_impl(@impl.args)
7
7
  end
8
8
 
9
9
  def location
10
- wrap_channel_owner(@channel_owner.location)
10
+ wrap_impl(@impl.location)
11
11
  end
12
12
 
13
13
  def text
14
- wrap_channel_owner(@channel_owner.text)
14
+ wrap_impl(@impl.text)
15
15
  end
16
16
 
17
17
  # One of the following values: `'log'`, `'debug'`, `'info'`, `'error'`, `'warning'`, `'dir'`, `'dirxml'`, `'table'`,
18
18
  # `'trace'`, `'clear'`, `'startGroup'`, `'startGroupCollapsed'`, `'endGroup'`, `'assert'`, `'profile'`, `'profileEnd'`,
19
19
  # `'count'`, `'timeEnd'`.
20
- def type_text
21
- raise NotImplementedError.new('type_text is not implemented yet.')
22
- end
23
-
24
- # @nodoc
25
20
  def type
26
- wrap_channel_owner(@channel_owner.type)
21
+ wrap_impl(@impl.type)
27
22
  end
28
23
 
29
24
  # -- inherited from EventEmitter --
30
25
  # @nodoc
31
- def off(event, callback)
32
- wrap_channel_owner(@channel_owner.off(event, callback))
26
+ def once(event, callback)
27
+ event_emitter_proxy.once(event, callback)
33
28
  end
34
29
 
35
30
  # -- inherited from EventEmitter --
36
31
  # @nodoc
37
- def once(event, callback)
38
- wrap_channel_owner(@channel_owner.once(event, callback))
32
+ def on(event, callback)
33
+ event_emitter_proxy.on(event, callback)
39
34
  end
40
35
 
41
36
  # -- inherited from EventEmitter --
42
37
  # @nodoc
43
- def on(event, callback)
44
- wrap_channel_owner(@channel_owner.on(event, callback))
38
+ def off(event, callback)
39
+ event_emitter_proxy.off(event, callback)
40
+ end
41
+
42
+ private def event_emitter_proxy
43
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
45
44
  end
46
45
  end
47
46
  end
@@ -18,6 +18,52 @@ module Playwright
18
18
  # page.evaluate(() => alert('1'));
19
19
  # })();
20
20
  # ```
21
+ #
22
+ # ```python async
23
+ # import asyncio
24
+ # from playwright.async_api import async_playwright
25
+ #
26
+ # async def handle_dialog(dialog):
27
+ # print(dialog.message)
28
+ # await dialog.dismiss()
29
+ #
30
+ # async def run(playwright):
31
+ # chromium = playwright.chromium
32
+ # browser = await chromium.launch()
33
+ # page = await browser.new_page()
34
+ # page.on("dialog", handle_dialog)
35
+ # page.evaluate("alert('1')")
36
+ # await browser.close()
37
+ #
38
+ # async def main():
39
+ # async with async_playwright() as playwright:
40
+ # await run(playwright)
41
+ # asyncio.run(main())
42
+ # ```
43
+ #
44
+ # ```python sync
45
+ # from playwright.sync_api import sync_playwright
46
+ #
47
+ # def handle_dialog(dialog):
48
+ # print(dialog.message)
49
+ # dialog.dismiss()
50
+ #
51
+ # def run(playwright):
52
+ # chromium = playwright.chromium
53
+ # browser = chromium.launch()
54
+ # page = browser.new_page()
55
+ # page.on("dialog", handle_dialog)
56
+ # page.evaluate("alert('1')")
57
+ # browser.close()
58
+ #
59
+ # with sync_playwright() as playwright:
60
+ # run(playwright)
61
+ # ```
62
+ #
63
+ # > NOTE: Dialogs are dismissed automatically, unless there is a [`event: Page.dialog`] listener. When listener is
64
+ # present, it **must** either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will
65
+ # [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
66
+ # actions like click will never finish.
21
67
  class Dialog < PlaywrightApi
22
68
 
23
69
  # Returns when the dialog has been accepted.
@@ -41,8 +87,8 @@ module Playwright
41
87
  end
42
88
 
43
89
  # Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.
44
- def type_text
45
- raise NotImplementedError.new('type_text is not implemented yet.')
90
+ def type
91
+ raise NotImplementedError.new('type is not implemented yet.')
46
92
  end
47
93
  end
48
94
  end
@@ -14,12 +14,27 @@ module Playwright
14
14
  # ]);
15
15
  # // wait for download to complete
16
16
  # const path = await download.path();
17
- # ...
18
17
  # ```
19
18
  #
20
- # > **NOTE** Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the
21
- # downloaded content. If `acceptDownloads` is not set or set to `false`, download events are emitted, but the actual
22
- # download is not performed and user has no access to the downloaded files.
19
+ # ```python async
20
+ # async with page.expect_download() as download_info:
21
+ # await page.click("a")
22
+ # download = await download_info.value
23
+ # # waits for download to complete
24
+ # path = await download.path()
25
+ # ```
26
+ #
27
+ # ```python sync
28
+ # with page.expect_download() as download_info:
29
+ # page.click("a")
30
+ # download = download_info.value
31
+ # # wait for download to complete
32
+ # path = download.path()
33
+ # ```
34
+ #
35
+ # > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the
36
+ # downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not
37
+ # performed and user has no access to the downloaded files.
23
38
  class Download < PlaywrightApi
24
39
 
25
40
  # Returns readable stream for current download or `null` if download failed.
@@ -29,22 +44,22 @@ module Playwright
29
44
 
30
45
  # Deletes the downloaded file.
31
46
  def delete
32
- raise NotImplementedError.new('delete is not implemented yet.')
47
+ wrap_impl(@impl.delete)
33
48
  end
34
49
 
35
50
  # Returns download error if any.
36
51
  def failure
37
- raise NotImplementedError.new('failure is not implemented yet.')
52
+ wrap_impl(@impl.failure)
38
53
  end
39
54
 
40
55
  # Returns path to the downloaded file in case of successful download.
41
56
  def path
42
- raise NotImplementedError.new('path is not implemented yet.')
57
+ wrap_impl(@impl.path)
43
58
  end
44
59
 
45
60
  # Saves the download to a user-specified path.
46
61
  def save_as(path)
47
- raise NotImplementedError.new('save_as is not implemented yet.')
62
+ wrap_impl(@impl.save_as(unwrap_impl(path)))
48
63
  end
49
64
 
50
65
  # Returns suggested filename for this download. It is typically computed by the browser from the
@@ -52,12 +67,34 @@ module Playwright
52
67
  # or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources). Different
53
68
  # browsers can use different logic for computing it.
54
69
  def suggested_filename
55
- raise NotImplementedError.new('suggested_filename is not implemented yet.')
70
+ wrap_impl(@impl.suggested_filename)
56
71
  end
57
72
 
58
73
  # Returns downloaded url.
59
74
  def url
60
- raise NotImplementedError.new('url is not implemented yet.')
75
+ wrap_impl(@impl.url)
76
+ end
77
+
78
+ # -- inherited from EventEmitter --
79
+ # @nodoc
80
+ def once(event, callback)
81
+ event_emitter_proxy.once(event, callback)
82
+ end
83
+
84
+ # -- inherited from EventEmitter --
85
+ # @nodoc
86
+ def on(event, callback)
87
+ event_emitter_proxy.on(event, callback)
88
+ end
89
+
90
+ # -- inherited from EventEmitter --
91
+ # @nodoc
92
+ def off(event, callback)
93
+ event_emitter_proxy.off(event, callback)
94
+ end
95
+
96
+ private def event_emitter_proxy
97
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
61
98
  end
62
99
  end
63
100
  end
@@ -3,7 +3,8 @@ require_relative './js_handle.rb'
3
3
  module Playwright
4
4
  # - extends: `JSHandle`
5
5
  #
6
- # ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.$`] method.
6
+ # ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.querySelector`]
7
+ # method.
7
8
  #
8
9
  #
9
10
  # ```js
@@ -19,72 +20,48 @@ module Playwright
19
20
  # })();
20
21
  # ```
21
22
  #
23
+ # ```python async
24
+ # import asyncio
25
+ # from playwright.async_api import async_playwright
26
+ #
27
+ # async def run(playwright):
28
+ # chromium = playwright.chromium
29
+ # browser = await chromium.launch()
30
+ # page = await browser.new_page()
31
+ # await page.goto("https://example.com")
32
+ # href_element = await page.query_selector("a")
33
+ # await href_element.click()
34
+ # # ...
35
+ #
36
+ # async def main():
37
+ # async with async_playwright() as playwright:
38
+ # await run(playwright)
39
+ # asyncio.run(main())
40
+ # ```
41
+ #
42
+ # ```python sync
43
+ # from playwright.sync_api import sync_playwright
44
+ #
45
+ # def run(playwright):
46
+ # chromium = playwright.chromium
47
+ # browser = chromium.launch()
48
+ # page = browser.new_page()
49
+ # page.goto("https://example.com")
50
+ # href_element = page.query_selector("a")
51
+ # href_element.click()
52
+ # # ...
53
+ #
54
+ # with sync_playwright() as playwright:
55
+ # run(playwright)
56
+ # ```
57
+ #
22
58
  # ElementHandle prevents DOM element from garbage collection unless the handle is disposed with
23
59
  # [`method: JSHandle.dispose`]. ElementHandles are auto-disposed when their origin frame gets navigated.
24
60
  #
25
- # ElementHandle instances can be used as an argument in [`method: Page.$eval`] and [`method: Page.evaluate`] methods.
61
+ # ElementHandle instances can be used as an argument in [`method: Page.evalOnSelector`] and [`method: Page.evaluate`]
62
+ # methods.
26
63
  class ElementHandle < JSHandle
27
64
 
28
- # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
29
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
30
- # returns `null`.
31
- def query_selector(selector)
32
- raise NotImplementedError.new('query_selector is not implemented yet.')
33
- end
34
-
35
- # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
36
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
37
- # returns empty array.
38
- def query_selector_all(selector)
39
- raise NotImplementedError.new('query_selector_all is not implemented yet.')
40
- end
41
-
42
- # Returns the return value of `pageFunction`
43
- #
44
- # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
45
- # argument to `pageFunction`. See [Working with selectors](./selectors.md#working-with-selectors) for more details. If no
46
- # elements match the selector, the method throws an error.
47
- #
48
- # If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
49
- #
50
- # Examples:
51
- #
52
- #
53
- # ```js
54
- # const tweetHandle = await page.$('.tweet');
55
- # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
56
- # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
57
- # ```
58
- def eval_on_selector(selector, pageFunction, arg: nil)
59
- raise NotImplementedError.new('eval_on_selector is not implemented yet.')
60
- end
61
-
62
- # Returns the return value of `pageFunction`
63
- #
64
- # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
65
- # matched elements as a first argument to `pageFunction`. See
66
- # [Working with selectors](./selectors.md#working-with-selectors) for more details.
67
- #
68
- # If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
69
- #
70
- # Examples:
71
- #
72
- # ```html
73
- # <div class="feed">
74
- # <div class="tweet">Hello!</div>
75
- # <div class="tweet">Hi!</div>
76
- # </div>
77
- # ```
78
- #
79
- #
80
- # ```js
81
- # const feedHandle = await page.$('.feed');
82
- # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
83
- # ```
84
- def eval_on_selector_all(selector, pageFunction, arg: nil)
85
- raise NotImplementedError.new('eval_on_selector_all is not implemented yet.')
86
- end
87
-
88
65
  # This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is
89
66
  # calculated relative to the main frame viewport - which is usually the same as the browser window.
90
67
  #
@@ -103,8 +80,18 @@ module Playwright
103
80
  # const box = await elementHandle.boundingBox();
104
81
  # await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
105
82
  # ```
83
+ #
84
+ # ```python async
85
+ # box = await element_handle.bounding_box()
86
+ # await page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
87
+ # ```
88
+ #
89
+ # ```python sync
90
+ # box = element_handle.bounding_box()
91
+ # page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
92
+ # ```
106
93
  def bounding_box
107
- raise NotImplementedError.new('bounding_box is not implemented yet.')
94
+ wrap_impl(@impl.bounding_box)
108
95
  end
109
96
 
110
97
  # This method checks the element by performing the following steps:
@@ -121,7 +108,7 @@ module Playwright
121
108
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
122
109
  # Passing zero timeout disables this.
123
110
  def check(force: nil, noWaitAfter: nil, timeout: nil)
124
- raise NotImplementedError.new('check is not implemented yet.')
111
+ wrap_impl(@impl.check(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
125
112
  end
126
113
 
127
114
  # This method clicks the element by performing the following steps:
@@ -143,12 +130,12 @@ module Playwright
143
130
  noWaitAfter: nil,
144
131
  position: nil,
145
132
  timeout: nil)
146
- raise NotImplementedError.new('click is not implemented yet.')
133
+ wrap_impl(@impl.click(button: unwrap_impl(button), clickCount: unwrap_impl(clickCount), delay: unwrap_impl(delay), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
147
134
  end
148
135
 
149
136
  # Returns the content frame for element handles referencing iframe nodes, or `null` otherwise
150
137
  def content_frame
151
- raise NotImplementedError.new('content_frame is not implemented yet.')
138
+ wrap_impl(@impl.content_frame)
152
139
  end
153
140
 
154
141
  # This method double clicks the element by performing the following steps:
@@ -163,7 +150,7 @@ module Playwright
163
150
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
164
151
  # Passing zero timeout disables this.
165
152
  #
166
- # > **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.
153
+ # > NOTE: `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.
167
154
  def dblclick(
168
155
  button: nil,
169
156
  delay: nil,
@@ -172,7 +159,7 @@ module Playwright
172
159
  noWaitAfter: nil,
173
160
  position: nil,
174
161
  timeout: nil)
175
- raise NotImplementedError.new('dblclick is not implemented yet.')
162
+ wrap_impl(@impl.dblclick(button: unwrap_impl(button), delay: unwrap_impl(delay), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
176
163
  end
177
164
 
178
165
  # The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the elment, `click`
@@ -184,6 +171,14 @@ module Playwright
184
171
  # await elementHandle.dispatchEvent('click');
185
172
  # ```
186
173
  #
174
+ # ```python async
175
+ # await element_handle.dispatch_event("click")
176
+ # ```
177
+ #
178
+ # ```python sync
179
+ # element_handle.dispatch_event("click")
180
+ # ```
181
+ #
187
182
  # Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties
188
183
  # and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.
189
184
  #
@@ -204,26 +199,110 @@ module Playwright
204
199
  # const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
205
200
  # await elementHandle.dispatchEvent('dragstart', { dataTransfer });
206
201
  # ```
202
+ #
203
+ # ```python async
204
+ # # note you can only create data_transfer in chromium and firefox
205
+ # data_transfer = await page.evaluate_handle("new DataTransfer()")
206
+ # await element_handle.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
207
+ # ```
208
+ #
209
+ # ```python sync
210
+ # # note you can only create data_transfer in chromium and firefox
211
+ # data_transfer = page.evaluate_handle("new DataTransfer()")
212
+ # element_handle.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
213
+ # ```
207
214
  def dispatch_event(type, eventInit: nil)
208
- raise NotImplementedError.new('dispatch_event is not implemented yet.')
215
+ wrap_impl(@impl.dispatch_event(unwrap_impl(type), eventInit: unwrap_impl(eventInit)))
216
+ end
217
+
218
+ # Returns the return value of `expression`.
219
+ #
220
+ # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
221
+ # argument to `expression`. See [Working with selectors](./selectors.md) for more details. If no elements match the
222
+ # selector, the method throws an error.
223
+ #
224
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelector`] would wait for the promise to resolve
225
+ # and return its value.
226
+ #
227
+ # Examples:
228
+ #
229
+ #
230
+ # ```js
231
+ # const tweetHandle = await page.$('.tweet');
232
+ # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
233
+ # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
234
+ # ```
235
+ #
236
+ # ```python async
237
+ # tweet_handle = await page.query_selector(".tweet")
238
+ # assert await tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
239
+ # assert await tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
240
+ # ```
241
+ #
242
+ # ```python sync
243
+ # tweet_handle = page.query_selector(".tweet")
244
+ # assert tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
245
+ # assert tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
246
+ # ```
247
+ def eval_on_selector(selector, expression, arg: nil)
248
+ wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
249
+ end
250
+
251
+ # Returns the return value of `expression`.
252
+ #
253
+ # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
254
+ # matched elements as a first argument to `expression`. See [Working with selectors](./selectors.md) for more details.
255
+ #
256
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelectorAll`] would wait for the promise to
257
+ # resolve and return its value.
258
+ #
259
+ # Examples:
260
+ #
261
+ # ```html
262
+ # <div class="feed">
263
+ # <div class="tweet">Hello!</div>
264
+ # <div class="tweet">Hi!</div>
265
+ # </div>
266
+ # ```
267
+ #
268
+ #
269
+ # ```js
270
+ # const feedHandle = await page.$('.feed');
271
+ # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
272
+ # ```
273
+ #
274
+ # ```python async
275
+ # feed_handle = await page.query_selector(".feed")
276
+ # assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
277
+ # ```
278
+ #
279
+ # ```python sync
280
+ # feed_handle = page.query_selector(".feed")
281
+ # assert feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
282
+ # ```
283
+ def eval_on_selector_all(selector, expression, arg: nil)
284
+ wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
209
285
  end
210
286
 
211
287
  # This method waits for [actionability](./actionability.md) checks, focuses the element, fills it and triggers an `input`
212
- # event after filling. If the element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws
213
- # an error. Note that you can pass an empty string to clear the input field.
288
+ # event after filling. If the element is inside the `<label>` element that has associated
289
+ # [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), that control will be filled
290
+ # instead. If the element to be filled is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method
291
+ # throws an error. Note that you can pass an empty string to clear the input field.
214
292
  def fill(value, noWaitAfter: nil, timeout: nil)
215
- raise NotImplementedError.new('fill is not implemented yet.')
293
+ wrap_impl(@impl.fill(unwrap_impl(value), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
216
294
  end
217
295
 
218
296
  # Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
219
297
  def focus
220
- raise NotImplementedError.new('focus is not implemented yet.')
298
+ wrap_impl(@impl.focus)
221
299
  end
222
300
 
223
301
  # Returns element attribute value.
224
302
  def get_attribute(name)
225
- raise NotImplementedError.new('get_attribute is not implemented yet.')
303
+ wrap_impl(@impl.get_attribute(unwrap_impl(name)))
226
304
  end
305
+ alias_method :[], :get_attribute
227
306
 
228
307
  # This method hovers over the element by performing the following steps:
229
308
  # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
@@ -236,52 +315,52 @@ module Playwright
236
315
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
237
316
  # Passing zero timeout disables this.
238
317
  def hover(force: nil, modifiers: nil, position: nil, timeout: nil)
239
- raise NotImplementedError.new('hover is not implemented yet.')
318
+ wrap_impl(@impl.hover(force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
240
319
  end
241
320
 
242
321
  # Returns the `element.innerHTML`.
243
322
  def inner_html
244
- raise NotImplementedError.new('inner_html is not implemented yet.')
323
+ wrap_impl(@impl.inner_html)
245
324
  end
246
325
 
247
326
  # Returns the `element.innerText`.
248
327
  def inner_text
249
- raise NotImplementedError.new('inner_text is not implemented yet.')
328
+ wrap_impl(@impl.inner_text)
250
329
  end
251
330
 
252
331
  # Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
253
332
  def checked?
254
- raise NotImplementedError.new('checked? is not implemented yet.')
333
+ wrap_impl(@impl.checked?)
255
334
  end
256
335
 
257
336
  # Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).
258
337
  def disabled?
259
- raise NotImplementedError.new('disabled? is not implemented yet.')
338
+ wrap_impl(@impl.disabled?)
260
339
  end
261
340
 
262
341
  # Returns whether the element is [editable](./actionability.md#editable).
263
342
  def editable?
264
- raise NotImplementedError.new('editable? is not implemented yet.')
343
+ wrap_impl(@impl.editable?)
265
344
  end
266
345
 
267
346
  # Returns whether the element is [enabled](./actionability.md#enabled).
268
347
  def enabled?
269
- raise NotImplementedError.new('enabled? is not implemented yet.')
348
+ wrap_impl(@impl.enabled?)
270
349
  end
271
350
 
272
351
  # Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).
273
352
  def hidden?
274
- raise NotImplementedError.new('hidden? is not implemented yet.')
353
+ wrap_impl(@impl.hidden?)
275
354
  end
276
355
 
277
356
  # Returns whether the element is [visible](./actionability.md#visible).
278
357
  def visible?
279
- raise NotImplementedError.new('visible? is not implemented yet.')
358
+ wrap_impl(@impl.visible?)
280
359
  end
281
360
 
282
361
  # Returns the frame containing the given element.
283
362
  def owner_frame
284
- raise NotImplementedError.new('owner_frame is not implemented yet.')
363
+ wrap_impl(@impl.owner_frame)
285
364
  end
286
365
 
287
366
  # Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
@@ -303,7 +382,19 @@ module Playwright
303
382
  # Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When speficied with the
304
383
  # modifier, modifier is pressed and being held while the subsequent key is being pressed.
305
384
  def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
306
- raise NotImplementedError.new('press is not implemented yet.')
385
+ wrap_impl(@impl.press(unwrap_impl(key), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
386
+ end
387
+
388
+ # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
389
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns `null`.
390
+ def query_selector(selector)
391
+ wrap_impl(@impl.query_selector(unwrap_impl(selector)))
392
+ end
393
+
394
+ # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
395
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns empty array.
396
+ def query_selector_all(selector)
397
+ wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
307
398
  end
308
399
 
309
400
  # Returns the buffer with the captured screenshot.
@@ -316,17 +407,17 @@ module Playwright
316
407
  quality: nil,
317
408
  timeout: nil,
318
409
  type: nil)
319
- raise NotImplementedError.new('screenshot is not implemented yet.')
410
+ wrap_impl(@impl.screenshot(omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
320
411
  end
321
412
 
322
413
  # This method waits for [actionability](./actionability.md) checks, then tries to scroll element into view, unless it is
323
414
  # completely visible as defined by
324
- # [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s ```ratio```.
415
+ # [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.
325
416
  #
326
417
  # Throws when `elementHandle` does not point to an element
327
418
  # [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
328
419
  def scroll_into_view_if_needed(timeout: nil)
329
- raise NotImplementedError.new('scroll_into_view_if_needed is not implemented yet.')
420
+ wrap_impl(@impl.scroll_into_view_if_needed(timeout: unwrap_impl(timeout)))
330
421
  end
331
422
 
332
423
  # Returns the array of option values that have been successfully selected.
@@ -334,28 +425,56 @@ module Playwright
334
425
  # Triggers a `change` and `input` event once all the provided options have been selected. If element is not a `<select>`
335
426
  # element, the method throws an error.
336
427
  #
428
+ # Will wait until all specified options are present in the `<select>` element.
429
+ #
337
430
  #
338
431
  # ```js
339
432
  # // single selection matching the value
340
433
  # handle.selectOption('blue');
341
434
  #
342
- # // single selection matching both the value and the label
435
+ # // single selection matching the label
343
436
  # handle.selectOption({ label: 'Blue' });
344
437
  #
345
438
  # // multiple selection
346
- # handle.selectOption('red', 'green', 'blue');
439
+ # handle.selectOption(['red', 'green', 'blue']);
440
+ # ```
441
+ #
442
+ # ```python async
443
+ # # single selection matching the value
444
+ # await handle.select_option("blue")
445
+ # # single selection matching the label
446
+ # await handle.select_option(label="blue")
447
+ # # multiple selection
448
+ # await handle.select_option(value=["red", "green", "blue"])
449
+ # ```
450
+ #
451
+ # ```python sync
452
+ # # single selection matching the value
453
+ # handle.select_option("blue")
454
+ # # single selection matching both the label
455
+ # handle.select_option(label="blue")
456
+ # # multiple selection
457
+ # handle.select_option(value=["red", "green", "blue"])
458
+ # ```
347
459
  #
348
- # // multiple selection for blue, red and second option
349
- # handle.selectOption({ value: 'blue' }, { index: 2 }, 'red');
460
+ # ```python sync
461
+ # # single selection matching the value
462
+ # handle.select_option("blue")
463
+ # # single selection matching both the value and the label
464
+ # handle.select_option(label="blue")
465
+ # # multiple selection
466
+ # handle.select_option("red", "green", "blue")
467
+ # # multiple selection for blue, red and second option
468
+ # handle.select_option(value="blue", { index: 2 }, "red")
350
469
  # ```
351
470
  def select_option(values, noWaitAfter: nil, timeout: nil)
352
- raise NotImplementedError.new('select_option is not implemented yet.')
471
+ wrap_impl(@impl.select_option(unwrap_impl(values), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
353
472
  end
354
473
 
355
474
  # This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text
356
475
  # content.
357
476
  def select_text(timeout: nil)
358
- raise NotImplementedError.new('select_text is not implemented yet.')
477
+ wrap_impl(@impl.select_text(timeout: unwrap_impl(timeout)))
359
478
  end
360
479
 
361
480
  # This method expects `elementHandle` to point to an
@@ -364,7 +483,7 @@ module Playwright
364
483
  # Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
365
484
  # are resolved relative to the the current working directory. For empty array, clears the selected files.
366
485
  def set_input_files(files, noWaitAfter: nil, timeout: nil)
367
- raise NotImplementedError.new('set_input_files is not implemented yet.')
486
+ wrap_impl(@impl.set_input_files(unwrap_impl(files), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
368
487
  end
369
488
  alias_method :input_files=, :set_input_files
370
489
 
@@ -379,19 +498,19 @@ module Playwright
379
498
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
380
499
  # Passing zero timeout disables this.
381
500
  #
382
- # > **NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.
501
+ # > NOTE: `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.
383
502
  def tap_point(
384
503
  force: nil,
385
504
  modifiers: nil,
386
505
  noWaitAfter: nil,
387
506
  position: nil,
388
507
  timeout: nil)
389
- raise NotImplementedError.new('tap_point is not implemented yet.')
508
+ wrap_impl(@impl.tap_point(force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
390
509
  end
391
510
 
392
511
  # Returns the `node.textContent`.
393
512
  def text_content
394
- raise NotImplementedError.new('text_content is not implemented yet.')
513
+ wrap_impl(@impl.text_content)
395
514
  end
396
515
 
397
516
  # Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
@@ -404,6 +523,16 @@ module Playwright
404
523
  # await elementHandle.type('World', {delay: 100}); // Types slower, like a user
405
524
  # ```
406
525
  #
526
+ # ```python async
527
+ # await element_handle.type("hello") # types instantly
528
+ # await element_handle.type("world", delay=100) # types slower, like a user
529
+ # ```
530
+ #
531
+ # ```python sync
532
+ # element_handle.type("hello") # types instantly
533
+ # element_handle.type("world", delay=100) # types slower, like a user
534
+ # ```
535
+ #
407
536
  # An example of typing into a text field and then submitting the form:
408
537
  #
409
538
  #
@@ -412,8 +541,20 @@ module Playwright
412
541
  # await elementHandle.type('some text');
413
542
  # await elementHandle.press('Enter');
414
543
  # ```
415
- def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
416
- raise NotImplementedError.new('type_text is not implemented yet.')
544
+ #
545
+ # ```python async
546
+ # element_handle = await page.query_selector("input")
547
+ # await element_handle.type("some text")
548
+ # await element_handle.press("Enter")
549
+ # ```
550
+ #
551
+ # ```python sync
552
+ # element_handle = page.query_selector("input")
553
+ # element_handle.type("some text")
554
+ # element_handle.press("Enter")
555
+ # ```
556
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
557
+ wrap_impl(@impl.type(unwrap_impl(text), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
417
558
  end
418
559
 
419
560
  # This method checks the element by performing the following steps:
@@ -430,7 +571,7 @@ module Playwright
430
571
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
431
572
  # Passing zero timeout disables this.
432
573
  def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
433
- raise NotImplementedError.new('uncheck is not implemented yet.')
574
+ wrap_impl(@impl.uncheck(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
434
575
  end
435
576
 
436
577
  # Returns when the element satisfies the `state`.
@@ -448,7 +589,7 @@ module Playwright
448
589
  #
449
590
  # If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.
450
591
  def wait_for_element_state(state, timeout: nil)
451
- raise NotImplementedError.new('wait_for_element_state is not implemented yet.')
592
+ wrap_impl(@impl.wait_for_element_state(unwrap_impl(state), timeout: unwrap_impl(timeout)))
452
593
  end
453
594
 
454
595
  # Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or
@@ -467,27 +608,45 @@ module Playwright
467
608
  # const span = await div.waitForSelector('span', { state: 'attached' });
468
609
  # ```
469
610
  #
470
- # > **NOTE** This method does not work across navigations, use [`method: Page.waitForSelector`] instead.
611
+ # ```python async
612
+ # await page.set_content("<div><span></span></div>")
613
+ # div = await page.query_selector("div")
614
+ # # waiting for the "span" selector relative to the div.
615
+ # span = await div.wait_for_selector("span", state="attached")
616
+ # ```
617
+ #
618
+ # ```python sync
619
+ # page.set_content("<div><span></span></div>")
620
+ # div = page.query_selector("div")
621
+ # # waiting for the "span" selector relative to the div.
622
+ # span = div.wait_for_selector("span", state="attached")
623
+ # ```
624
+ #
625
+ # > NOTE: This method does not work across navigations, use [`method: Page.waitForSelector`] instead.
471
626
  def wait_for_selector(selector, state: nil, timeout: nil)
472
- raise NotImplementedError.new('wait_for_selector is not implemented yet.')
627
+ wrap_impl(@impl.wait_for_selector(unwrap_impl(selector), state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
473
628
  end
474
629
 
475
630
  # -- inherited from EventEmitter --
476
631
  # @nodoc
477
- def off(event, callback)
478
- wrap_channel_owner(@channel_owner.off(event, callback))
632
+ def once(event, callback)
633
+ event_emitter_proxy.once(event, callback)
479
634
  end
480
635
 
481
636
  # -- inherited from EventEmitter --
482
637
  # @nodoc
483
- def once(event, callback)
484
- wrap_channel_owner(@channel_owner.once(event, callback))
638
+ def on(event, callback)
639
+ event_emitter_proxy.on(event, callback)
485
640
  end
486
641
 
487
642
  # -- inherited from EventEmitter --
488
643
  # @nodoc
489
- def on(event, callback)
490
- wrap_channel_owner(@channel_owner.on(event, callback))
644
+ def off(event, callback)
645
+ event_emitter_proxy.off(event, callback)
646
+ end
647
+
648
+ private def event_emitter_proxy
649
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
491
650
  end
492
651
  end
493
652
  end