playwright-ruby-client 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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