playwright-ruby-client 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -2
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +7 -1
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +3 -2
  9. data/lib/playwright/channel_owners/android.rb +10 -1
  10. data/lib/playwright/channel_owners/android_device.rb +163 -0
  11. data/lib/playwright/channel_owners/browser.rb +13 -13
  12. data/lib/playwright/channel_owners/browser_context.rb +9 -1
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  15. data/lib/playwright/channel_owners/frame.rb +371 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +416 -19
  18. data/lib/playwright/channel_owners/request.rb +98 -0
  19. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  20. data/lib/playwright/connection.rb +9 -6
  21. data/lib/playwright/errors.rb +2 -2
  22. data/lib/playwright/event_emitter.rb +8 -1
  23. data/lib/playwright/event_emitter_proxy.rb +49 -0
  24. data/lib/playwright/file_chooser_impl.rb +23 -0
  25. data/lib/playwright/http_headers.rb +20 -0
  26. data/lib/playwright/input_files.rb +42 -0
  27. data/lib/playwright/javascript/expression.rb +37 -0
  28. data/lib/playwright/javascript/function.rb +37 -0
  29. data/lib/playwright/javascript/value_parser.rb +1 -1
  30. data/lib/playwright/javascript/value_serializer.rb +11 -11
  31. data/lib/playwright/keyboard_impl.rb +36 -0
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +84 -29
  34. data/lib/playwright/select_option_values.rb +32 -0
  35. data/lib/playwright/timeout_settings.rb +2 -2
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/url_matcher.rb +19 -0
  38. data/lib/playwright/utils.rb +18 -0
  39. data/lib/playwright/version.rb +1 -1
  40. data/lib/playwright/wait_helper.rb +1 -1
  41. data/lib/playwright_api/accessibility.rb +46 -6
  42. data/lib/playwright_api/android.rb +37 -0
  43. data/lib/playwright_api/android_device.rb +82 -0
  44. data/lib/playwright_api/android_input.rb +25 -0
  45. data/lib/playwright_api/binding_call.rb +10 -6
  46. data/lib/playwright_api/browser.rb +85 -18
  47. data/lib/playwright_api/browser_context.rb +269 -37
  48. data/lib/playwright_api/browser_type.rb +60 -11
  49. data/lib/playwright_api/cdp_session.rb +23 -1
  50. data/lib/playwright_api/chromium_browser_context.rb +18 -6
  51. data/lib/playwright_api/console_message.rb +14 -15
  52. data/lib/playwright_api/dialog.rb +48 -2
  53. data/lib/playwright_api/download.rb +47 -10
  54. data/lib/playwright_api/element_handle.rb +269 -110
  55. data/lib/playwright_api/file_chooser.rb +23 -7
  56. data/lib/playwright_api/frame.rb +439 -154
  57. data/lib/playwright_api/js_handle.rb +69 -24
  58. data/lib/playwright_api/keyboard.rb +99 -9
  59. data/lib/playwright_api/mouse.rb +22 -0
  60. data/lib/playwright_api/page.rb +856 -229
  61. data/lib/playwright_api/playwright.rb +108 -20
  62. data/lib/playwright_api/request.rb +77 -29
  63. data/lib/playwright_api/response.rb +10 -13
  64. data/lib/playwright_api/route.rb +49 -0
  65. data/lib/playwright_api/selectors.rb +20 -8
  66. data/lib/playwright_api/video.rb +8 -0
  67. data/lib/playwright_api/web_socket.rb +0 -8
  68. data/lib/playwright_api/worker.rb +25 -13
  69. data/playwright.gemspec +1 -0
  70. metadata +33 -2
@@ -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