playwright-ruby-client 0.0.6 → 0.2.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -5
  3. data/docs/api_coverage.md +359 -0
  4. data/lib/playwright.rb +6 -2
  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 +6 -7
  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 +14 -14
  12. data/lib/playwright/channel_owners/browser_context.rb +10 -2
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +243 -1
  15. data/lib/playwright/channel_owners/frame.rb +269 -22
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +379 -34
  18. data/lib/playwright/channel_owners/request.rb +9 -1
  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 +15 -0
  28. data/lib/playwright/javascript/function.rb +15 -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/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +66 -19
  34. data/lib/playwright/select_option_values.rb +42 -0
  35. data/lib/playwright/timeout_settings.rb +1 -1
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/utils.rb +18 -0
  38. data/lib/playwright/version.rb +1 -1
  39. data/lib/playwright/wait_helper.rb +1 -1
  40. data/lib/playwright_api/android.rb +32 -0
  41. data/lib/playwright_api/android_device.rb +77 -0
  42. data/lib/playwright_api/android_input.rb +25 -0
  43. data/lib/playwright_api/binding_call.rb +10 -6
  44. data/lib/playwright_api/browser.rb +22 -22
  45. data/lib/playwright_api/browser_context.rb +31 -22
  46. data/lib/playwright_api/browser_type.rb +16 -56
  47. data/lib/playwright_api/chromium_browser_context.rb +10 -8
  48. data/lib/playwright_api/console_message.rb +9 -10
  49. data/lib/playwright_api/dialog.rb +7 -3
  50. data/lib/playwright_api/download.rb +28 -11
  51. data/lib/playwright_api/element_handle.rb +139 -127
  52. data/lib/playwright_api/file_chooser.rb +17 -9
  53. data/lib/playwright_api/frame.rb +148 -148
  54. data/lib/playwright_api/js_handle.rb +26 -22
  55. data/lib/playwright_api/keyboard.rb +6 -6
  56. data/lib/playwright_api/page.rb +215 -179
  57. data/lib/playwright_api/playwright.rb +34 -46
  58. data/lib/playwright_api/request.rb +7 -8
  59. data/lib/playwright_api/response.rb +10 -6
  60. data/lib/playwright_api/selectors.rb +13 -9
  61. data/lib/playwright_api/web_socket.rb +10 -1
  62. data/lib/playwright_api/worker.rb +13 -13
  63. data/playwright.gemspec +1 -0
  64. metadata +32 -6
  65. data/lib/playwright/input_type.rb +0 -19
  66. data/lib/playwright/input_types/mouse.rb +0 -4
  67. data/lib/playwright/input_types/touchscreen.rb +0 -4
@@ -49,11 +49,6 @@ module Playwright
49
49
  # ```
50
50
  class BrowserType < PlaywrightApi
51
51
 
52
- # This methods attaches Playwright to an existing browser instance.
53
- def connect(params)
54
- raise NotImplementedError.new('connect is not implemented yet.')
55
- end
56
-
57
52
  # A path where Playwright expects to find a bundled browser executable.
58
53
  def executable_path
59
54
  wrap_impl(@impl.executable_path)
@@ -109,12 +104,11 @@ module Playwright
109
104
  handleSIGTERM: nil,
110
105
  headless: nil,
111
106
  ignoreDefaultArgs: nil,
112
- logger: nil,
113
107
  proxy: nil,
114
108
  slowMo: nil,
115
109
  timeout: nil,
116
110
  &block)
117
- wrap_impl(@impl.launch(args: args, chromiumSandbox: chromiumSandbox, devtools: devtools, downloadsPath: downloadsPath, env: env, executablePath: executablePath, firefoxUserPrefs: firefoxUserPrefs, handleSIGHUP: handleSIGHUP, handleSIGINT: handleSIGINT, handleSIGTERM: handleSIGTERM, headless: headless, ignoreDefaultArgs: ignoreDefaultArgs, logger: logger, proxy: proxy, slowMo: slowMo, timeout: timeout, &wrap_block_call(block)))
111
+ wrap_impl(@impl.launch(args: unwrap_impl(args), chromiumSandbox: unwrap_impl(chromiumSandbox), devtools: unwrap_impl(devtools), downloadsPath: unwrap_impl(downloadsPath), env: unwrap_impl(env), executablePath: unwrap_impl(executablePath), firefoxUserPrefs: unwrap_impl(firefoxUserPrefs), handleSIGHUP: unwrap_impl(handleSIGHUP), handleSIGINT: unwrap_impl(handleSIGINT), handleSIGTERM: unwrap_impl(handleSIGTERM), headless: unwrap_impl(headless), ignoreDefaultArgs: unwrap_impl(ignoreDefaultArgs), proxy: unwrap_impl(proxy), slowMo: unwrap_impl(slowMo), timeout: unwrap_impl(timeout), &wrap_block_call(block)))
118
112
  end
119
113
 
120
114
  # Returns the persistent browser context instance.
@@ -146,60 +140,22 @@ module Playwright
146
140
  isMobile: nil,
147
141
  javaScriptEnabled: nil,
148
142
  locale: nil,
149
- logger: nil,
143
+ noViewport: nil,
150
144
  offline: nil,
151
145
  permissions: nil,
152
146
  proxy: nil,
153
- recordHar: nil,
154
- recordVideo: nil,
147
+ record_har_omit_content: nil,
148
+ record_har_path: nil,
149
+ record_video_dir: nil,
150
+ record_video_size: nil,
155
151
  slowMo: nil,
156
152
  timeout: nil,
157
153
  timezoneId: nil,
158
154
  userAgent: nil,
159
- videoSize: nil,
160
- videosPath: nil,
161
155
  viewport: nil)
162
156
  raise NotImplementedError.new('launch_persistent_context is not implemented yet.')
163
157
  end
164
158
 
165
- # Returns the browser app instance.
166
- #
167
- # Launches browser server that client can connect to. An example of launching a browser executable and connecting to it
168
- # later:
169
- #
170
- #
171
- # ```js
172
- # const { chromium } = require('playwright'); // Or 'webkit' or 'firefox'.
173
- #
174
- # (async () => {
175
- # const browserServer = await chromium.launchServer();
176
- # const wsEndpoint = browserServer.wsEndpoint();
177
- # // Use web socket endpoint later to establish a connection.
178
- # const browser = await chromium.connect({ wsEndpoint });
179
- # // Close browser instance.
180
- # await browserServer.close();
181
- # })();
182
- # ```
183
- def launch_server(
184
- args: nil,
185
- chromiumSandbox: nil,
186
- devtools: nil,
187
- downloadsPath: nil,
188
- env: nil,
189
- executablePath: nil,
190
- firefoxUserPrefs: nil,
191
- handleSIGHUP: nil,
192
- handleSIGINT: nil,
193
- handleSIGTERM: nil,
194
- headless: nil,
195
- ignoreDefaultArgs: nil,
196
- logger: nil,
197
- port: nil,
198
- proxy: nil,
199
- timeout: nil)
200
- raise NotImplementedError.new('launch_server is not implemented yet.')
201
- end
202
-
203
159
  # Returns browser name. For example: `'chromium'`, `'webkit'` or `'firefox'`.
204
160
  def name
205
161
  wrap_impl(@impl.name)
@@ -207,20 +163,24 @@ module Playwright
207
163
 
208
164
  # -- inherited from EventEmitter --
209
165
  # @nodoc
210
- def on(event, callback)
211
- wrap_impl(@impl.on(event, callback))
166
+ def once(event, callback)
167
+ event_emitter_proxy.once(event, callback)
212
168
  end
213
169
 
214
170
  # -- inherited from EventEmitter --
215
171
  # @nodoc
216
- def off(event, callback)
217
- wrap_impl(@impl.off(event, callback))
172
+ def on(event, callback)
173
+ event_emitter_proxy.on(event, callback)
218
174
  end
219
175
 
220
176
  # -- inherited from EventEmitter --
221
177
  # @nodoc
222
- def once(event, callback)
223
- wrap_impl(@impl.once(event, callback))
178
+ def off(event, callback)
179
+ event_emitter_proxy.off(event, callback)
180
+ end
181
+
182
+ private def event_emitter_proxy
183
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
224
184
  end
225
185
  end
226
186
  end
@@ -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(event, callback))
39
+ def once(event, callback)
40
+ event_emitter_proxy.once(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(event, callback))
45
+ def on(event, callback)
46
+ event_emitter_proxy.on(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(event, callback))
51
+ def off(event, callback)
52
+ event_emitter_proxy.off(event, callback)
53
+ end
54
+
55
+ private def event_emitter_proxy
56
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
55
57
  end
56
58
  end
57
59
  end
@@ -17,31 +17,30 @@ module Playwright
17
17
  # One of the following values: `'log'`, `'debug'`, `'info'`, `'error'`, `'warning'`, `'dir'`, `'dirxml'`, `'table'`,
18
18
  # `'trace'`, `'clear'`, `'startGroup'`, `'startGroupCollapsed'`, `'endGroup'`, `'assert'`, `'profile'`, `'profileEnd'`,
19
19
  # `'count'`, `'timeEnd'`.
20
- def type_text
21
- raise NotImplementedError.new('type_text is not implemented yet.')
20
+ def type
21
+ wrap_impl(@impl.type)
22
22
  end
23
23
 
24
+ # -- inherited from EventEmitter --
24
25
  # @nodoc
25
- def type
26
- wrap_impl(@impl.type)
26
+ def once(event, callback)
27
+ event_emitter_proxy.once(event, callback)
27
28
  end
28
29
 
29
30
  # -- inherited from EventEmitter --
30
31
  # @nodoc
31
32
  def on(event, callback)
32
- wrap_impl(@impl.on(event, callback))
33
+ event_emitter_proxy.on(event, callback)
33
34
  end
34
35
 
35
36
  # -- inherited from EventEmitter --
36
37
  # @nodoc
37
38
  def off(event, callback)
38
- wrap_impl(@impl.off(event, callback))
39
+ event_emitter_proxy.off(event, callback)
39
40
  end
40
41
 
41
- # -- inherited from EventEmitter --
42
- # @nodoc
43
- def once(event, callback)
44
- wrap_impl(@impl.once(event, callback))
42
+ private def event_emitter_proxy
43
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
45
44
  end
46
45
  end
47
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,6 +59,11 @@ 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.
@@ -83,8 +87,8 @@ module Playwright
83
87
  end
84
88
 
85
89
  # Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.
86
- def type_text
87
- raise NotImplementedError.new('type_text is not implemented yet.')
90
+ def type
91
+ raise NotImplementedError.new('type is not implemented yet.')
88
92
  end
89
93
  end
90
94
  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 once(event, callback)
76
+ event_emitter_proxy.once(event, callback)
77
+ end
78
+
79
+ # -- inherited from EventEmitter --
80
+ # @nodoc
81
+ def on(event, callback)
82
+ event_emitter_proxy.on(event, callback)
83
+ end
84
+
85
+ # -- inherited from EventEmitter --
86
+ # @nodoc
87
+ def off(event, callback)
88
+ event_emitter_proxy.off(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(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(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(selector, pageFunction, arg: 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(selector, pageFunction, arg: 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
  #
@@ -172,7 +91,7 @@ module Playwright
172
91
  # page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
173
92
  # ```
174
93
  def bounding_box
175
- raise NotImplementedError.new('bounding_box is not implemented yet.')
94
+ wrap_impl(@impl.bounding_box)
176
95
  end
177
96
 
178
97
  # This method checks the element by performing the following steps:
@@ -189,7 +108,7 @@ module Playwright
189
108
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
190
109
  # Passing zero timeout disables this.
191
110
  def check(force: nil, noWaitAfter: nil, timeout: nil)
192
- raise NotImplementedError.new('check is not implemented yet.')
111
+ wrap_impl(@impl.check(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
193
112
  end
194
113
 
195
114
  # This method clicks the element by performing the following steps:
@@ -211,12 +130,12 @@ module Playwright
211
130
  noWaitAfter: nil,
212
131
  position: nil,
213
132
  timeout: nil)
214
- wrap_impl(@impl.click(button: button, clickCount: clickCount, delay: delay, force: force, modifiers: modifiers, noWaitAfter: noWaitAfter, position: position, timeout: timeout))
133
+ wrap_impl(@impl.click(button: unwrap_impl(button), clickCount: unwrap_impl(clickCount), delay: unwrap_impl(delay), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
215
134
  end
216
135
 
217
136
  # Returns the content frame for element handles referencing iframe nodes, or `null` otherwise
218
137
  def content_frame
219
- raise NotImplementedError.new('content_frame is not implemented yet.')
138
+ wrap_impl(@impl.content_frame)
220
139
  end
221
140
 
222
141
  # This method double clicks the element by performing the following steps:
@@ -240,7 +159,7 @@ module Playwright
240
159
  noWaitAfter: nil,
241
160
  position: nil,
242
161
  timeout: nil)
243
- raise NotImplementedError.new('dblclick is not implemented yet.')
162
+ wrap_impl(@impl.dblclick(button: unwrap_impl(button), delay: unwrap_impl(delay), force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
244
163
  end
245
164
 
246
165
  # The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the elment, `click`
@@ -293,25 +212,97 @@ module Playwright
293
212
  # element_handle.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
294
213
  # ```
295
214
  def dispatch_event(type, eventInit: nil)
296
- raise NotImplementedError.new('dispatch_event is not implemented yet.')
215
+ wrap_impl(@impl.dispatch_event(unwrap_impl(type), eventInit: unwrap_impl(eventInit)))
216
+ end
217
+
218
+ # Returns the return value of `expression`.
219
+ #
220
+ # The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
221
+ # argument to `expression`. See [Working with selectors](./selectors.md) for more details. If no elements match the
222
+ # selector, the method throws an error.
223
+ #
224
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelector`] would wait for the promise to resolve
225
+ # and return its value.
226
+ #
227
+ # Examples:
228
+ #
229
+ #
230
+ # ```js
231
+ # const tweetHandle = await page.$('.tweet');
232
+ # expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
233
+ # expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
234
+ # ```
235
+ #
236
+ # ```python async
237
+ # tweet_handle = await page.query_selector(".tweet")
238
+ # assert await tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
239
+ # assert await tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
240
+ # ```
241
+ #
242
+ # ```python sync
243
+ # tweet_handle = page.query_selector(".tweet")
244
+ # assert tweet_handle.eval_on_selector(".like", "node => node.innerText") == "100"
245
+ # assert tweet_handle.eval_on_selector(".retweets", "node => node.innerText") = "10"
246
+ # ```
247
+ def eval_on_selector(selector, expression, arg: nil)
248
+ wrap_impl(@impl.eval_on_selector(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
249
+ end
250
+
251
+ # Returns the return value of `expression`.
252
+ #
253
+ # The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
254
+ # matched elements as a first argument to `expression`. See [Working with selectors](./selectors.md) for more details.
255
+ #
256
+ # If `expression` returns a [Promise], then [`method: ElementHandle.evalOnSelectorAll`] would wait for the promise to
257
+ # resolve and return its value.
258
+ #
259
+ # Examples:
260
+ #
261
+ # ```html
262
+ # <div class="feed">
263
+ # <div class="tweet">Hello!</div>
264
+ # <div class="tweet">Hi!</div>
265
+ # </div>
266
+ # ```
267
+ #
268
+ #
269
+ # ```js
270
+ # const feedHandle = await page.$('.feed');
271
+ # expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
272
+ # ```
273
+ #
274
+ # ```python async
275
+ # feed_handle = await page.query_selector(".feed")
276
+ # assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
277
+ # ```
278
+ #
279
+ # ```python sync
280
+ # feed_handle = page.query_selector(".feed")
281
+ # assert feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"]
282
+ # ```
283
+ def eval_on_selector_all(selector, expression, arg: nil)
284
+ wrap_impl(@impl.eval_on_selector_all(unwrap_impl(selector), unwrap_impl(expression), arg: unwrap_impl(arg)))
297
285
  end
298
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
- raise NotImplementedError.new('fill is not implemented yet.')
293
+ wrap_impl(@impl.fill(unwrap_impl(value), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
304
294
  end
305
295
 
306
296
  # Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
307
297
  def focus
308
- raise NotImplementedError.new('focus is not implemented yet.')
298
+ wrap_impl(@impl.focus)
309
299
  end
310
300
 
311
301
  # Returns element attribute value.
312
302
  def get_attribute(name)
313
- raise NotImplementedError.new('get_attribute is not implemented yet.')
303
+ wrap_impl(@impl.get_attribute(unwrap_impl(name)))
314
304
  end
305
+ alias_method :[], :get_attribute
315
306
 
316
307
  # This method hovers over the element by performing the following steps:
317
308
  # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
@@ -324,52 +315,52 @@ module Playwright
324
315
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
325
316
  # Passing zero timeout disables this.
326
317
  def hover(force: nil, modifiers: nil, position: nil, timeout: nil)
327
- raise NotImplementedError.new('hover is not implemented yet.')
318
+ wrap_impl(@impl.hover(force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
328
319
  end
329
320
 
330
321
  # Returns the `element.innerHTML`.
331
322
  def inner_html
332
- raise NotImplementedError.new('inner_html is not implemented yet.')
323
+ wrap_impl(@impl.inner_html)
333
324
  end
334
325
 
335
326
  # Returns the `element.innerText`.
336
327
  def inner_text
337
- raise NotImplementedError.new('inner_text is not implemented yet.')
328
+ wrap_impl(@impl.inner_text)
338
329
  end
339
330
 
340
331
  # Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
341
332
  def checked?
342
- raise NotImplementedError.new('checked? is not implemented yet.')
333
+ wrap_impl(@impl.checked?)
343
334
  end
344
335
 
345
336
  # Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).
346
337
  def disabled?
347
- raise NotImplementedError.new('disabled? is not implemented yet.')
338
+ wrap_impl(@impl.disabled?)
348
339
  end
349
340
 
350
341
  # Returns whether the element is [editable](./actionability.md#editable).
351
342
  def editable?
352
- raise NotImplementedError.new('editable? is not implemented yet.')
343
+ wrap_impl(@impl.editable?)
353
344
  end
354
345
 
355
346
  # Returns whether the element is [enabled](./actionability.md#enabled).
356
347
  def enabled?
357
- raise NotImplementedError.new('enabled? is not implemented yet.')
348
+ wrap_impl(@impl.enabled?)
358
349
  end
359
350
 
360
351
  # Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).
361
352
  def hidden?
362
- raise NotImplementedError.new('hidden? is not implemented yet.')
353
+ wrap_impl(@impl.hidden?)
363
354
  end
364
355
 
365
356
  # Returns whether the element is [visible](./actionability.md#visible).
366
357
  def visible?
367
- raise NotImplementedError.new('visible? is not implemented yet.')
358
+ wrap_impl(@impl.visible?)
368
359
  end
369
360
 
370
361
  # Returns the frame containing the given element.
371
362
  def owner_frame
372
- raise NotImplementedError.new('owner_frame is not implemented yet.')
363
+ wrap_impl(@impl.owner_frame)
373
364
  end
374
365
 
375
366
  # Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
@@ -391,7 +382,19 @@ module Playwright
391
382
  # Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When speficied with the
392
383
  # modifier, modifier is pressed and being held while the subsequent key is being pressed.
393
384
  def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
394
- wrap_impl(@impl.press(key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout))
385
+ wrap_impl(@impl.press(unwrap_impl(key), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
386
+ end
387
+
388
+ # The method finds an element matching the specified selector in the `ElementHandle`'s subtree. See
389
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns `null`.
390
+ def query_selector(selector)
391
+ wrap_impl(@impl.query_selector(unwrap_impl(selector)))
392
+ end
393
+
394
+ # The method finds all elements matching the specified selector in the `ElementHandle`s subtree. See
395
+ # [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns empty array.
396
+ def query_selector_all(selector)
397
+ wrap_impl(@impl.query_selector_all(unwrap_impl(selector)))
395
398
  end
396
399
 
397
400
  # Returns the buffer with the captured screenshot.
@@ -404,7 +407,7 @@ module Playwright
404
407
  quality: nil,
405
408
  timeout: nil,
406
409
  type: nil)
407
- raise NotImplementedError.new('screenshot is not implemented yet.')
410
+ wrap_impl(@impl.screenshot(omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
408
411
  end
409
412
 
410
413
  # This method waits for [actionability](./actionability.md) checks, then tries to scroll element into view, unless it is
@@ -414,7 +417,7 @@ module Playwright
414
417
  # Throws when `elementHandle` does not point to an element
415
418
  # [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
416
419
  def scroll_into_view_if_needed(timeout: nil)
417
- raise NotImplementedError.new('scroll_into_view_if_needed is not implemented yet.')
420
+ wrap_impl(@impl.scroll_into_view_if_needed(timeout: unwrap_impl(timeout)))
418
421
  end
419
422
 
420
423
  # Returns the array of option values that have been successfully selected.
@@ -455,7 +458,6 @@ module Playwright
455
458
  # ```
456
459
  #
457
460
  # ```python sync
458
- # # FIXME
459
461
  # # single selection matching the value
460
462
  # handle.select_option("blue")
461
463
  # # single selection matching both the value and the label
@@ -465,14 +467,20 @@ module Playwright
465
467
  # # multiple selection for blue, red and second option
466
468
  # handle.select_option(value="blue", { index: 2 }, "red")
467
469
  # ```
468
- def select_option(values, noWaitAfter: nil, timeout: nil)
469
- raise NotImplementedError.new('select_option is not implemented yet.')
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)))
470
478
  end
471
479
 
472
480
  # This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text
473
481
  # content.
474
482
  def select_text(timeout: nil)
475
- raise NotImplementedError.new('select_text is not implemented yet.')
483
+ wrap_impl(@impl.select_text(timeout: unwrap_impl(timeout)))
476
484
  end
477
485
 
478
486
  # This method expects `elementHandle` to point to an
@@ -481,7 +489,7 @@ module Playwright
481
489
  # Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
482
490
  # are resolved relative to the the current working directory. For empty array, clears the selected files.
483
491
  def set_input_files(files, noWaitAfter: nil, timeout: nil)
484
- raise NotImplementedError.new('set_input_files is not implemented yet.')
492
+ wrap_impl(@impl.set_input_files(unwrap_impl(files), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
485
493
  end
486
494
  alias_method :input_files=, :set_input_files
487
495
 
@@ -503,12 +511,12 @@ module Playwright
503
511
  noWaitAfter: nil,
504
512
  position: nil,
505
513
  timeout: nil)
506
- raise NotImplementedError.new('tap_point is not implemented yet.')
514
+ wrap_impl(@impl.tap_point(force: unwrap_impl(force), modifiers: unwrap_impl(modifiers), noWaitAfter: unwrap_impl(noWaitAfter), position: unwrap_impl(position), timeout: unwrap_impl(timeout)))
507
515
  end
508
516
 
509
517
  # Returns the `node.textContent`.
510
518
  def text_content
511
- raise NotImplementedError.new('text_content is not implemented yet.')
519
+ wrap_impl(@impl.text_content)
512
520
  end
513
521
 
514
522
  # Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
@@ -551,8 +559,8 @@ module Playwright
551
559
  # element_handle.type("some text")
552
560
  # element_handle.press("Enter")
553
561
  # ```
554
- def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
555
- wrap_impl(@impl.type_text(text, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout))
562
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
563
+ wrap_impl(@impl.type(unwrap_impl(text), delay: unwrap_impl(delay), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
556
564
  end
557
565
 
558
566
  # This method checks the element by performing the following steps:
@@ -569,7 +577,7 @@ module Playwright
569
577
  # When all steps combined have not finished during the specified `timeout`, this method rejects with a `TimeoutError`.
570
578
  # Passing zero timeout disables this.
571
579
  def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
572
- raise NotImplementedError.new('uncheck is not implemented yet.')
580
+ wrap_impl(@impl.uncheck(force: unwrap_impl(force), noWaitAfter: unwrap_impl(noWaitAfter), timeout: unwrap_impl(timeout)))
573
581
  end
574
582
 
575
583
  # Returns when the element satisfies the `state`.
@@ -587,7 +595,7 @@ module Playwright
587
595
  #
588
596
  # If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.
589
597
  def wait_for_element_state(state, timeout: nil)
590
- raise NotImplementedError.new('wait_for_element_state is not implemented yet.')
598
+ wrap_impl(@impl.wait_for_element_state(unwrap_impl(state), timeout: unwrap_impl(timeout)))
591
599
  end
592
600
 
593
601
  # Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or
@@ -622,25 +630,29 @@ module Playwright
622
630
  #
623
631
  # > NOTE: This method does not work across navigations, use [`method: Page.waitForSelector`] instead.
624
632
  def wait_for_selector(selector, state: nil, timeout: nil)
625
- raise NotImplementedError.new('wait_for_selector is not implemented yet.')
633
+ wrap_impl(@impl.wait_for_selector(unwrap_impl(selector), state: unwrap_impl(state), timeout: unwrap_impl(timeout)))
626
634
  end
627
635
 
628
636
  # -- inherited from EventEmitter --
629
637
  # @nodoc
630
- def on(event, callback)
631
- wrap_impl(@impl.on(event, callback))
638
+ def once(event, callback)
639
+ event_emitter_proxy.once(event, callback)
632
640
  end
633
641
 
634
642
  # -- inherited from EventEmitter --
635
643
  # @nodoc
636
- def off(event, callback)
637
- wrap_impl(@impl.off(event, callback))
644
+ def on(event, callback)
645
+ event_emitter_proxy.on(event, callback)
638
646
  end
639
647
 
640
648
  # -- inherited from EventEmitter --
641
649
  # @nodoc
642
- def once(event, callback)
643
- wrap_impl(@impl.once(event, callback))
650
+ def off(event, callback)
651
+ event_emitter_proxy.off(event, callback)
652
+ end
653
+
654
+ private def event_emitter_proxy
655
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
644
656
  end
645
657
  end
646
658
  end