playwright-ruby-client 0.0.3 → 0.0.8

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -12
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +8 -0
  5. data/lib/playwright/channel_owner.rb +16 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +163 -0
  8. data/lib/playwright/channel_owners/browser.rb +22 -29
  9. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  10. data/lib/playwright/channel_owners/console_message.rb +21 -0
  11. data/lib/playwright/channel_owners/element_handle.rb +314 -0
  12. data/lib/playwright/channel_owners/frame.rb +466 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +55 -0
  14. data/lib/playwright/channel_owners/page.rb +353 -5
  15. data/lib/playwright/channel_owners/request.rb +90 -0
  16. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  17. data/lib/playwright/connection.rb +15 -14
  18. data/lib/playwright/errors.rb +1 -1
  19. data/lib/playwright/event_emitter.rb +13 -0
  20. data/lib/playwright/input_files.rb +42 -0
  21. data/lib/playwright/input_type.rb +19 -0
  22. data/lib/playwright/input_types/android_input.rb +19 -0
  23. data/lib/playwright/input_types/keyboard.rb +32 -0
  24. data/lib/playwright/input_types/mouse.rb +4 -0
  25. data/lib/playwright/input_types/touchscreen.rb +4 -0
  26. data/lib/playwright/javascript.rb +13 -0
  27. data/lib/playwright/javascript/expression.rb +67 -0
  28. data/lib/playwright/javascript/function.rb +67 -0
  29. data/lib/playwright/javascript/value_parser.rb +75 -0
  30. data/lib/playwright/javascript/value_serializer.rb +54 -0
  31. data/lib/playwright/playwright_api.rb +45 -25
  32. data/lib/playwright/select_option_values.rb +32 -0
  33. data/lib/playwright/timeout_settings.rb +19 -0
  34. data/lib/playwright/url_matcher.rb +19 -0
  35. data/lib/playwright/utils.rb +37 -0
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +73 -0
  38. data/lib/playwright_api/accessibility.rb +60 -6
  39. data/lib/playwright_api/android.rb +33 -0
  40. data/lib/playwright_api/android_device.rb +78 -0
  41. data/lib/playwright_api/android_input.rb +25 -0
  42. data/lib/playwright_api/binding_call.rb +18 -0
  43. data/lib/playwright_api/browser.rb +136 -44
  44. data/lib/playwright_api/browser_context.rb +378 -51
  45. data/lib/playwright_api/browser_type.rb +137 -55
  46. data/lib/playwright_api/cdp_session.rb +32 -7
  47. data/lib/playwright_api/chromium_browser_context.rb +31 -0
  48. data/lib/playwright_api/console_message.rb +27 -7
  49. data/lib/playwright_api/dialog.rb +47 -3
  50. data/lib/playwright_api/download.rb +29 -5
  51. data/lib/playwright_api/element_handle.rb +429 -143
  52. data/lib/playwright_api/file_chooser.rb +13 -2
  53. data/lib/playwright_api/frame.rb +633 -179
  54. data/lib/playwright_api/js_handle.rb +97 -17
  55. data/lib/playwright_api/keyboard.rb +152 -24
  56. data/lib/playwright_api/mouse.rb +28 -3
  57. data/lib/playwright_api/page.rb +1183 -317
  58. data/lib/playwright_api/playwright.rb +174 -13
  59. data/lib/playwright_api/request.rb +115 -30
  60. data/lib/playwright_api/response.rb +22 -3
  61. data/lib/playwright_api/route.rb +63 -4
  62. data/lib/playwright_api/selectors.rb +29 -7
  63. data/lib/playwright_api/touchscreen.rb +2 -1
  64. data/lib/playwright_api/video.rb +11 -1
  65. data/lib/playwright_api/web_socket.rb +5 -5
  66. data/lib/playwright_api/worker.rb +29 -5
  67. data/playwright.gemspec +3 -0
  68. metadata +68 -2
@@ -1,7 +1,11 @@
1
1
  module Playwright
2
- # Download objects are dispatched by page via the page.on('download') event.
3
- # All the downloaded files belonging to the browser context are deleted when the browser context is closed. All downloaded files are deleted when the browser closes.
2
+ # `Download` objects are dispatched by page via the [`event: Page.download`] event.
3
+ #
4
+ # All the downloaded files belonging to the browser context are deleted when the browser context is closed. All downloaded
5
+ # files are deleted when the browser closes.
6
+ #
4
7
  # Download event is emitted once the download starts. Download path becomes available once download completes:
8
+ #
5
9
  #
6
10
  # ```js
7
11
  # const [ download ] = await Promise.all([
@@ -10,10 +14,27 @@ module Playwright
10
14
  # ]);
11
15
  # // wait for download to complete
12
16
  # const path = await download.path();
13
- # ...
14
17
  # ```
15
18
  #
16
- # **NOTE** Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the downloaded content. If `acceptDownloads` is not set or set to `false`, download events are emitted, but the actual 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.
17
38
  class Download < PlaywrightApi
18
39
 
19
40
  # Returns readable stream for current download or `null` if download failed.
@@ -41,7 +62,10 @@ module Playwright
41
62
  raise NotImplementedError.new('save_as is not implemented yet.')
42
63
  end
43
64
 
44
- # Returns suggested filename for this download. It is typically computed by the browser from the `Content-Disposition` response header or the `download` attribute. See the spec on whatwg. Different browsers can use different logic for computing it.
65
+ # Returns suggested filename for this download. It is typically computed by the browser from the
66
+ # [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) response header
67
+ # or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources). Different
68
+ # browsers can use different logic for computing it.
45
69
  def suggested_filename
46
70
  raise NotImplementedError.new('suggested_filename is not implemented yet.')
47
71
  end
@@ -1,7 +1,10 @@
1
1
  require_relative './js_handle.rb'
2
2
 
3
3
  module Playwright
4
- # ElementHandle represents an in-page DOM element. ElementHandles can be created with the `page.$(selector)` method.
4
+ # - extends: `JSHandle`
5
+ #
6
+ # ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.$`] method.
7
+ #
5
8
  #
6
9
  # ```js
7
10
  # const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
@@ -15,336 +18,587 @@ module Playwright
15
18
  # // ...
16
19
  # })();
17
20
  # ```
18
- # ElementHandle prevents DOM element from garbage collection unless the handle is disposed with `jsHandle.dispose()`. ElementHandles are auto-disposed when their origin frame gets navigated.
19
- # ElementHandle instances can be used as an argument in `page.$eval(selector, pageFunction[, arg])` and `page.evaluate(pageFunction[, arg])` methods.
21
+ #
22
+ # ```python async
23
+ # import asyncio
24
+ # from playwright.async_api import async_playwright
25
+ #
26
+ # async def run(playwright):
27
+ # chromium = playwright.chromium
28
+ # browser = await chromium.launch()
29
+ # page = await browser.new_page()
30
+ # await page.goto("https://example.com")
31
+ # href_element = await page.query_selector("a")
32
+ # await href_element.click()
33
+ # # ...
34
+ #
35
+ # async def main():
36
+ # async with async_playwright() as playwright:
37
+ # await run(playwright)
38
+ # asyncio.run(main())
39
+ # ```
40
+ #
41
+ # ```python sync
42
+ # from playwright.sync_api import sync_playwright
43
+ #
44
+ # def run(playwright):
45
+ # chromium = playwright.chromium
46
+ # browser = chromium.launch()
47
+ # page = browser.new_page()
48
+ # page.goto("https://example.com")
49
+ # href_element = page.query_selector("a")
50
+ # href_element.click()
51
+ # # ...
52
+ #
53
+ # with sync_playwright() as playwright:
54
+ # run(playwright)
55
+ # ```
56
+ #
57
+ # ElementHandle prevents DOM element from garbage collection unless the handle is disposed with
58
+ # [`method: JSHandle.dispose`]. ElementHandles are auto-disposed when their origin frame gets navigated.
59
+ #
60
+ # ElementHandle instances can be used as an argument in [`method: Page.$eval`] and [`method: Page.evaluate`] methods.
20
61
  class ElementHandle < JSHandle
21
62
 
22
- # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See Working with selectors for more details. If no elements match the selector, returns `null`.
23
- def S(selector)
24
- raise NotImplementedError.new('S is not implemented yet.')
63
+ # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
64
+ # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
65
+ # returns `null`.
66
+ def query_selector(selector)
67
+ wrap_impl(@impl.query_selector(unwrap_impl(selector)))
25
68
  end
26
69
 
27
- # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See Working with selectors for more details. If no elements match the selector, returns empty array.
28
- def SS(selector)
29
- raise NotImplementedError.new('SS is not implemented yet.')
70
+ # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
71
+ # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
72
+ # returns empty array.
73
+ def query_selector_all(selector)
74
+ wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
30
75
  end
31
76
 
32
77
  # Returns the return value of `pageFunction`
33
- # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first argument to `pageFunction`. See Working with selectors for more details. If no elements match the selector, the method throws an error.
34
- # If `pageFunction` returns a Promise, then `frame.$eval` would wait for the promise to resolve and return its value.
78
+ #
79
+ # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
80
+ # argument to `pageFunction`. See [Working with selectors](./selectors.md#working-with-selectors) for more details. If no
81
+ # elements match the selector, the method throws an error.
82
+ #
83
+ # If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
84
+ #
35
85
  # Examples:
86
+ #
36
87
  #
37
88
  # ```js
38
89
  # const tweetHandle = await page.$('.tweet');
39
90
  # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
40
91
  # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
41
92
  # ```
42
- def Seval(selector, pageFunction, arg: nil)
43
- raise NotImplementedError.new('Seval is not implemented yet.')
93
+ #
94
+ # ```python async
95
+ # tweet_handle = await page.query_selector(".tweet")
96
+ # assert await tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
97
+ # assert await tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
98
+ # ```
99
+ #
100
+ # ```python sync
101
+ # tweet_handle = page.query_selector(".tweet")
102
+ # assert tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
103
+ # assert tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
104
+ # ```
105
+ def eval_on_selector(selector, pageFunction, arg: nil)
106
+ wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(pageFunction), arg: unwrap_impl(arg)))
44
107
  end
45
108
 
46
109
  # Returns the return value of `pageFunction`
47
- # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of matched elements as a first argument to `pageFunction`. See Working with selectors for more details.
48
- # If `pageFunction` returns a Promise, then `frame.$$eval` would wait for the promise to resolve and return its value.
110
+ #
111
+ # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
112
+ # matched elements as a first argument to `pageFunction`. See
113
+ # [Working with selectors](./selectors.md#working-with-selectors) for more details.
114
+ #
115
+ # If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
116
+ #
49
117
  # Examples:
118
+ #
50
119
  # ```html
51
120
  # <div class="feed">
52
121
  # <div class="tweet">Hello!</div>
53
122
  # <div class="tweet">Hi!</div>
54
123
  # </div>
55
124
  # ```
125
+ #
56
126
  #
57
127
  # ```js
58
128
  # const feedHandle = await page.$('.feed');
59
129
  # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
60
130
  # ```
61
- def SSeval(selector, pageFunction, arg: nil)
62
- raise NotImplementedError.new('SSeval is not implemented yet.')
131
+ #
132
+ # ```python async
133
+ # # FIXME
134
+ # feed_handle = await page.query_selector(".feed")
135
+ # assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
136
+ # ```
137
+ #
138
+ # ```python sync
139
+ # feed_handle = page.query_selector(".feed")
140
+ # assert feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
141
+ # ```
142
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
143
+ wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(pageFunction), arg: unwrap_impl(arg)))
63
144
  end
64
145
 
65
- # This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is calculated relative to the main frame viewport - which is usually the same as the browser window.
66
- # Scrolling affects the returned bonding box, similarly to Element.getBoundingClientRect. That means `x` and/or `y` may be negative.
67
- # Elements from child frames return the bounding box relative to the main frame, unlike the Element.getBoundingClientRect.
68
- # Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the following snippet should click the center of the element.
146
+ # This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is
147
+ # calculated relative to the main frame viewport - which is usually the same as the browser window.
148
+ #
149
+ # Scrolling affects the returned bonding box, similarly to
150
+ # [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). That
151
+ # means `x` and/or `y` may be negative.
152
+ #
153
+ # Elements from child frames return the bounding box relative to the main frame, unlike the
154
+ # [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
155
+ #
156
+ # Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the following
157
+ # snippet should click the center of the element.
158
+ #
69
159
  #
70
160
  # ```js
71
161
  # const box = await elementHandle.boundingBox();
72
162
  # await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
73
163
  # ```
164
+ #
165
+ # ```python async
166
+ # box = await element_handle.bounding_box()
167
+ # await page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
168
+ # ```
169
+ #
170
+ # ```python sync
171
+ # box = element_handle.bounding_box()
172
+ # page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
173
+ # ```
74
174
  def bounding_box
75
- raise NotImplementedError.new('bounding_box is not implemented yet.')
175
+ wrap_impl(@impl.bounding_box)
76
176
  end
77
177
 
78
178
  # This method checks the element by performing the following steps:
79
- #
80
- # Ensure that element is a checkbox or a radio input. If not, this method rejects. If the element is already checked, this method returns immediately.
81
- # Wait for actionability checks on the element, unless `force` option is set.
82
- # Scroll the element into view if needed.
83
- # Use page.mouse to click in the center of the element.
84
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
85
- # Ensure that the element is now checked. If not, this method rejects.
179
+ # 1. Ensure that element is a checkbox or a radio input. If not, this method rejects. If the element is already
180
+ # checked, this method returns immediately.
181
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
182
+ # 1. Scroll the element into view if needed.
183
+ # 1. Use [`property: Page.mouse`] to click in the center of the element.
184
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
185
+ # 1. Ensure that the element is now checked. If not, this method rejects.
86
186
  #
87
187
  # If the element is detached from the DOM at any moment during the action, this method rejects.
88
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
188
+ #
189
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
190
+ # Passing zero timeout disables this.
89
191
  def check(force: nil, noWaitAfter: nil, timeout: nil)
90
- raise NotImplementedError.new('check is not implemented yet.')
192
+ wrap_impl(@impl.check(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
91
193
  end
92
194
 
93
195
  # This method clicks the element by performing the following steps:
94
- #
95
- # Wait for actionability checks on the element, unless `force` option is set.
96
- # Scroll the element into view if needed.
97
- # Use page.mouse to click in the center of the element, or the specified `position`.
98
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
196
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
197
+ # 1. Scroll the element into view if needed.
198
+ # 1. Use [`property: Page.mouse`] to click in the center of the element, or the specified `position`.
199
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
99
200
  #
100
201
  # If the element is detached from the DOM at any moment during the action, this method rejects.
101
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
202
+ #
203
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
204
+ # Passing zero timeout disables this.
102
205
  def click(
103
206
  button: nil,
104
207
  clickCount: nil,
105
208
  delay: nil,
106
- position: nil,
107
- modifiers: nil,
108
209
  force: nil,
210
+ modifiers: nil,
109
211
  noWaitAfter: nil,
212
+ position: nil,
110
213
  timeout: nil)
111
- raise NotImplementedError.new('click is not implemented yet.')
214
+ 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)))
112
215
  end
113
216
 
114
217
  # Returns the content frame for element handles referencing iframe nodes, or `null` otherwise
115
218
  def content_frame
116
- raise NotImplementedError.new('content_frame is not implemented yet.')
219
+ wrap_impl(@impl.content_frame)
117
220
  end
118
221
 
119
222
  # This method double clicks the element by performing the following steps:
120
- #
121
- # Wait for actionability checks on the element, unless `force` option is set.
122
- # Scroll the element into view if needed.
123
- # Use page.mouse to double click in the center of the element, or the specified `position`.
124
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. Note that if the first click of the `dblclick()` triggers a navigation event, this method will reject.
223
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
224
+ # 1. Scroll the element into view if needed.
225
+ # 1. Use [`property: Page.mouse`] to double click in the center of the element, or the specified `position`.
226
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. Note that if the
227
+ # first click of the `dblclick()` triggers a navigation event, this method will reject.
125
228
  #
126
229
  # If the element is detached from the DOM at any moment during the action, this method rejects.
127
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
128
230
  #
129
- # **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.
231
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
232
+ # Passing zero timeout disables this.
233
+ #
234
+ # > NOTE: `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.
130
235
  def dblclick(
131
236
  button: nil,
132
237
  delay: nil,
133
- position: nil,
134
- modifiers: nil,
135
238
  force: nil,
239
+ modifiers: nil,
136
240
  noWaitAfter: nil,
241
+ position: nil,
137
242
  timeout: nil)
138
- raise NotImplementedError.new('dblclick is not implemented yet.')
243
+ 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)))
139
244
  end
140
245
 
141
- # The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the elment, `click` is dispatched. This is equivalend to calling element.click().
246
+ # The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the elment, `click`
247
+ # is dispatched. This is equivalend to calling
248
+ # [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).
249
+ #
142
250
  #
143
251
  # ```js
144
252
  # await elementHandle.dispatchEvent('click');
145
253
  # ```
146
- # Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.
147
- # Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:
148
254
  #
149
- # DragEvent
150
- # FocusEvent
151
- # KeyboardEvent
152
- # MouseEvent
153
- # PointerEvent
154
- # TouchEvent
155
- # Event
255
+ # ```python async
256
+ # await element_handle.dispatch_event("click")
257
+ # ```
258
+ #
259
+ # ```python sync
260
+ # element_handle.dispatch_event("click")
261
+ # ```
262
+ #
263
+ # Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties
264
+ # and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.
265
+ #
266
+ # Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:
267
+ # - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
268
+ # - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
269
+ # - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
270
+ # - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
271
+ # - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
272
+ # - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
273
+ # - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
156
274
  #
157
275
  # You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
276
+ #
158
277
  #
159
278
  # ```js
160
279
  # // Note you can only create DataTransfer in Chromium and Firefox
161
280
  # const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
162
281
  # await elementHandle.dispatchEvent('dragstart', { dataTransfer });
163
282
  # ```
283
+ #
284
+ # ```python async
285
+ # # note you can only create data_transfer in chromium and firefox
286
+ # data_transfer = await page.evaluate_handle("new DataTransfer()")
287
+ # await element_handle.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
288
+ # ```
289
+ #
290
+ # ```python sync
291
+ # # note you can only create data_transfer in chromium and firefox
292
+ # data_transfer = page.evaluate_handle("new DataTransfer()")
293
+ # element_handle.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
294
+ # ```
164
295
  def dispatch_event(type, eventInit: nil)
165
- raise NotImplementedError.new('dispatch_event is not implemented yet.')
296
+ wrap_impl(@impl.dispatch_event(unwrap_impl(type), eventInit: unwrap_impl(eventInit)))
166
297
  end
167
298
 
168
- # This method waits for actionability checks, focuses the element, fills it and triggers an `input` event after filling. If the element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an error. Note that you can pass an empty string to clear the input field.
299
+ # This method waits for [actionability](./actionability.md) checks, focuses the element, fills it and triggers an `input`
300
+ # event after filling. If the element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws
301
+ # an error. Note that you can pass an empty string to clear the input field.
169
302
  def fill(value, noWaitAfter: nil, timeout: nil)
170
- raise NotImplementedError.new('fill is not implemented yet.')
303
+ wrap_impl(@impl.fill(unwrap_impl(value), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
171
304
  end
172
305
 
173
- # Calls focus on the element.
306
+ # Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
174
307
  def focus
175
- raise NotImplementedError.new('focus is not implemented yet.')
308
+ wrap_impl(@impl.focus)
176
309
  end
177
310
 
178
311
  # Returns element attribute value.
179
312
  def get_attribute(name)
180
- raise NotImplementedError.new('get_attribute is not implemented yet.')
313
+ wrap_impl(@impl.get_attribute(unwrap_impl(name)))
181
314
  end
315
+ alias_method :[], :get_attribute
182
316
 
183
317
  # This method hovers over the element by performing the following steps:
184
- #
185
- # Wait for actionability checks on the element, unless `force` option is set.
186
- # Scroll the element into view if needed.
187
- # Use page.mouse to hover over the center of the element, or the specified `position`.
188
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
318
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
319
+ # 1. Scroll the element into view if needed.
320
+ # 1. Use [`property: Page.mouse`] to hover over the center of the element, or the specified `position`.
321
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
189
322
  #
190
323
  # If the element is detached from the DOM at any moment during the action, this method rejects.
191
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
192
- def hover(position: nil, modifiers: nil, force: nil, timeout: nil)
193
- raise NotImplementedError.new('hover is not implemented yet.')
324
+ #
325
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
326
+ # Passing zero timeout disables this.
327
+ def hover(force: nil, modifiers: nil, position: nil, timeout: nil)
328
+ wrap_impl(@impl.hover(force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
194
329
  end
195
330
 
196
331
  # Returns the `element.innerHTML`.
197
332
  def inner_html
198
- raise NotImplementedError.new('inner_html is not implemented yet.')
333
+ wrap_impl(@impl.inner_html)
199
334
  end
200
335
 
201
336
  # Returns the `element.innerText`.
202
337
  def inner_text
203
- raise NotImplementedError.new('inner_text is not implemented yet.')
338
+ wrap_impl(@impl.inner_text)
339
+ end
340
+
341
+ # Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
342
+ def checked?
343
+ wrap_impl(@impl.checked?)
344
+ end
345
+
346
+ # Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).
347
+ def disabled?
348
+ wrap_impl(@impl.disabled?)
349
+ end
350
+
351
+ # Returns whether the element is [editable](./actionability.md#editable).
352
+ def editable?
353
+ wrap_impl(@impl.editable?)
354
+ end
355
+
356
+ # Returns whether the element is [enabled](./actionability.md#enabled).
357
+ def enabled?
358
+ wrap_impl(@impl.enabled?)
359
+ end
360
+
361
+ # Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).
362
+ def hidden?
363
+ wrap_impl(@impl.hidden?)
364
+ end
365
+
366
+ # Returns whether the element is [visible](./actionability.md#visible).
367
+ def visible?
368
+ wrap_impl(@impl.visible?)
204
369
  end
205
370
 
206
371
  # Returns the frame containing the given element.
207
372
  def owner_frame
208
- raise NotImplementedError.new('owner_frame is not implemented yet.')
373
+ wrap_impl(@impl.owner_frame)
209
374
  end
210
375
 
211
- # Focuses the element, and then uses `keyboard.down(key)` and `keyboard.up(key)`.
212
- # `key` can specify the intended keyboardEvent.key value or a single character to generate the text for. A superset of the `key` values can be found here. Examples of the keys are:
213
- # `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
214
- # Following modification shortcuts are also suported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
376
+ # Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
377
+ #
378
+ # `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
379
+ # value or a single character to generate the text for. A superset of the `key` values can be found
380
+ # [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:
381
+ #
382
+ # `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
383
+ # `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
384
+ #
385
+ # Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
386
+ #
215
387
  # Holding down `Shift` will type the text that corresponds to the `key` in the upper case.
216
- # If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective texts.
217
- # Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When speficied with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
388
+ #
389
+ # If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective
390
+ # texts.
391
+ #
392
+ # Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When speficied with the
393
+ # modifier, modifier is pressed and being held while the subsequent key is being pressed.
218
394
  def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
219
- raise NotImplementedError.new('press is not implemented yet.')
395
+ wrap_impl(@impl.press(unwrap_impl(key), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
220
396
  end
221
397
 
222
398
  # Returns the buffer with the captured screenshot.
223
- # This method waits for the actionability checks, then scrolls element into view before taking a screenshot. If the element is detached from DOM, the method throws an error.
399
+ #
400
+ # This method waits for the [actionability](./actionability.md) checks, then scrolls element into view before taking a
401
+ # screenshot. If the element is detached from DOM, the method throws an error.
224
402
  def screenshot(
403
+ omitBackground: nil,
225
404
  path: nil,
226
- type: nil,
227
405
  quality: nil,
228
- omitBackground: nil,
229
- timeout: nil)
230
- raise NotImplementedError.new('screenshot is not implemented yet.')
406
+ timeout: nil,
407
+ type: nil)
408
+ wrap_impl(@impl.screenshot(omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
231
409
  end
232
410
 
233
- # This method waits for actionability checks, then tries to scroll element into view, unless it is completely visible as defined by IntersectionObserver's `ratio`.
234
- # Throws when `elementHandle` does not point to an element connected to a Document or a ShadowRoot.
411
+ # This method waits for [actionability](./actionability.md) checks, then tries to scroll element into view, unless it is
412
+ # completely visible as defined by
413
+ # [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.
414
+ #
415
+ # Throws when `elementHandle` does not point to an element
416
+ # [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
235
417
  def scroll_into_view_if_needed(timeout: nil)
236
- raise NotImplementedError.new('scroll_into_view_if_needed is not implemented yet.')
418
+ wrap_impl(@impl.scroll_into_view_if_needed(timeout: unwrap_impl(timeout)))
237
419
  end
238
420
 
239
421
  # Returns the array of option values that have been successfully selected.
240
- # Triggers a `change` and `input` event once all the provided options have been selected. If element is not a `<select>` element, the method throws an error.
422
+ #
423
+ # Triggers a `change` and `input` event once all the provided options have been selected. If element is not a `<select>`
424
+ # element, the method throws an error.
425
+ #
426
+ # Will wait until all specified options are present in the `<select>` element.
427
+ #
241
428
  #
242
429
  # ```js
243
430
  # // single selection matching the value
244
431
  # handle.selectOption('blue');
245
432
  #
246
- # // single selection matching both the value and the label
433
+ # // single selection matching the label
247
434
  # handle.selectOption({ label: 'Blue' });
248
435
  #
249
436
  # // multiple selection
250
- # handle.selectOption('red', 'green', 'blue');
437
+ # handle.selectOption(['red', 'green', 'blue']);
438
+ # ```
439
+ #
440
+ # ```python async
441
+ # # single selection matching the value
442
+ # await handle.select_option("blue")
443
+ # # single selection matching the label
444
+ # await handle.select_option(label="blue")
445
+ # # multiple selection
446
+ # await handle.select_option(value=["red", "green", "blue"])
447
+ # ```
251
448
  #
252
- # // multiple selection for blue, red and second option
253
- # handle.selectOption({ value: 'blue' }, { index: 2 }, 'red');
449
+ # ```python sync
450
+ # # single selection matching the value
451
+ # handle.select_option("blue")
452
+ # # single selection matching both the label
453
+ # handle.select_option(label="blue")
454
+ # # multiple selection
455
+ # handle.select_option(value=["red", "green", "blue"])
456
+ # ```
457
+ #
458
+ # ```python sync
459
+ # # FIXME
460
+ # # single selection matching the value
461
+ # handle.select_option("blue")
462
+ # # single selection matching both the value and the label
463
+ # handle.select_option(label="blue")
464
+ # # multiple selection
465
+ # handle.select_option("red", "green", "blue")
466
+ # # multiple selection for blue, red and second option
467
+ # handle.select_option(value="blue", { index: 2 }, "red")
254
468
  # ```
255
469
  def select_option(values, noWaitAfter: nil, timeout: nil)
256
- raise NotImplementedError.new('select_option is not implemented yet.')
470
+ wrap_impl(@impl.select_option(unwrap_impl(values), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
257
471
  end
258
472
 
259
- # This method waits for actionability checks, then focuses the element and selects all its text content.
473
+ # This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text
474
+ # content.
260
475
  def select_text(timeout: nil)
261
- raise NotImplementedError.new('select_text is not implemented yet.')
476
+ wrap_impl(@impl.select_text(timeout: unwrap_impl(timeout)))
262
477
  end
263
478
 
264
- # This method expects `elementHandle` to point to an input element.
265
- # Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they are resolved relative to the the current working directory. For empty array, clears the selected files.
479
+ # This method expects `elementHandle` to point to an
480
+ # [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
481
+ #
482
+ # Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
483
+ # are resolved relative to the the current working directory. For empty array, clears the selected files.
266
484
  def set_input_files(files, noWaitAfter: nil, timeout: nil)
267
- raise NotImplementedError.new('set_input_files is not implemented yet.')
485
+ wrap_impl(@impl.set_input_files(unwrap_impl(files), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
268
486
  end
487
+ alias_method :input_files=, :set_input_files
269
488
 
270
489
  # This method taps the element by performing the following steps:
271
- #
272
- # Wait for actionability checks on the element, unless `force` option is set.
273
- # Scroll the element into view if needed.
274
- # Use page.touchscreen to tap in the center of the element, or the specified `position`.
275
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
490
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
491
+ # 1. Scroll the element into view if needed.
492
+ # 1. Use [`property: Page.touchscreen`] to tap the center of the element, or the specified `position`.
493
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
276
494
  #
277
495
  # If the element is detached from the DOM at any moment during the action, this method rejects.
278
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
279
496
  #
280
- # **NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.
497
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
498
+ # Passing zero timeout disables this.
499
+ #
500
+ # > NOTE: `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.
281
501
  def tap_point(
282
- position: nil,
283
- modifiers: nil,
284
502
  force: nil,
503
+ modifiers: nil,
285
504
  noWaitAfter: nil,
505
+ position: nil,
286
506
  timeout: nil)
287
- raise NotImplementedError.new('tap_point is not implemented yet.')
507
+ 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)))
288
508
  end
289
509
 
290
510
  # Returns the `node.textContent`.
291
511
  def text_content
292
- raise NotImplementedError.new('text_content is not implemented yet.')
293
- end
294
-
295
- def to_string
296
- raise NotImplementedError.new('to_string is not implemented yet.')
512
+ wrap_impl(@impl.text_content)
297
513
  end
298
514
 
299
515
  # Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
300
- # To press a special key, like `Control` or `ArrowDown`, use `elementHandle.press(key[, options])`.
516
+ #
517
+ # To press a special key, like `Control` or `ArrowDown`, use [`method: ElementHandle.press`].
518
+ #
301
519
  #
302
520
  # ```js
303
521
  # await elementHandle.type('Hello'); // Types instantly
304
522
  # await elementHandle.type('World', {delay: 100}); // Types slower, like a user
305
523
  # ```
524
+ #
525
+ # ```python async
526
+ # await element_handle.type("hello") # types instantly
527
+ # await element_handle.type("world", delay=100) # types slower, like a user
528
+ # ```
529
+ #
530
+ # ```python sync
531
+ # element_handle.type("hello") # types instantly
532
+ # element_handle.type("world", delay=100) # types slower, like a user
533
+ # ```
534
+ #
306
535
  # An example of typing into a text field and then submitting the form:
536
+ #
307
537
  #
308
538
  # ```js
309
539
  # const elementHandle = await page.$('input');
310
540
  # await elementHandle.type('some text');
311
541
  # await elementHandle.press('Enter');
312
542
  # ```
313
- def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
314
- raise NotImplementedError.new('type_text is not implemented yet.')
543
+ #
544
+ # ```python async
545
+ # element_handle = await page.query_selector("input")
546
+ # await element_handle.type("some text")
547
+ # await element_handle.press("Enter")
548
+ # ```
549
+ #
550
+ # ```python sync
551
+ # element_handle = page.query_selector("input")
552
+ # element_handle.type("some text")
553
+ # element_handle.press("Enter")
554
+ # ```
555
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
556
+ wrap_impl(@impl.type(unwrap_impl(text), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
315
557
  end
316
558
 
317
559
  # This method checks the element by performing the following steps:
318
- #
319
- # Ensure that element is a checkbox or a radio input. If not, this method rejects. If the element is already unchecked, this method returns immediately.
320
- # Wait for actionability checks on the element, unless `force` option is set.
321
- # Scroll the element into view if needed.
322
- # Use page.mouse to click in the center of the element.
323
- # Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
324
- # Ensure that the element is now unchecked. If not, this method rejects.
560
+ # 1. Ensure that element is a checkbox or a radio input. If not, this method rejects. If the element is already
561
+ # unchecked, this method returns immediately.
562
+ # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
563
+ # 1. Scroll the element into view if needed.
564
+ # 1. Use [`property: Page.mouse`] to click in the center of the element.
565
+ # 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
566
+ # 1. Ensure that the element is now unchecked. If not, this method rejects.
325
567
  #
326
568
  # If the element is detached from the DOM at any moment during the action, this method rejects.
327
- # When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
569
+ #
570
+ # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
571
+ # Passing zero timeout disables this.
328
572
  def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
329
- raise NotImplementedError.new('uncheck is not implemented yet.')
573
+ wrap_impl(@impl.uncheck(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
330
574
  end
331
575
 
332
- # Returns the element satisfies the `state`.
333
- # Depending on the `state` parameter, this method waits for one of the actionability checks to pass. This method throws when the element is detached while waiting, unless waiting for the `"hidden"` state.
576
+ # Returns when the element satisfies the `state`.
334
577
  #
335
- # `"visible"` Wait until the element is visible.
336
- # `"hidden"` Wait until the element is not visible or not attached. Note that waiting for hidden does not throw when the element detaches.
337
- # `"stable"` Wait until the element is both visible and stable.
338
- # `"enabled"` Wait until the element is enabled.
339
- # `"disabled"` Wait until the element is not enabled.
578
+ # Depending on the `state` parameter, this method waits for one of the [actionability](./actionability.md) checks to pass.
579
+ # This method throws when the element is detached while waiting, unless waiting for the `"hidden"` state.
580
+ # - `"visible"` Wait until the element is [visible](./actionability.md#visible).
581
+ # - `"hidden"` Wait until the element is [not visible](./actionability.md#visible) or
582
+ # [not attached](./actionability.md#attached). Note that waiting for hidden does not throw when the element detaches.
583
+ # - `"stable"` Wait until the element is both [visible](./actionability.md#visible) and
584
+ # [stable](./actionability.md#stable).
585
+ # - `"enabled"` Wait until the element is [enabled](./actionability.md#enabled).
586
+ # - `"disabled"` Wait until the element is [not enabled](./actionability.md#enabled).
587
+ # - `"editable"` Wait until the element is [editable](./actionability.md#editable).
340
588
  #
341
589
  # If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.
342
590
  def wait_for_element_state(state, timeout: nil)
343
- raise NotImplementedError.new('wait_for_element_state is not implemented yet.')
591
+ wrap_impl(@impl.wait_for_element_state(unwrap_impl(state), timeout: unwrap_impl(timeout)))
344
592
  end
345
593
 
346
- # Returns element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or `detached`.
347
- # Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
594
+ # Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or
595
+ # `detached`.
596
+ #
597
+ # Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, or
598
+ # become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the method
599
+ # will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will
600
+ # throw.
601
+ #
348
602
  #
349
603
  # ```js
350
604
  # await page.setContent(`<div><span></span></div>`);
@@ -353,9 +607,41 @@ module Playwright
353
607
  # const span = await div.waitForSelector('span', { state: 'attached' });
354
608
  # ```
355
609
  #
356
- # **NOTE** This method does not work across navigations, use `page.waitForSelector(selector[, options])` instead.
610
+ # ```python async
611
+ # await page.set_content("<div><span></span></div>")
612
+ # div = await page.query_selector("div")
613
+ # # waiting for the "span" selector relative to the div.
614
+ # span = await div.wait_for_selector("span", state="attached")
615
+ # ```
616
+ #
617
+ # ```python sync
618
+ # page.set_content("<div><span></span></div>")
619
+ # div = page.query_selector("div")
620
+ # # waiting for the "span" selector relative to the div.
621
+ # span = div.wait_for_selector("span", state="attached")
622
+ # ```
623
+ #
624
+ # > NOTE: This method does not work across navigations, use [`method: Page.waitForSelector`] instead.
357
625
  def wait_for_selector(selector, state: nil, timeout: nil)
358
- raise NotImplementedError.new('wait_for_selector is not implemented yet.')
626
+ wrap_impl(@impl.wait_for_selector(unwrap_impl(selector), state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
627
+ end
628
+
629
+ # -- inherited from EventEmitter --
630
+ # @nodoc
631
+ def on(event, callback)
632
+ wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
633
+ end
634
+
635
+ # -- inherited from EventEmitter --
636
+ # @nodoc
637
+ def off(event, callback)
638
+ wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
639
+ end
640
+
641
+ # -- inherited from EventEmitter --
642
+ # @nodoc
643
+ def once(event, callback)
644
+ wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
359
645
  end
360
646
  end
361
647
  end