playwright-ruby-client 0.0.8 → 0.3.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/docs/api_coverage.md +159 -101
  4. data/lib/playwright.rb +5 -2
  5. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +13 -6
  8. data/lib/playwright/channel_owner.rb +3 -5
  9. data/lib/playwright/channel_owners/android.rb +1 -1
  10. data/lib/playwright/channel_owners/android_device.rb +12 -12
  11. data/lib/playwright/channel_owners/binding_call.rb +3 -0
  12. data/lib/playwright/channel_owners/browser.rb +1 -1
  13. data/lib/playwright/channel_owners/browser_context.rb +157 -3
  14. data/lib/playwright/channel_owners/dialog.rb +28 -0
  15. data/lib/playwright/channel_owners/download.rb +27 -0
  16. data/lib/playwright/channel_owners/element_handle.rb +15 -4
  17. data/lib/playwright/channel_owners/frame.rb +24 -5
  18. data/lib/playwright/channel_owners/js_handle.rb +1 -1
  19. data/lib/playwright/channel_owners/page.rb +367 -29
  20. data/lib/playwright/channel_owners/playwright.rb +4 -0
  21. data/lib/playwright/channel_owners/request.rb +35 -3
  22. data/lib/playwright/channel_owners/response.rb +60 -0
  23. data/lib/playwright/channel_owners/route.rb +78 -0
  24. data/lib/playwright/channel_owners/selectors.rb +19 -1
  25. data/lib/playwright/errors.rb +1 -1
  26. data/lib/playwright/event_emitter.rb +8 -1
  27. data/lib/playwright/event_emitter_proxy.rb +49 -0
  28. data/lib/playwright/file_chooser_impl.rb +23 -0
  29. data/lib/playwright/http_headers.rb +20 -0
  30. data/lib/playwright/input_files.rb +1 -1
  31. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +5 -1
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +59 -20
  34. data/lib/playwright/route_handler_entry.rb +36 -0
  35. data/lib/playwright/select_option_values.rb +14 -4
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/utils.rb +4 -3
  38. data/lib/playwright/version.rb +1 -1
  39. data/lib/playwright/wait_helper.rb +1 -1
  40. data/lib/playwright_api/android.rb +82 -11
  41. data/lib/playwright_api/android_device.rb +148 -32
  42. data/lib/playwright_api/android_input.rb +17 -13
  43. data/lib/playwright_api/android_socket.rb +16 -0
  44. data/lib/playwright_api/android_web_view.rb +21 -0
  45. data/lib/playwright_api/binding_call.rb +14 -5
  46. data/lib/playwright_api/browser.rb +22 -23
  47. data/lib/playwright_api/browser_context.rb +49 -35
  48. data/lib/playwright_api/browser_type.rb +24 -64
  49. data/lib/playwright_api/chromium_browser_context.rb +10 -8
  50. data/lib/playwright_api/console_message.rb +10 -6
  51. data/lib/playwright_api/dialog.rb +37 -6
  52. data/lib/playwright_api/download.rb +28 -11
  53. data/lib/playwright_api/element_handle.rb +107 -96
  54. data/lib/playwright_api/file_chooser.rb +18 -10
  55. data/lib/playwright_api/frame.rb +124 -117
  56. data/lib/playwright_api/js_handle.rb +20 -22
  57. data/lib/playwright_api/keyboard.rb +5 -5
  58. data/lib/playwright_api/page.rb +206 -148
  59. data/lib/playwright_api/playwright.rb +33 -45
  60. data/lib/playwright_api/request.rb +11 -12
  61. data/lib/playwright_api/response.rb +30 -16
  62. data/lib/playwright_api/route.rb +27 -5
  63. data/lib/playwright_api/selectors.rb +14 -10
  64. data/lib/playwright_api/web_socket.rb +10 -1
  65. data/lib/playwright_api/worker.rb +13 -13
  66. metadata +16 -7
  67. data/lib/playwright/input_type.rb +0 -19
  68. data/lib/playwright/input_types/mouse.rb +0 -4
  69. data/lib/playwright/input_types/touchscreen.rb +0 -4
@@ -11,12 +11,10 @@ module Playwright
11
11
  # ```
12
12
  #
13
13
  # ```python async
14
- # # FIXME
15
14
  # background_page = await context.wait_for_event("backgroundpage")
16
15
  # ```
17
16
  #
18
17
  # ```python sync
19
- # # FIXME
20
18
  # background_page = context.wait_for_event("backgroundpage")
21
19
  # ```
22
20
  class ChromiumBrowserContext < BrowserContext
@@ -38,20 +36,24 @@ module Playwright
38
36
 
39
37
  # -- inherited from EventEmitter --
40
38
  # @nodoc
41
- def on(event, callback)
42
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
39
+ def off(event, callback)
40
+ event_emitter_proxy.off(event, callback)
43
41
  end
44
42
 
45
43
  # -- inherited from EventEmitter --
46
44
  # @nodoc
47
- def off(event, callback)
48
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
45
+ def once(event, callback)
46
+ event_emitter_proxy.once(event, callback)
49
47
  end
50
48
 
51
49
  # -- inherited from EventEmitter --
52
50
  # @nodoc
53
- def once(event, callback)
54
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
51
+ def on(event, callback)
52
+ event_emitter_proxy.on(event, callback)
53
+ end
54
+
55
+ private def event_emitter_proxy
56
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
55
57
  end
56
58
  end
57
59
  end
@@ -23,20 +23,24 @@ module Playwright
23
23
 
24
24
  # -- inherited from EventEmitter --
25
25
  # @nodoc
26
- def on(event, callback)
27
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
26
+ def off(event, callback)
27
+ event_emitter_proxy.off(event, callback)
28
28
  end
29
29
 
30
30
  # -- inherited from EventEmitter --
31
31
  # @nodoc
32
- def off(event, callback)
33
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
32
+ def once(event, callback)
33
+ event_emitter_proxy.once(event, callback)
34
34
  end
35
35
 
36
36
  # -- inherited from EventEmitter --
37
37
  # @nodoc
38
- def once(event, callback)
39
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
38
+ def on(event, callback)
39
+ event_emitter_proxy.on(event, callback)
40
+ end
41
+
42
+ private def event_emitter_proxy
43
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
40
44
  end
41
45
  end
42
46
  end
@@ -42,7 +42,6 @@ module Playwright
42
42
  # ```
43
43
  #
44
44
  # ```python sync
45
- # # FIXME
46
45
  # from playwright.sync_api import sync_playwright
47
46
  #
48
47
  # def handle_dialog(dialog):
@@ -60,31 +59,63 @@ module Playwright
60
59
  # with sync_playwright() as playwright:
61
60
  # run(playwright)
62
61
  # ```
62
+ #
63
+ # > NOTE: Dialogs are dismissed automatically, unless there is a [`event: Page.dialog`] listener. When listener is
64
+ # present, it **must** either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will
65
+ # [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
66
+ # actions like click will never finish.
63
67
  class Dialog < PlaywrightApi
64
68
 
65
69
  # Returns when the dialog has been accepted.
66
70
  def accept(promptText: nil)
67
- raise NotImplementedError.new('accept is not implemented yet.')
71
+ wrap_impl(@impl.accept(promptText: unwrap_impl(promptText)))
68
72
  end
69
73
 
70
74
  # If dialog is prompt, returns default prompt value. Otherwise, returns empty string.
71
75
  def default_value
72
- raise NotImplementedError.new('default_value is not implemented yet.')
76
+ wrap_impl(@impl.default_value)
73
77
  end
74
78
 
75
79
  # Returns when the dialog has been dismissed.
76
80
  def dismiss
77
- raise NotImplementedError.new('dismiss is not implemented yet.')
81
+ wrap_impl(@impl.dismiss)
78
82
  end
79
83
 
80
84
  # A message displayed in the dialog.
81
85
  def message
82
- raise NotImplementedError.new('message is not implemented yet.')
86
+ wrap_impl(@impl.message)
83
87
  end
84
88
 
85
89
  # Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.
86
90
  def type
87
- raise NotImplementedError.new('type is not implemented yet.')
91
+ wrap_impl(@impl.type)
92
+ end
93
+
94
+ # @nodoc
95
+ def accept_async(promptText: nil)
96
+ wrap_impl(@impl.accept_async(promptText: unwrap_impl(promptText)))
97
+ end
98
+
99
+ # -- inherited from EventEmitter --
100
+ # @nodoc
101
+ def off(event, callback)
102
+ event_emitter_proxy.off(event, callback)
103
+ end
104
+
105
+ # -- inherited from EventEmitter --
106
+ # @nodoc
107
+ def once(event, callback)
108
+ event_emitter_proxy.once(event, callback)
109
+ end
110
+
111
+ # -- inherited from EventEmitter --
112
+ # @nodoc
113
+ def on(event, callback)
114
+ event_emitter_proxy.on(event, callback)
115
+ end
116
+
117
+ private def event_emitter_proxy
118
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
88
119
  end
89
120
  end
90
121
  end
@@ -37,29 +37,24 @@ module Playwright
37
37
  # performed and user has no access to the downloaded files.
38
38
  class Download < PlaywrightApi
39
39
 
40
- # Returns readable stream for current download or `null` if download failed.
41
- def create_read_stream
42
- raise NotImplementedError.new('create_read_stream is not implemented yet.')
43
- end
44
-
45
40
  # Deletes the downloaded file.
46
41
  def delete
47
- raise NotImplementedError.new('delete is not implemented yet.')
42
+ wrap_impl(@impl.delete)
48
43
  end
49
44
 
50
45
  # Returns download error if any.
51
46
  def failure
52
- raise NotImplementedError.new('failure is not implemented yet.')
47
+ wrap_impl(@impl.failure)
53
48
  end
54
49
 
55
50
  # Returns path to the downloaded file in case of successful download.
56
51
  def path
57
- raise NotImplementedError.new('path is not implemented yet.')
52
+ wrap_impl(@impl.path)
58
53
  end
59
54
 
60
55
  # Saves the download to a user-specified path.
61
56
  def save_as(path)
62
- raise NotImplementedError.new('save_as is not implemented yet.')
57
+ wrap_impl(@impl.save_as(unwrap_impl(path)))
63
58
  end
64
59
 
65
60
  # Returns suggested filename for this download. It is typically computed by the browser from the
@@ -67,12 +62,34 @@ module Playwright
67
62
  # or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources). Different
68
63
  # browsers can use different logic for computing it.
69
64
  def suggested_filename
70
- raise NotImplementedError.new('suggested_filename is not implemented yet.')
65
+ wrap_impl(@impl.suggested_filename)
71
66
  end
72
67
 
73
68
  # Returns downloaded url.
74
69
  def url
75
- raise NotImplementedError.new('url is not implemented yet.')
70
+ wrap_impl(@impl.url)
71
+ end
72
+
73
+ # -- inherited from EventEmitter --
74
+ # @nodoc
75
+ def off(event, callback)
76
+ event_emitter_proxy.off(event, callback)
77
+ end
78
+
79
+ # -- inherited from EventEmitter --
80
+ # @nodoc
81
+ def once(event, callback)
82
+ event_emitter_proxy.once(event, callback)
83
+ end
84
+
85
+ # -- inherited from EventEmitter --
86
+ # @nodoc
87
+ def on(event, callback)
88
+ event_emitter_proxy.on(event, callback)
89
+ end
90
+
91
+ private def event_emitter_proxy
92
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
76
93
  end
77
94
  end
78
95
  end
@@ -3,7 +3,8 @@ require_relative './js_handle.rb'
3
3
  module Playwright
4
4
  # - extends: `JSHandle`
5
5
  #
6
- # ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.$`] method.
6
+ # ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.querySelector`]
7
+ # method.
7
8
  #
8
9
  #
9
10
  # ```js
@@ -57,92 +58,10 @@ module Playwright
57
58
  # ElementHandle prevents DOM element from garbage collection unless the handle is disposed with
58
59
  # [`method: JSHandle.dispose`]. ElementHandles are auto-disposed when their origin frame gets navigated.
59
60
  #
60
- # ElementHandle instances can be used as an argument in [`method: Page.$eval`] and [`method: Page.evaluate`] methods.
61
+ # ElementHandle instances can be used as an argument in [`method: Page.evalOnSelector`] and [`method: Page.evaluate`]
62
+ # methods.
61
63
  class ElementHandle < JSHandle
62
64
 
63
- # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
64
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
65
- # returns `null`.
66
- def query_selector(selector)
67
- wrap_impl(@impl.query_selector(unwrap_impl(selector)))
68
- end
69
-
70
- # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
71
- # [Working with selectors](./selectors.md#working-with-selectors) for more details. If no elements match the selector,
72
- # returns empty array.
73
- def query_selector_all(selector)
74
- wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
75
- end
76
-
77
- # Returns the return value of `pageFunction`
78
- #
79
- # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
80
- # argument to `pageFunction`. See [Working with selectors](./selectors.md#working-with-selectors) for more details. If no
81
- # elements match the selector, the method throws an error.
82
- #
83
- # If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
84
- #
85
- # Examples:
86
- #
87
- #
88
- # ```js
89
- # const tweetHandle = await page.$('.tweet');
90
- # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
91
- # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
92
- # ```
93
- #
94
- # ```python async
95
- # tweet_handle = await page.query_selector(".tweet")
96
- # assert await tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
97
- # assert await tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
98
- # ```
99
- #
100
- # ```python sync
101
- # tweet_handle = page.query_selector(".tweet")
102
- # assert tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
103
- # assert tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
104
- # ```
105
- def eval_on_selector(selector, pageFunction, arg: nil)
106
- wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(pageFunction), arg: unwrap_impl(arg)))
107
- end
108
-
109
- # Returns the return value of `pageFunction`
110
- #
111
- # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
112
- # matched elements as a first argument to `pageFunction`. See
113
- # [Working with selectors](./selectors.md#working-with-selectors) for more details.
114
- #
115
- # If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
116
- #
117
- # Examples:
118
- #
119
- # ```html
120
- # <div class="feed">
121
- # <div class="tweet">Hello!</div>
122
- # <div class="tweet">Hi!</div>
123
- # </div>
124
- # ```
125
- #
126
- #
127
- # ```js
128
- # const feedHandle = await page.$('.feed');
129
- # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
130
- # ```
131
- #
132
- # ```python async
133
- # # FIXME
134
- # feed_handle = await page.query_selector(".feed")
135
- # assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
136
- # ```
137
- #
138
- # ```python sync
139
- # feed_handle = page.query_selector(".feed")
140
- # assert feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
141
- # ```
142
- def eval_on_selector_all(selector, pageFunction, arg: nil)
143
- wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(pageFunction), arg: unwrap_impl(arg)))
144
- end
145
-
146
65
  # This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is
147
66
  # calculated relative to the main frame viewport - which is usually the same as the browser window.
148
67
  #
@@ -296,9 +215,80 @@ module Playwright
296
215
  wrap_impl(@impl.dispatch_event(unwrap_impl(type), eventInit: unwrap_impl(eventInit)))
297
216
  end
298
217
 
218
+ # Returns the return value of `expression`.
219
+ #
220
+ # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
221
+ # argument to `expression`. See [Working with selectors](./selectors.md) for more details. If no elements match the
222
+ # selector, the method throws an error.
223
+ #
224
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelector`] would wait for the promise to resolve
225
+ # and return its value.
226
+ #
227
+ # Examples:
228
+ #
229
+ #
230
+ # ```js
231
+ # const tweetHandle = await page.$('.tweet');
232
+ # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
233
+ # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
234
+ # ```
235
+ #
236
+ # ```python async
237
+ # tweet_handle = await page.query_selector(".tweet")
238
+ # assert await tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
239
+ # assert await tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
240
+ # ```
241
+ #
242
+ # ```python sync
243
+ # tweet_handle = page.query_selector(".tweet")
244
+ # assert tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
245
+ # assert tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
246
+ # ```
247
+ def eval_on_selector(selector, expression, arg: nil)
248
+ wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
249
+ end
250
+
251
+ # Returns the return value of `expression`.
252
+ #
253
+ # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
254
+ # matched elements as a first argument to `expression`. See [Working with selectors](./selectors.md) for more details.
255
+ #
256
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelectorAll`] would wait for the promise to
257
+ # resolve and return its value.
258
+ #
259
+ # Examples:
260
+ #
261
+ # ```html
262
+ # <div class="feed">
263
+ # <div class="tweet">Hello!</div>
264
+ # <div class="tweet">Hi!</div>
265
+ # </div>
266
+ # ```
267
+ #
268
+ #
269
+ # ```js
270
+ # const feedHandle = await page.$('.feed');
271
+ # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
272
+ # ```
273
+ #
274
+ # ```python async
275
+ # feed_handle = await page.query_selector(".feed")
276
+ # assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
277
+ # ```
278
+ #
279
+ # ```python sync
280
+ # feed_handle = page.query_selector(".feed")
281
+ # assert feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
282
+ # ```
283
+ def eval_on_selector_all(selector, expression, arg: nil)
284
+ wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
285
+ end
286
+
299
287
  # This method waits for [actionability](./actionability.md) checks, focuses the element, fills it and triggers an `input`
300
- # event after filling. If the element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws
301
- # an error. Note that you can pass an empty string to clear the input field.
288
+ # event after filling. If the element is inside the `<label>` element that has associated
289
+ # [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), that control will be filled
290
+ # instead. If the element to be filled is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method
291
+ # throws an error. Note that you can pass an empty string to clear the input field.
302
292
  def fill(value, noWaitAfter: nil, timeout: nil)
303
293
  wrap_impl(@impl.fill(unwrap_impl(value), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
304
294
  end
@@ -395,6 +385,18 @@ module Playwright
395
385
  wrap_impl(@impl.press(unwrap_impl(key), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
396
386
  end
397
387
 
388
+ # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
389
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns `null`.
390
+ def query_selector(selector)
391
+ wrap_impl(@impl.query_selector(unwrap_impl(selector)))
392
+ end
393
+
394
+ # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
395
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns empty array.
396
+ def query_selector_all(selector)
397
+ wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
398
+ end
399
+
398
400
  # Returns the buffer with the captured screenshot.
399
401
  #
400
402
  # This method waits for the [actionability](./actionability.md) checks, then scrolls element into view before taking a
@@ -456,7 +458,6 @@ module Playwright
456
458
  # ```
457
459
  #
458
460
  # ```python sync
459
- # # FIXME
460
461
  # # single selection matching the value
461
462
  # handle.select_option("blue")
462
463
  # # single selection matching both the value and the label
@@ -466,8 +467,14 @@ module Playwright
466
467
  # # multiple selection for blue, red and second option
467
468
  # handle.select_option(value="blue", { index: 2 }, "red")
468
469
  # ```
469
- def select_option(values, noWaitAfter: nil, timeout: nil)
470
- wrap_impl(@impl.select_option(unwrap_impl(values), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
470
+ def select_option(
471
+ element: nil,
472
+ index: nil,
473
+ value: nil,
474
+ label: nil,
475
+ noWaitAfter: nil,
476
+ timeout: nil)
477
+ wrap_impl(@impl.select_option(element: unwrap_impl(element), index: unwrap_impl(index), value: unwrap_impl(value), label: unwrap_impl(label), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
471
478
  end
472
479
 
473
480
  # This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text
@@ -628,20 +635,24 @@ module Playwright
628
635
 
629
636
  # -- inherited from EventEmitter --
630
637
  # @nodoc
631
- def on(event, callback)
632
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
638
+ def off(event, callback)
639
+ event_emitter_proxy.off(event, callback)
633
640
  end
634
641
 
635
642
  # -- inherited from EventEmitter --
636
643
  # @nodoc
637
- def off(event, callback)
638
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
644
+ def once(event, callback)
645
+ event_emitter_proxy.once(event, callback)
639
646
  end
640
647
 
641
648
  # -- inherited from EventEmitter --
642
649
  # @nodoc
643
- def once(event, callback)
644
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
650
+ def on(event, callback)
651
+ event_emitter_proxy.on(event, callback)
652
+ end
653
+
654
+ private def event_emitter_proxy
655
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
645
656
  end
646
657
  end
647
658
  end