playwright-ruby-client 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -2
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +7 -1
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +3 -2
  9. data/lib/playwright/channel_owners/android.rb +10 -1
  10. data/lib/playwright/channel_owners/android_device.rb +163 -0
  11. data/lib/playwright/channel_owners/browser.rb +13 -13
  12. data/lib/playwright/channel_owners/browser_context.rb +9 -1
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  15. data/lib/playwright/channel_owners/frame.rb +371 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +416 -19
  18. data/lib/playwright/channel_owners/request.rb +98 -0
  19. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  20. data/lib/playwright/connection.rb +9 -6
  21. data/lib/playwright/errors.rb +2 -2
  22. data/lib/playwright/event_emitter.rb +8 -1
  23. data/lib/playwright/event_emitter_proxy.rb +49 -0
  24. data/lib/playwright/file_chooser_impl.rb +23 -0
  25. data/lib/playwright/http_headers.rb +20 -0
  26. data/lib/playwright/input_files.rb +42 -0
  27. data/lib/playwright/javascript/expression.rb +37 -0
  28. data/lib/playwright/javascript/function.rb +37 -0
  29. data/lib/playwright/javascript/value_parser.rb +1 -1
  30. data/lib/playwright/javascript/value_serializer.rb +11 -11
  31. data/lib/playwright/keyboard_impl.rb +36 -0
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +84 -29
  34. data/lib/playwright/select_option_values.rb +32 -0
  35. data/lib/playwright/timeout_settings.rb +2 -2
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/url_matcher.rb +19 -0
  38. data/lib/playwright/utils.rb +18 -0
  39. data/lib/playwright/version.rb +1 -1
  40. data/lib/playwright/wait_helper.rb +1 -1
  41. data/lib/playwright_api/accessibility.rb +46 -6
  42. data/lib/playwright_api/android.rb +37 -0
  43. data/lib/playwright_api/android_device.rb +82 -0
  44. data/lib/playwright_api/android_input.rb +25 -0
  45. data/lib/playwright_api/binding_call.rb +10 -6
  46. data/lib/playwright_api/browser.rb +85 -18
  47. data/lib/playwright_api/browser_context.rb +269 -37
  48. data/lib/playwright_api/browser_type.rb +60 -11
  49. data/lib/playwright_api/cdp_session.rb +23 -1
  50. data/lib/playwright_api/chromium_browser_context.rb +18 -6
  51. data/lib/playwright_api/console_message.rb +14 -15
  52. data/lib/playwright_api/dialog.rb +48 -2
  53. data/lib/playwright_api/download.rb +47 -10
  54. data/lib/playwright_api/element_handle.rb +269 -110
  55. data/lib/playwright_api/file_chooser.rb +23 -7
  56. data/lib/playwright_api/frame.rb +439 -154
  57. data/lib/playwright_api/js_handle.rb +69 -24
  58. data/lib/playwright_api/keyboard.rb +99 -9
  59. data/lib/playwright_api/mouse.rb +22 -0
  60. data/lib/playwright_api/page.rb +856 -229
  61. data/lib/playwright_api/playwright.rb +108 -20
  62. data/lib/playwright_api/request.rb +77 -29
  63. data/lib/playwright_api/response.rb +10 -13
  64. data/lib/playwright_api/route.rb +49 -0
  65. data/lib/playwright_api/selectors.rb +20 -8
  66. data/lib/playwright_api/video.rb +8 -0
  67. data/lib/playwright_api/web_socket.rb +0 -8
  68. data/lib/playwright_api/worker.rb +25 -13
  69. data/playwright.gemspec +1 -0
  70. metadata +33 -2
@@ -3,31 +3,47 @@ module Playwright
3
3
  #
4
4
  #
5
5
  # ```js
6
- # page.on('filechooser', async (fileChooser) => {
7
- # await fileChooser.setFiles('/tmp/myfile.pdf');
8
- # });
6
+ # const [fileChooser] = await Promise.all([
7
+ # page.waitForEvent('filechooser'),
8
+ # page.click('upload')
9
+ # ]);
10
+ # await fileChooser.setFiles('myfile.pdf');
11
+ # ```
12
+ #
13
+ # ```python async
14
+ # async with page.expect_file_chooser() as fc_info:
15
+ # await page.click("upload")
16
+ # file_chooser = await fc_info.value
17
+ # await file_chooser.set_files("myfile.pdf")
18
+ # ```
19
+ #
20
+ # ```python sync
21
+ # with page.expect_file_chooser() as fc_info:
22
+ # page.click("upload")
23
+ # file_chooser = fc_info.value
24
+ # file_chooser.set_files("myfile.pdf")
9
25
  # ```
10
26
  class FileChooser < PlaywrightApi
11
27
 
12
28
  # Returns input element associated with this file chooser.
13
29
  def element
14
- raise NotImplementedError.new('element is not implemented yet.')
30
+ wrap_impl(@impl.element)
15
31
  end
16
32
 
17
33
  # Returns whether this file chooser accepts multiple files.
18
34
  def multiple?
19
- raise NotImplementedError.new('multiple? is not implemented yet.')
35
+ wrap_impl(@impl.multiple?)
20
36
  end
21
37
 
22
38
  # Returns page this file chooser belongs to.
23
39
  def page
24
- raise NotImplementedError.new('page is not implemented yet.')
40
+ wrap_impl(@impl.page)
25
41
  end
26
42
 
27
43
  # Sets the value of the file input this chooser is associated with. If some of the `filePaths` are relative paths, then
28
44
  # they are resolved relative to the the current working directory. For empty array, clears the selected files.
29
45
  def set_files(files, noWaitAfter: nil, timeout: nil)
30
- raise NotImplementedError.new('set_files is not implemented yet.')
46
+ wrap_impl(@impl.set_files(unwrap_impl(files), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
31
47
  end
32
48
  alias_method :files=, :set_files
33
49
  end
@@ -31,77 +31,55 @@ module Playwright
31
31
  # })();
32
32
  # ```
33
33
  #
34
- # An example of getting text from an iframe element:
34
+ # ```python async
35
+ # import asyncio
36
+ # from playwright.async_api import async_playwright
35
37
  #
36
- #
37
- # ```js
38
- # const frame = page.frames().find(frame => frame.name() === 'myframe');
39
- # const text = await frame.$eval('.selector', element => element.textContent);
40
- # console.log(text);
38
+ # async def run(playwright):
39
+ # firefox = playwright.firefox
40
+ # browser = await firefox.launch()
41
+ # page = await browser.new_page()
42
+ # await page.goto("https://www.theverge.com")
43
+ # dump_frame_tree(page.main_frame, "")
44
+ # await browser.close()
45
+ #
46
+ # def dump_frame_tree(frame, indent):
47
+ # print(indent + frame.name + '@' + frame.url)
48
+ # for child in frame.child_frames:
49
+ # dump_frame_tree(child, indent + " ")
50
+ #
51
+ # async def main():
52
+ # async with async_playwright() as playwright:
53
+ # await run(playwright)
54
+ # asyncio.run(main())
55
+ # ```
56
+ #
57
+ # ```python sync
58
+ # from playwright.sync_api import sync_playwright
59
+ #
60
+ # def run(playwright):
61
+ # firefox = playwright.firefox
62
+ # browser = firefox.launch()
63
+ # page = browser.new_page()
64
+ # page.goto("https://www.theverge.com")
65
+ # dump_frame_tree(page.main_frame, "")
66
+ # browser.close()
67
+ #
68
+ # def dump_frame_tree(frame, indent):
69
+ # print(indent + frame.name + '@' + frame.url)
70
+ # for child in frame.child_frames:
71
+ # dump_frame_tree(child, indent + " ")
72
+ #
73
+ # with sync_playwright() as playwright:
74
+ # run(playwright)
41
75
  # ```
42
76
  class Frame < PlaywrightApi
43
77
 
44
- # Returns the ElementHandle pointing to the frame element.
45
- #
46
- # The method finds an element matching the specified selector within the frame. See
47
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
48
- # returns `null`.
49
- def query_selector(selector)
50
- raise NotImplementedError.new('query_selector is not implemented yet.')
51
- end
52
-
53
- # Returns the ElementHandles pointing to the frame elements.
54
- #
55
- # The method finds all elements matching the specified selector within the frame. See
56
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
57
- # returns empty array.
58
- def query_selector_all(selector)
59
- raise NotImplementedError.new('query_selector_all is not implemented yet.')
60
- end
61
-
62
- # Returns the return value of `pageFunction`
63
- #
64
- # The method finds an element matching the specified selector within the frame and passes it as a first argument to
65
- # `pageFunction`. See [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements
66
- # match the selector, the method throws an error.
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
- #
73
- # ```js
74
- # const searchValue = await frame.$eval('#search', el => el.value);
75
- # const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
76
- # const html = await frame.$eval('.main-container', (e, suffix) => e.outerHTML + suffix, 'hello');
77
- # ```
78
- def eval_on_selector(selector, pageFunction, arg: nil)
79
- raise NotImplementedError.new('eval_on_selector is not implemented yet.')
80
- end
81
-
82
- # Returns the return value of `pageFunction`
83
- #
84
- # The method finds all elements matching the specified selector within the frame and passes an array of matched elements
85
- # as a first argument to `pageFunction`. See [Working with selectors](./selectors.md#working-with-selectors) for more
86
- # details.
87
- #
88
- # If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
89
- #
90
- # Examples:
91
- #
92
- #
93
- # ```js
94
- # const divsCounts = await frame.$$eval('div', (divs, min) => divs.length >= min, 10);
95
- # ```
96
- def eval_on_selector_all(selector, pageFunction, arg: nil)
97
- raise NotImplementedError.new('eval_on_selector_all is not implemented yet.')
98
- end
99
-
100
78
  # Returns the added tag when the script's onload fires or when the script content was injected into frame.
101
79
  #
102
80
  # Adds a `<script>` tag into the page with the desired url or content.
103
81
  def add_script_tag(content: nil, path: nil, type: nil, url: nil)
104
- raise NotImplementedError.new('add_script_tag is not implemented yet.')
82
+ wrap_impl(@impl.add_script_tag(content: unwrap_impl(content), path: unwrap_impl(path), type: unwrap_impl(type), url: unwrap_impl(url)))
105
83
  end
106
84
 
107
85
  # Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.
@@ -109,7 +87,7 @@ module Playwright
109
87
  # Adds a `<link rel="stylesheet">` tag into the page with the desired url or a `<style type="text/css">` tag with the
110
88
  # content.
111
89
  def add_style_tag(content: nil, path: nil, url: nil)
112
- raise NotImplementedError.new('add_style_tag is not implemented yet.')
90
+ wrap_impl(@impl.add_style_tag(content: unwrap_impl(content), path: unwrap_impl(path), url: unwrap_impl(url)))
113
91
  end
114
92
 
115
93
  # This method checks an element matching `selector` by performing the following steps:
@@ -126,11 +104,11 @@ module Playwright
126
104
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
127
105
  # Passing zero timeout disables this.
128
106
  def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
129
- raise NotImplementedError.new('check is not implemented yet.')
107
+ wrap_impl(@impl.check(unwrap_impl(selector), force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
130
108
  end
131
109
 
132
110
  def child_frames
133
- wrap_channel_owner(@channel_owner.child_frames)
111
+ wrap_impl(@impl.child_frames)
134
112
  end
135
113
 
136
114
  # This method clicks an element matching `selector` by performing the following steps:
@@ -153,12 +131,12 @@ module Playwright
153
131
  noWaitAfter: nil,
154
132
  position: nil,
155
133
  timeout: nil)
156
- raise NotImplementedError.new('click is not implemented yet.')
134
+ wrap_impl(@impl.click(unwrap_impl(selector), 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)))
157
135
  end
158
136
 
159
137
  # Gets the full HTML contents of the frame, including the doctype.
160
138
  def content
161
- wrap_channel_owner(@channel_owner.content)
139
+ wrap_impl(@impl.content)
162
140
  end
163
141
 
164
142
  # This method double clicks an element matching `selector` by performing the following steps:
@@ -173,7 +151,7 @@ module Playwright
173
151
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
174
152
  # Passing zero timeout disables this.
175
153
  #
176
- # > **NOTE** `frame.dblclick()` dispatches two `click` events and a single `dblclick` event.
154
+ # > NOTE: `frame.dblclick()` dispatches two `click` events and a single `dblclick` event.
177
155
  def dblclick(
178
156
  selector,
179
157
  button: nil,
@@ -183,7 +161,7 @@ module Playwright
183
161
  noWaitAfter: nil,
184
162
  position: nil,
185
163
  timeout: nil)
186
- raise NotImplementedError.new('dblclick is not implemented yet.')
164
+ wrap_impl(@impl.dblclick(unwrap_impl(selector), 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)))
187
165
  end
188
166
 
189
167
  # The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the elment, `click`
@@ -195,6 +173,14 @@ module Playwright
195
173
  # await frame.dispatchEvent('button#submit', 'click');
196
174
  # ```
197
175
  #
176
+ # ```python async
177
+ # await frame.dispatch_event("button#submit", "click")
178
+ # ```
179
+ #
180
+ # ```python sync
181
+ # frame.dispatch_event("button#submit", "click")
182
+ # ```
183
+ #
198
184
  # Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties
199
185
  # and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.
200
186
  #
@@ -215,18 +201,89 @@ module Playwright
215
201
  # const dataTransfer = await frame.evaluateHandle(() => new DataTransfer());
216
202
  # await frame.dispatchEvent('#source', 'dragstart', { dataTransfer });
217
203
  # ```
204
+ #
205
+ # ```python async
206
+ # # note you can only create data_transfer in chromium and firefox
207
+ # data_transfer = await frame.evaluate_handle("new DataTransfer()")
208
+ # await frame.dispatch_event("#source", "dragstart", { "dataTransfer": data_transfer })
209
+ # ```
210
+ #
211
+ # ```python sync
212
+ # # note you can only create data_transfer in chromium and firefox
213
+ # data_transfer = frame.evaluate_handle("new DataTransfer()")
214
+ # frame.dispatch_event("#source", "dragstart", { "dataTransfer": data_transfer })
215
+ # ```
218
216
  def dispatch_event(selector, type, eventInit: nil, timeout: nil)
219
- raise NotImplementedError.new('dispatch_event is not implemented yet.')
217
+ wrap_impl(@impl.dispatch_event(unwrap_impl(selector), unwrap_impl(type), eventInit: unwrap_impl(eventInit), timeout: unwrap_impl(timeout)))
218
+ end
219
+
220
+ # Returns the return value of `expression`.
221
+ #
222
+ # The method finds an element matching the specified selector within the frame and passes it as a first argument to
223
+ # `expression`. See [Working with selectors](./selectors.md) for more details. If no elements match the selector, the
224
+ # method throws an error.
225
+ #
226
+ # If `expression` returns a [Promise], then [`method: Frame.evalOnSelector`] would wait for the promise to resolve and
227
+ # return its value.
228
+ #
229
+ # Examples:
230
+ #
231
+ #
232
+ # ```js
233
+ # const searchValue = await frame.$eval('#search', el => el.value);
234
+ # const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
235
+ # const html = await frame.$eval('.main-container', (e, suffix) => e.outerHTML + suffix, 'hello');
236
+ # ```
237
+ #
238
+ # ```python async
239
+ # search_value = await frame.eval_on_selector("#search", "el => el.value")
240
+ # preload_href = await frame.eval_on_selector("link[rel=preload]", "el => el.href")
241
+ # html = await frame.eval_on_selector(".main-container", "(e, suffix) => e.outerHTML + suffix", "hello")
242
+ # ```
243
+ #
244
+ # ```python sync
245
+ # search_value = frame.eval_on_selector("#search", "el => el.value")
246
+ # preload_href = frame.eval_on_selector("link[rel=preload]", "el => el.href")
247
+ # html = frame.eval_on_selector(".main-container", "(e, suffix) => e.outerHTML + suffix", "hello")
248
+ # ```
249
+ def eval_on_selector(selector, expression, arg: nil)
250
+ wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
220
251
  end
221
252
 
222
- # Returns the return value of `pageFunction`
253
+ # Returns the return value of `expression`.
223
254
  #
224
- # If the function passed to the `frame.evaluate` returns a [Promise], then `frame.evaluate` would wait for the promise to
225
- # resolve and return its value.
255
+ # The method finds all elements matching the specified selector within the frame and passes an array of matched elements
256
+ # as a first argument to `expression`. See [Working with selectors](./selectors.md) for more details.
226
257
  #
227
- # If the function passed to the `frame.evaluate` returns a non-[Serializable] value, then `frame.evaluate` returns
228
- # `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`:
229
- # `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
258
+ # If `expression` returns a [Promise], then [`method: Frame.evalOnSelectorAll`] would wait for the promise to resolve and
259
+ # return its value.
260
+ #
261
+ # Examples:
262
+ #
263
+ #
264
+ # ```js
265
+ # const divsCounts = await frame.$$eval('div', (divs, min) => divs.length >= min, 10);
266
+ # ```
267
+ #
268
+ # ```python async
269
+ # divs_counts = await frame.eval_on_selector_all("div", "(divs, min) => divs.length >= min", 10)
270
+ # ```
271
+ #
272
+ # ```python sync
273
+ # divs_counts = frame.eval_on_selector_all("div", "(divs, min) => divs.length >= min", 10)
274
+ # ```
275
+ def eval_on_selector_all(selector, expression, arg: nil)
276
+ wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
277
+ end
278
+
279
+ # Returns the return value of `expression`.
280
+ #
281
+ # If the function passed to the [`method: Frame.evaluate`] returns a [Promise], then [`method: Frame.evaluate`] would wait
282
+ # for the promise to resolve and return its value.
283
+ #
284
+ # If the function passed to the [`method: Frame.evaluate`] returns a non-[Serializable] value, then
285
+ # [`method: Frame.evaluate`] returns `undefined`. Playwright also supports transferring some additional values that are
286
+ # not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
230
287
  #
231
288
  #
232
289
  # ```js
@@ -236,6 +293,16 @@ module Playwright
236
293
  # console.log(result); // prints "56"
237
294
  # ```
238
295
  #
296
+ # ```python async
297
+ # result = await frame.evaluate("([x, y]) => Promise.resolve(x * y)", [7, 8])
298
+ # print(result) # prints "56"
299
+ # ```
300
+ #
301
+ # ```python sync
302
+ # result = frame.evaluate("([x, y]) => Promise.resolve(x * y)", [7, 8])
303
+ # print(result) # prints "56"
304
+ # ```
305
+ #
239
306
  # A string can also be passed in instead of a function.
240
307
  #
241
308
  #
@@ -243,7 +310,19 @@ module Playwright
243
310
  # console.log(await frame.evaluate('1 + 2')); // prints "3"
244
311
  # ```
245
312
  #
246
- # `ElementHandle` instances can be passed as an argument to the `frame.evaluate`:
313
+ # ```python async
314
+ # print(await frame.evaluate("1 + 2")) # prints "3"
315
+ # x = 10
316
+ # print(await frame.evaluate(f"1 + {x}")) # prints "11"
317
+ # ```
318
+ #
319
+ # ```python sync
320
+ # print(frame.evaluate("1 + 2")) # prints "3"
321
+ # x = 10
322
+ # print(frame.evaluate(f"1 + {x}")) # prints "11"
323
+ # ```
324
+ #
325
+ # `ElementHandle` instances can be passed as an argument to the [`method: Frame.evaluate`]:
247
326
  #
248
327
  #
249
328
  # ```js
@@ -251,17 +330,29 @@ module Playwright
251
330
  # const html = await frame.evaluate(([body, suffix]) => body.innerHTML + suffix, [bodyHandle, 'hello']);
252
331
  # await bodyHandle.dispose();
253
332
  # ```
254
- def evaluate(pageFunction, arg: nil)
255
- wrap_channel_owner(@channel_owner.evaluate(pageFunction, arg: arg))
333
+ #
334
+ # ```python async
335
+ # body_handle = await frame.query_selector("body")
336
+ # html = await frame.evaluate("([body, suffix]) => body.innerHTML + suffix", [body_handle, "hello"])
337
+ # await body_handle.dispose()
338
+ # ```
339
+ #
340
+ # ```python sync
341
+ # body_handle = frame.query_selector("body")
342
+ # html = frame.evaluate("([body, suffix]) => body.innerHTML + suffix", [body_handle, "hello"])
343
+ # body_handle.dispose()
344
+ # ```
345
+ def evaluate(expression, arg: nil)
346
+ wrap_impl(@impl.evaluate(unwrap_impl(expression), arg: unwrap_impl(arg)))
256
347
  end
257
348
 
258
- # Returns the return value of `pageFunction` as in-page object (JSHandle).
349
+ # Returns the return value of `expression` as a `JSHandle`.
259
350
  #
260
- # The only difference between `frame.evaluate` and `frame.evaluateHandle` is that `frame.evaluateHandle` returns in-page
261
- # object (JSHandle).
351
+ # The only difference between [`method: Frame.evaluate`] and [`method: Frame.evaluateHandle`] is that
352
+ # [method: Frame.evaluateHandle`] returns `JSHandle`.
262
353
  #
263
- # If the function, passed to the `frame.evaluateHandle`, returns a [Promise], then `frame.evaluateHandle` would wait for
264
- # the promise to resolve and return its value.
354
+ # If the function, passed to the [`method: Frame.evaluateHandle`], returns a [Promise], then
355
+ # [`method: Frame.evaluateHandle`] would wait for the promise to resolve and return its value.
265
356
  #
266
357
  #
267
358
  # ```js
@@ -269,6 +360,16 @@ module Playwright
269
360
  # aWindowHandle; // Handle for the window object.
270
361
  # ```
271
362
  #
363
+ # ```python async
364
+ # a_window_handle = await frame.evaluate_handle("Promise.resolve(window)")
365
+ # a_window_handle # handle for the window object.
366
+ # ```
367
+ #
368
+ # ```python sync
369
+ # a_window_handle = frame.evaluate_handle("Promise.resolve(window)")
370
+ # a_window_handle # handle for the window object.
371
+ # ```
372
+ #
272
373
  # A string can also be passed in instead of a function.
273
374
  #
274
375
  #
@@ -276,7 +377,15 @@ module Playwright
276
377
  # const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'.
277
378
  # ```
278
379
  #
279
- # `JSHandle` instances can be passed as an argument to the `frame.evaluateHandle`:
380
+ # ```python async
381
+ # a_handle = await page.evaluate_handle("document") # handle for the "document"
382
+ # ```
383
+ #
384
+ # ```python sync
385
+ # a_handle = page.evaluate_handle("document") # handle for the "document"
386
+ # ```
387
+ #
388
+ # `JSHandle` instances can be passed as an argument to the [`method: Frame.evaluateHandle`]:
280
389
  #
281
390
  #
282
391
  # ```js
@@ -285,24 +394,39 @@ module Playwright
285
394
  # console.log(await resultHandle.jsonValue());
286
395
  # await resultHandle.dispose();
287
396
  # ```
288
- def evaluate_handle(pageFunction, arg: nil)
289
- wrap_channel_owner(@channel_owner.evaluate_handle(pageFunction, arg: arg))
397
+ #
398
+ # ```python async
399
+ # a_handle = await page.evaluate_handle("document.body")
400
+ # result_handle = await page.evaluate_handle("body => body.innerHTML", a_handle)
401
+ # print(await result_handle.json_value())
402
+ # await result_handle.dispose()
403
+ # ```
404
+ #
405
+ # ```python sync
406
+ # a_handle = page.evaluate_handle("document.body")
407
+ # result_handle = page.evaluate_handle("body => body.innerHTML", a_handle)
408
+ # print(result_handle.json_value())
409
+ # result_handle.dispose()
410
+ # ```
411
+ def evaluate_handle(expression, arg: nil)
412
+ wrap_impl(@impl.evaluate_handle(unwrap_impl(expression), arg: unwrap_impl(arg)))
290
413
  end
291
414
 
292
415
  # This method waits for an element matching `selector`, waits for [actionability](./actionability.md) checks, focuses the
293
- # element, fills it and triggers an `input` event after filling. If the element matching `selector` is not an `<input>`,
294
- # `<textarea>` or `[contenteditable]` element, this method throws an error. Note that you can pass an empty string to
295
- # clear the input field.
416
+ # element, fills it and triggers an `input` event after filling. If the element is inside the `<label>` element that has
417
+ # associated [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), that control will be
418
+ # filled instead. If the element to be filled is not an `<input>`, `<textarea>` or `[contenteditable]` element, this
419
+ # method throws an error. Note that you can pass an empty string to clear the input field.
296
420
  #
297
421
  # To send fine-grained keyboard events, use [`method: Frame.type`].
298
422
  def fill(selector, value, noWaitAfter: nil, timeout: nil)
299
- raise NotImplementedError.new('fill is not implemented yet.')
423
+ wrap_impl(@impl.fill(unwrap_impl(selector), unwrap_impl(value), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
300
424
  end
301
425
 
302
426
  # This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the method
303
427
  # waits until a matching element appears in the DOM.
304
428
  def focus(selector, timeout: nil)
305
- wrap_channel_owner(@channel_owner.focus(selector, timeout: timeout))
429
+ wrap_impl(@impl.focus(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
306
430
  end
307
431
 
308
432
  # Returns the `frame` or `iframe` element handle which corresponds to this frame.
@@ -318,13 +442,25 @@ module Playwright
318
442
  # const contentFrame = await frameElement.contentFrame();
319
443
  # console.log(frame === contentFrame); // -> true
320
444
  # ```
445
+ #
446
+ # ```python async
447
+ # frame_element = await frame.frame_element()
448
+ # content_frame = await frame_element.content_frame()
449
+ # assert frame == content_frame
450
+ # ```
451
+ #
452
+ # ```python sync
453
+ # frame_element = frame.frame_element()
454
+ # content_frame = frame_element.content_frame()
455
+ # assert frame == content_frame
456
+ # ```
321
457
  def frame_element
322
458
  raise NotImplementedError.new('frame_element is not implemented yet.')
323
459
  end
324
460
 
325
461
  # Returns element attribute value.
326
462
  def get_attribute(selector, name, timeout: nil)
327
- raise NotImplementedError.new('get_attribute is not implemented yet.')
463
+ wrap_impl(@impl.get_attribute(unwrap_impl(selector), unwrap_impl(name), timeout: unwrap_impl(timeout)))
328
464
  end
329
465
 
330
466
  # Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
@@ -341,12 +477,12 @@ module Playwright
341
477
  # "Not Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling
342
478
  # [`method: Response.status`].
343
479
  #
344
- # > **NOTE** `frame.goto` either throws an error or returns a main resource response. The only exceptions are navigation
345
- # to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
346
- # > **NOTE** Headless mode doesn't support navigation to a PDF document. See the
480
+ # > NOTE: `frame.goto` either throws an error or returns a main resource response. The only exceptions are navigation to
481
+ # `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
482
+ # > NOTE: Headless mode doesn't support navigation to a PDF document. See the
347
483
  # [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
348
484
  def goto(url, referer: nil, timeout: nil, waitUntil: nil)
349
- wrap_channel_owner(@channel_owner.goto(url, referer: referer, timeout: timeout, waitUntil: waitUntil))
485
+ wrap_impl(@impl.goto(unwrap_impl(url), referer: unwrap_impl(referer), timeout: unwrap_impl(timeout), waitUntil: unwrap_impl(waitUntil)))
350
486
  end
351
487
 
352
488
  # This method hovers over an element matching `selector` by performing the following steps:
@@ -365,72 +501,71 @@ module Playwright
365
501
  modifiers: nil,
366
502
  position: nil,
367
503
  timeout: nil)
368
- raise NotImplementedError.new('hover is not implemented yet.')
504
+ wrap_impl(@impl.hover(unwrap_impl(selector), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
369
505
  end
370
506
 
371
507
  # Returns `element.innerHTML`.
372
508
  def inner_html(selector, timeout: nil)
373
- raise NotImplementedError.new('inner_html is not implemented yet.')
509
+ wrap_impl(@impl.inner_html(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
374
510
  end
375
511
 
376
512
  # Returns `element.innerText`.
377
513
  def inner_text(selector, timeout: nil)
378
- raise NotImplementedError.new('inner_text is not implemented yet.')
514
+ wrap_impl(@impl.inner_text(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
379
515
  end
380
516
 
381
517
  # Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
382
518
  def checked?(selector, timeout: nil)
383
- raise NotImplementedError.new('checked? is not implemented yet.')
519
+ wrap_impl(@impl.checked?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
384
520
  end
385
521
 
386
522
  # Returns `true` if the frame has been detached, or `false` otherwise.
387
523
  def detached?
388
- raise NotImplementedError.new('detached? is not implemented yet.')
524
+ wrap_impl(@impl.detached?)
389
525
  end
390
526
 
391
527
  # Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).
392
528
  def disabled?(selector, timeout: nil)
393
- raise NotImplementedError.new('disabled? is not implemented yet.')
529
+ wrap_impl(@impl.disabled?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
394
530
  end
395
531
 
396
532
  # Returns whether the element is [editable](./actionability.md#editable).
397
533
  def editable?(selector, timeout: nil)
398
- raise NotImplementedError.new('editable? is not implemented yet.')
534
+ wrap_impl(@impl.editable?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
399
535
  end
400
536
 
401
537
  # Returns whether the element is [enabled](./actionability.md#enabled).
402
538
  def enabled?(selector, timeout: nil)
403
- raise NotImplementedError.new('enabled? is not implemented yet.')
539
+ wrap_impl(@impl.enabled?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
404
540
  end
405
541
 
406
542
  # Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).
407
543
  def hidden?(selector, timeout: nil)
408
- raise NotImplementedError.new('hidden? is not implemented yet.')
544
+ wrap_impl(@impl.hidden?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
409
545
  end
410
546
 
411
547
  # Returns whether the element is [visible](./actionability.md#visible).
412
548
  def visible?(selector, timeout: nil)
413
- raise NotImplementedError.new('visible? is not implemented yet.')
549
+ wrap_impl(@impl.visible?(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
414
550
  end
415
551
 
416
552
  # Returns frame's name attribute as specified in the tag.
417
553
  #
418
554
  # If the name is empty, returns the id attribute instead.
419
555
  #
420
- # > **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed
421
- # later.
556
+ # > NOTE: This value is calculated once when the frame is created, and will not update if the attribute is changed later.
422
557
  def name
423
- wrap_channel_owner(@channel_owner.name)
558
+ wrap_impl(@impl.name)
424
559
  end
425
560
 
426
561
  # Returns the page containing this frame.
427
562
  def page
428
- wrap_channel_owner(@channel_owner.page)
563
+ wrap_impl(@impl.page)
429
564
  end
430
565
 
431
566
  # Parent frame, if any. Detached frames and main frames return `null`.
432
567
  def parent_frame
433
- wrap_channel_owner(@channel_owner.parent_frame)
568
+ wrap_impl(@impl.parent_frame)
434
569
  end
435
570
 
436
571
  # `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
@@ -455,7 +590,23 @@ module Playwright
455
590
  delay: nil,
456
591
  noWaitAfter: nil,
457
592
  timeout: nil)
458
- wrap_channel_owner(@channel_owner.press(selector, key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout))
593
+ wrap_impl(@impl.press(unwrap_impl(selector), unwrap_impl(key), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
594
+ end
595
+
596
+ # Returns the ElementHandle pointing to the frame element.
597
+ #
598
+ # The method finds an element matching the specified selector within the frame. See
599
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns `null`.
600
+ def query_selector(selector)
601
+ wrap_impl(@impl.query_selector(unwrap_impl(selector)))
602
+ end
603
+
604
+ # Returns the ElementHandles pointing to the frame elements.
605
+ #
606
+ # The method finds all elements matching the specified selector within the frame. See
607
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns empty array.
608
+ def query_selector_all(selector)
609
+ wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
459
610
  end
460
611
 
461
612
  # Returns the array of option values that have been successfully selected.
@@ -463,6 +614,8 @@ module Playwright
463
614
  # Triggers a `change` and `input` event once all the provided options have been selected. If there's no `<select>` element
464
615
  # matching `selector`, the method throws an error.
465
616
  #
617
+ # Will wait until all specified options are present in the `<select>` element.
618
+ #
466
619
  #
467
620
  # ```js
468
621
  # // single selection matching the value
@@ -474,12 +627,30 @@ module Playwright
474
627
  # // multiple selection
475
628
  # frame.selectOption('select#colors', 'red', 'green', 'blue');
476
629
  # ```
630
+ #
631
+ # ```python async
632
+ # # single selection matching the value
633
+ # await frame.select_option("select#colors", "blue")
634
+ # # single selection matching the label
635
+ # await frame.select_option("select#colors", label="blue")
636
+ # # multiple selection
637
+ # await frame.select_option("select#colors", value=["red", "green", "blue"])
638
+ # ```
639
+ #
640
+ # ```python sync
641
+ # # single selection matching the value
642
+ # frame.select_option("select#colors", "blue")
643
+ # # single selection matching both the label
644
+ # frame.select_option("select#colors", label="blue")
645
+ # # multiple selection
646
+ # frame.select_option("select#colors", value=["red", "green", "blue"])
647
+ # ```
477
648
  def select_option(selector, values, noWaitAfter: nil, timeout: nil)
478
- raise NotImplementedError.new('select_option is not implemented yet.')
649
+ wrap_impl(@impl.select_option(unwrap_impl(selector), unwrap_impl(values), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
479
650
  end
480
651
 
481
652
  def set_content(html, timeout: nil, waitUntil: nil)
482
- wrap_channel_owner(@channel_owner.set_content(html, timeout: timeout, waitUntil: waitUntil))
653
+ wrap_impl(@impl.set_content(unwrap_impl(html), timeout: unwrap_impl(timeout), waitUntil: unwrap_impl(waitUntil)))
483
654
  end
484
655
  alias_method :content=, :set_content
485
656
 
@@ -489,7 +660,7 @@ module Playwright
489
660
  # Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
490
661
  # are resolved relative to the the current working directory. For empty array, clears the selected files.
491
662
  def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
492
- raise NotImplementedError.new('set_input_files is not implemented yet.')
663
+ wrap_impl(@impl.set_input_files(unwrap_impl(selector), unwrap_impl(files), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
493
664
  end
494
665
 
495
666
  # This method taps an element matching `selector` by performing the following steps:
@@ -503,7 +674,7 @@ module Playwright
503
674
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
504
675
  # Passing zero timeout disables this.
505
676
  #
506
- # > **NOTE** `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.
677
+ # > NOTE: `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.
507
678
  def tap_point(
508
679
  selector,
509
680
  force: nil,
@@ -511,17 +682,17 @@ module Playwright
511
682
  noWaitAfter: nil,
512
683
  position: nil,
513
684
  timeout: nil)
514
- raise NotImplementedError.new('tap_point is not implemented yet.')
685
+ wrap_impl(@impl.tap_point(unwrap_impl(selector), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
515
686
  end
516
687
 
517
688
  # Returns `element.textContent`.
518
689
  def text_content(selector, timeout: nil)
519
- raise NotImplementedError.new('text_content is not implemented yet.')
690
+ wrap_impl(@impl.text_content(unwrap_impl(selector), timeout: unwrap_impl(timeout)))
520
691
  end
521
692
 
522
693
  # Returns the page title.
523
694
  def title
524
- wrap_channel_owner(@channel_owner.title)
695
+ wrap_impl(@impl.title)
525
696
  end
526
697
 
527
698
  # Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `frame.type` can be used to
@@ -534,13 +705,23 @@ module Playwright
534
705
  # await frame.type('#mytextarea', 'Hello'); // Types instantly
535
706
  # await frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
536
707
  # ```
537
- def type_text(
708
+ #
709
+ # ```python async
710
+ # await frame.type("#mytextarea", "hello") # types instantly
711
+ # await frame.type("#mytextarea", "world", delay=100) # types slower, like a user
712
+ # ```
713
+ #
714
+ # ```python sync
715
+ # frame.type("#mytextarea", "hello") # types instantly
716
+ # frame.type("#mytextarea", "world", delay=100) # types slower, like a user
717
+ # ```
718
+ def type(
538
719
  selector,
539
720
  text,
540
721
  delay: nil,
541
722
  noWaitAfter: nil,
542
723
  timeout: nil)
543
- wrap_channel_owner(@channel_owner.type_text(selector, text, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout))
724
+ wrap_impl(@impl.type(unwrap_impl(selector), unwrap_impl(text), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
544
725
  end
545
726
 
546
727
  # This method checks an element matching `selector` by performing the following steps:
@@ -557,17 +738,17 @@ module Playwright
557
738
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
558
739
  # Passing zero timeout disables this.
559
740
  def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
560
- raise NotImplementedError.new('uncheck is not implemented yet.')
741
+ wrap_impl(@impl.uncheck(unwrap_impl(selector), force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
561
742
  end
562
743
 
563
744
  # Returns frame's url.
564
745
  def url
565
- wrap_channel_owner(@channel_owner.url)
746
+ wrap_impl(@impl.url)
566
747
  end
567
748
 
568
- # Returns when the `pageFunction` returns a truthy value, returns that value.
749
+ # Returns when the `expression` returns a truthy value, returns that value.
569
750
  #
570
- # The `waitForFunction` can be used to observe viewport size change:
751
+ # The [`method: Frame.waitForFunction`] can be used to observe viewport size change:
571
752
  #
572
753
  #
573
754
  # ```js
@@ -583,6 +764,39 @@ module Playwright
583
764
  # })();
584
765
  # ```
585
766
  #
767
+ # ```python async
768
+ # import asyncio
769
+ # from playwright.async_api import async_playwright
770
+ #
771
+ # async def run(playwright):
772
+ # webkit = playwright.webkit
773
+ # browser = await webkit.launch()
774
+ # page = await browser.new_page()
775
+ # await page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
776
+ # await page.main_frame.wait_for_function("() => window.x > 0")
777
+ # await browser.close()
778
+ #
779
+ # async def main():
780
+ # async with async_playwright() as playwright:
781
+ # await run(playwright)
782
+ # asyncio.run(main())
783
+ # ```
784
+ #
785
+ # ```python sync
786
+ # from playwright.sync_api import sync_playwright
787
+ #
788
+ # def run(playwright):
789
+ # webkit = playwright.webkit
790
+ # browser = webkit.launch()
791
+ # page = browser.new_page()
792
+ # page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
793
+ # page.main_frame.wait_for_function("() => window.x > 0")
794
+ # browser.close()
795
+ #
796
+ # with sync_playwright() as playwright:
797
+ # run(playwright)
798
+ # ```
799
+ #
586
800
  # To pass an argument to the predicate of `frame.waitForFunction` function:
587
801
  #
588
802
  #
@@ -590,8 +804,18 @@ module Playwright
590
804
  # const selector = '.foo';
591
805
  # await frame.waitForFunction(selector => !!document.querySelector(selector), selector);
592
806
  # ```
593
- def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
594
- raise NotImplementedError.new('wait_for_function is not implemented yet.')
807
+ #
808
+ # ```python async
809
+ # selector = ".foo"
810
+ # await frame.wait_for_function("selector => !!document.querySelector(selector)", selector)
811
+ # ```
812
+ #
813
+ # ```python sync
814
+ # selector = ".foo"
815
+ # frame.wait_for_function("selector => !!document.querySelector(selector)", selector)
816
+ # ```
817
+ def wait_for_function(expression, arg: nil, polling: nil, timeout: nil)
818
+ wrap_impl(@impl.wait_for_function(unwrap_impl(expression), arg: unwrap_impl(arg), polling: unwrap_impl(polling), timeout: unwrap_impl(timeout)))
595
819
  end
596
820
 
597
821
  # Waits for the required load state to be reached.
@@ -604,13 +828,23 @@ module Playwright
604
828
  # await frame.click('button'); // Click triggers navigation.
605
829
  # await frame.waitForLoadState(); // Waits for 'load' state by default.
606
830
  # ```
831
+ #
832
+ # ```python async
833
+ # await frame.click("button") # click triggers navigation.
834
+ # await frame.wait_for_load_state() # the promise resolves after "load" event.
835
+ # ```
836
+ #
837
+ # ```python sync
838
+ # frame.click("button") # click triggers navigation.
839
+ # frame.wait_for_load_state() # the promise resolves after "load" event.
840
+ # ```
607
841
  def wait_for_load_state(state: nil, timeout: nil)
608
- raise NotImplementedError.new('wait_for_load_state is not implemented yet.')
842
+ wrap_impl(@impl.wait_for_load_state(state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
609
843
  end
610
844
 
611
- # Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
612
- # last redirect. In case of navigation to a different anchor or navigation due to History API usage, the navigation will
613
- # resolve with `null`.
845
+ # Waits for the frame navigation and returns the main resource response. In case of multiple redirects, the navigation
846
+ # will resolve with the response of the last redirect. In case of navigation to a different anchor or navigation due to
847
+ # History API usage, the navigation will resolve with `null`.
614
848
  #
615
849
  # This method waits for the frame to navigate to a new URL. It is useful for when you run code which will indirectly cause
616
850
  # the frame to navigate. Consider this example:
@@ -618,15 +852,27 @@ module Playwright
618
852
  #
619
853
  # ```js
620
854
  # const [response] = await Promise.all([
621
- # frame.waitForNavigation(), // Wait for the navigation to finish
622
- # frame.click('a.my-link'), // Clicking the link will indirectly cause a navigation
855
+ # frame.waitForNavigation(), // The promise resolves after navigation has finished
856
+ # frame.click('a.delayed-navigation'), // Clicking the link will indirectly cause a navigation
623
857
  # ]);
624
858
  # ```
625
859
  #
626
- # **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is
860
+ # ```python async
861
+ # async with frame.expect_navigation():
862
+ # await frame.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
863
+ # # Resolves after navigation has finished
864
+ # ```
865
+ #
866
+ # ```python sync
867
+ # with frame.expect_navigation():
868
+ # frame.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
869
+ # # Resolves after navigation has finished
870
+ # ```
871
+ #
872
+ # > NOTE: Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is
627
873
  # considered a navigation.
628
- def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil)
629
- raise NotImplementedError.new('wait_for_navigation is not implemented yet.')
874
+ def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
875
+ wrap_impl(@impl.expect_navigation(timeout: unwrap_impl(timeout), url: unwrap_impl(url), waitUntil: unwrap_impl(waitUntil), &wrap_block_call(block)))
630
876
  end
631
877
 
632
878
  # Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or
@@ -640,23 +886,58 @@ module Playwright
640
886
  #
641
887
  #
642
888
  # ```js
643
- # const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
889
+ # const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
644
890
  #
645
891
  # (async () => {
646
- # const browser = await webkit.launch();
892
+ # const browser = await chromium.launch();
647
893
  # const page = await browser.newPage();
648
- # let currentURL;
649
- # page.mainFrame()
650
- # .waitForSelector('img')
651
- # .then(() => console.log('First URL with image: ' + currentURL));
652
- # for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com']) {
894
+ # for (let currentURL of ['https://google.com', 'https://bbc.com']) {
653
895
  # await page.goto(currentURL);
896
+ # const element = await page.mainFrame().waitForSelector('img');
897
+ # console.log('Loaded image: ' + await element.getAttribute('src'));
654
898
  # }
655
899
  # await browser.close();
656
900
  # })();
657
901
  # ```
902
+ #
903
+ # ```python async
904
+ # import asyncio
905
+ # from playwright.async_api import async_playwright
906
+ #
907
+ # async def run(playwright):
908
+ # chromium = playwright.chromium
909
+ # browser = await chromium.launch()
910
+ # page = await browser.new_page()
911
+ # for current_url in ["https://google.com", "https://bbc.com"]:
912
+ # await page.goto(current_url, wait_until="domcontentloaded")
913
+ # element = await page.main_frame.wait_for_selector("img")
914
+ # print("Loaded image: " + str(await element.get_attribute("src")))
915
+ # await browser.close()
916
+ #
917
+ # async def main():
918
+ # async with async_playwright() as playwright:
919
+ # await run(playwright)
920
+ # asyncio.run(main())
921
+ # ```
922
+ #
923
+ # ```python sync
924
+ # from playwright.sync_api import sync_playwright
925
+ #
926
+ # def run(playwright):
927
+ # chromium = playwright.chromium
928
+ # browser = chromium.launch()
929
+ # page = browser.new_page()
930
+ # for current_url in ["https://google.com", "https://bbc.com"]:
931
+ # page.goto(current_url, wait_until="domcontentloaded")
932
+ # element = page.main_frame.wait_for_selector("img")
933
+ # print("Loaded image: " + str(element.get_attribute("src")))
934
+ # browser.close()
935
+ #
936
+ # with sync_playwright() as playwright:
937
+ # run(playwright)
938
+ # ```
658
939
  def wait_for_selector(selector, state: nil, timeout: nil)
659
- raise NotImplementedError.new('wait_for_selector is not implemented yet.')
940
+ wrap_impl(@impl.wait_for_selector(unwrap_impl(selector), state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
660
941
  end
661
942
 
662
943
  # Waits for the given `timeout` in milliseconds.
@@ -669,30 +950,34 @@ module Playwright
669
950
 
670
951
  # @nodoc
671
952
  def detached=(req)
672
- wrap_channel_owner(@channel_owner.detached=(req))
953
+ wrap_impl(@impl.detached=(unwrap_impl(req)))
673
954
  end
674
955
 
675
956
  # @nodoc
676
957
  def after_initialize
677
- wrap_channel_owner(@channel_owner.after_initialize)
958
+ wrap_impl(@impl.after_initialize)
678
959
  end
679
960
 
680
961
  # -- inherited from EventEmitter --
681
962
  # @nodoc
682
- def off(event, callback)
683
- wrap_channel_owner(@channel_owner.off(event, callback))
963
+ def once(event, callback)
964
+ event_emitter_proxy.once(event, callback)
684
965
  end
685
966
 
686
967
  # -- inherited from EventEmitter --
687
968
  # @nodoc
688
- def once(event, callback)
689
- wrap_channel_owner(@channel_owner.once(event, callback))
969
+ def on(event, callback)
970
+ event_emitter_proxy.on(event, callback)
690
971
  end
691
972
 
692
973
  # -- inherited from EventEmitter --
693
974
  # @nodoc
694
- def on(event, callback)
695
- wrap_channel_owner(@channel_owner.on(event, callback))
975
+ def off(event, callback)
976
+ event_emitter_proxy.off(event, callback)
977
+ end
978
+
979
+ private def event_emitter_proxy
980
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
696
981
  end
697
982
  end
698
983
  end