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
data/lib/playwright.rb CHANGED
@@ -10,13 +10,17 @@ require 'concurrent'
10
10
  require 'playwright/errors'
11
11
  require 'playwright/events'
12
12
  require 'playwright/event_emitter'
13
+ require 'playwright/event_emitter_proxy'
13
14
  require 'playwright/javascript'
14
15
  require 'playwright/utils'
15
16
 
17
+ require 'playwright/api_implementation'
16
18
  require 'playwright/channel'
17
19
  require 'playwright/channel_owner'
18
- require 'playwright/input_type'
20
+ require 'playwright/http_headers'
21
+ require 'playwright/input_files'
19
22
  require 'playwright/connection'
23
+ require 'playwright/select_option_values'
20
24
  require 'playwright/timeout_settings'
21
25
  require 'playwright/transport'
22
26
  require 'playwright/url_matcher'
@@ -35,7 +39,7 @@ module Playwright
35
39
 
36
40
  playwright_promise = connection.async_wait_for_object_with_known_name('Playwright')
37
41
  Thread.new { connection.run }
38
- playwright = PlaywrightApi.from_channel_owner(playwright_promise.value!)
42
+ playwright = PlaywrightApi.wrap(playwright_promise.value!)
39
43
  begin
40
44
  block.call(playwright)
41
45
  ensure
@@ -0,0 +1,23 @@
1
+ module Playwright
2
+ define_api_implementation :AndroidInputImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+
7
+ def type(text)
8
+ @channel.send_message_to_server('inputType', text: text)
9
+ end
10
+
11
+ def press(key)
12
+ @channel.send_message_to_server('inputPress', key: key)
13
+ end
14
+
15
+ def tap_point(point)
16
+ @channel.send_message_to_server('inputTap', point: point)
17
+ end
18
+
19
+ def drag(from, to, steps)
20
+ @channel.send_message_to_server('inputDrag', from: from, to: to, steps: steps)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Playwright
2
+ # Each Impl class include this module.
3
+ # Used for detecting whether the object is a XXXXImpl or not.
4
+ module ApiImplementation ; end
5
+
6
+ def self.define_api_implementation(class_name, &block)
7
+ klass = Class.new
8
+ klass.include(ApiImplementation)
9
+ klass.class_eval(&block) if block
10
+ if ::Playwright.const_defined?(class_name)
11
+ raise ArgumentError.new("Playwright::#{class_name} already exist. Choose another class name.")
12
+ end
13
+ ::Playwright.const_set(class_name, klass)
14
+ end
15
+ end
16
+
17
+ # load subclasses
18
+ Dir[File.join(__dir__, '*_impl.rb')].each { |f| require f }
@@ -24,5 +24,12 @@ module Playwright
24
24
  nil
25
25
  end
26
26
  end
27
+
28
+ # @param method [String]
29
+ # @param params [Hash]
30
+ def send_no_reply(method, params)
31
+ @connection.async_send_message_to_server(@guid, method, params)
32
+ nil
33
+ end
27
34
  end
28
35
  end
@@ -39,13 +39,14 @@ module Playwright
39
39
 
40
40
  attr_reader :channel
41
41
 
42
- def dispose
42
+ # used only from Connection. Not intended for public use. So keep private.
43
+ private def dispose!
43
44
  # Clean up from parent and connection.
44
45
  @parent&.send(:delete_object_from_child, @guid)
45
46
  @connection.send(:delete_object_from_channel_owner, @guid)
46
47
 
47
48
  # Dispose all children.
48
- @objects.each_value(&:dispose)
49
+ @objects.each_value { |object| object.send(:dispose!) }
49
50
  @objects.clear
50
51
  end
51
52
 
@@ -58,16 +59,14 @@ module Playwright
58
59
  "#<#{@guid}>"
59
60
  end
60
61
 
61
- private
62
-
63
- def after_initialize
62
+ private def after_initialize
64
63
  end
65
64
 
66
- def update_object_from_child(guid, child)
65
+ private def update_object_from_child(guid, child)
67
66
  @objects[guid] = child
68
67
  end
69
68
 
70
- def delete_object_from_child(guid)
69
+ private def delete_object_from_child(guid)
71
70
  @objects.delete(guid)
72
71
  end
73
72
  end
@@ -1,3 +1,12 @@
1
1
  module Playwright
2
- define_channel_owner :Android
2
+ define_channel_owner :Android do
3
+ private def after_initialize
4
+ @timeout_settings = TimeoutSettings.new
5
+ end
6
+
7
+ def devices
8
+ resp = @channel.send_message_to_server('devices')
9
+ resp.map { |device| ChannelOwners::AndroidDevice.from(device) }
10
+ end
11
+ end
3
12
  end
@@ -0,0 +1,163 @@
1
+ module Playwright
2
+ define_channel_owner :AndroidDevice do
3
+ include Utils::PrepareBrowserContextOptions
4
+
5
+ private def after_initialize
6
+ @input = AndroidInputImpl.new(@channel)
7
+ end
8
+
9
+ attr_reader :input
10
+
11
+ def serial
12
+ @initializer['serial']
13
+ end
14
+
15
+ def model
16
+ @initializer['model']
17
+ end
18
+
19
+ private def to_regex(value)
20
+ case value
21
+ when nil
22
+ nil
23
+ when Regexp
24
+ value
25
+ else
26
+ Regexp.new("^#{value}$")
27
+ end
28
+ end
29
+
30
+ private def to_selector_channel(selector)
31
+ {
32
+ checkable: selector[:checkable],
33
+ checked: selector[:checked],
34
+ clazz: to_regex(selector[:clazz]),
35
+ pkg: to_regex(selector[:pkg]),
36
+ desc: to_regex(selector[:desc]),
37
+ res: to_regex(selector[:res]),
38
+ text: to_regex(selector[:text]),
39
+ clickable: selector[:clickable],
40
+ depth: selector[:depth],
41
+ enabled: selector[:enabled],
42
+ focusable: selector[:focusable],
43
+ focused: selector[:focused],
44
+ hasChild: selector[:hasChild] ? { selector: to_selector_channel(selector[:hasChild][:selector]) } : nil,
45
+ hasDescendant: selector[:hasDescendant] ? {
46
+ selector: to_selector_channel(selector[:hasDescendant][:selector]),
47
+ maxDepth: selector[:hasDescendant][:maxDepth],
48
+ } : nil,
49
+ longClickable: selector[:longClickable],
50
+ scrollable: selector[:scrollable],
51
+ selected: selector[:selected],
52
+ }.compact
53
+ end
54
+
55
+ def tap_on(selector, duration: nil, timeout: nil)
56
+ params = {
57
+ selector: to_selector_channel(selector),
58
+ duration: duration,
59
+ timeout: timeout,
60
+ }.compact
61
+ @channel.send_message_to_server('tap', params)
62
+ end
63
+
64
+ def info(selector)
65
+ @channel.send_message_to_server('info', selector: to_selector_channel(selector))
66
+ end
67
+
68
+ def tree
69
+ @channel.send_message_to_server('tree')
70
+ end
71
+
72
+ def screenshot(path: nil)
73
+ encoded_binary = @channel.send_message_to_server('screenshot')
74
+ decoded_binary = Base64.strict_decode64(encoded_binary)
75
+ if path
76
+ File.open(path, 'wb') do |f|
77
+ f.write(decoded_binary)
78
+ end
79
+ end
80
+ decoded_binary
81
+ end
82
+
83
+ def close
84
+ @channel.send_message_to_server('close')
85
+ emit(Events::AndroidDevice::Close)
86
+ end
87
+
88
+ def shell(command)
89
+ resp = @channel.send_message_to_server('shell', command: command)
90
+ Base64.strict_decode64(resp)
91
+ end
92
+
93
+ def launch_browser(
94
+ pkg: nil,
95
+ acceptDownloads: nil,
96
+ bypassCSP: nil,
97
+ colorScheme: nil,
98
+ deviceScaleFactor: nil,
99
+ extraHTTPHeaders: nil,
100
+ geolocation: nil,
101
+ hasTouch: nil,
102
+ httpCredentials: nil,
103
+ ignoreHTTPSErrors: nil,
104
+ isMobile: nil,
105
+ javaScriptEnabled: nil,
106
+ locale: nil,
107
+ noViewport: nil,
108
+ offline: nil,
109
+ permissions: nil,
110
+ proxy: nil,
111
+ record_har_omit_content: nil,
112
+ record_har_path: nil,
113
+ record_video_dir: nil,
114
+ record_video_size: nil,
115
+ storageState: nil,
116
+ timezoneId: nil,
117
+ userAgent: nil,
118
+ viewport: nil,
119
+ &block)
120
+ params = {
121
+ pkg: pkg,
122
+ acceptDownloads: acceptDownloads,
123
+ bypassCSP: bypassCSP,
124
+ colorScheme: colorScheme,
125
+ deviceScaleFactor: deviceScaleFactor,
126
+ extraHTTPHeaders: extraHTTPHeaders,
127
+ geolocation: geolocation,
128
+ hasTouch: hasTouch,
129
+ httpCredentials: httpCredentials,
130
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
131
+ isMobile: isMobile,
132
+ javaScriptEnabled: javaScriptEnabled,
133
+ locale: locale,
134
+ noViewport: noViewport,
135
+ offline: offline,
136
+ permissions: permissions,
137
+ proxy: proxy,
138
+ record_har_omit_content: record_har_omit_content,
139
+ record_har_path: record_har_path,
140
+ record_video_dir: record_video_dir,
141
+ record_video_size: record_video_size,
142
+ storageState: storageState,
143
+ timezoneId: timezoneId,
144
+ userAgent: userAgent,
145
+ viewport: viewport,
146
+ }.compact
147
+ prepare_browser_context_options(params)
148
+
149
+ resp = @channel.send_message_to_server('launchBrowser', params)
150
+ context = ChannelOwners::BrowserContext.from(resp)
151
+
152
+ if block
153
+ begin
154
+ block.call(context)
155
+ ensure
156
+ context.close
157
+ end
158
+ else
159
+ context
160
+ end
161
+ end
162
+ end
163
+ end
@@ -2,8 +2,9 @@ module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser.py
3
3
  define_channel_owner :Browser do
4
4
  include Utils::Errors::SafeCloseError
5
+ include Utils::PrepareBrowserContextOptions
5
6
 
6
- def after_initialize
7
+ private def after_initialize
7
8
  @contexts = Set.new
8
9
  @channel.on('close', method(:on_close))
9
10
  end
@@ -16,26 +17,25 @@ module Playwright
16
17
  @connected
17
18
  end
18
19
 
19
- def new_context(**options)
20
+ def new_context(**options, &block)
20
21
  params = options.dup
21
- # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
22
- if params[:viewport] == 0
23
- params.delete(:viewport)
24
- params[:noDefaultViewport] = true
25
- end
26
- if params[:extraHTTPHeaders]
27
- # TODO
28
- end
29
- if params[:storageState].is_a?(String)
30
- params[:storageState] = JSON.parse(File.read(params[:storageState]))
31
- end
22
+ prepare_browser_context_options(params)
32
23
 
33
24
  resp = @channel.send_message_to_server('newContext', params.compact)
34
25
  context = ChannelOwners::BrowserContext.from(resp)
35
26
  @contexts << context
36
27
  context.browser = self
37
28
  context.options = params
38
- context
29
+
30
+ if block
31
+ begin
32
+ block.call(context)
33
+ ensure
34
+ context.close
35
+ end
36
+ else
37
+ context
38
+ end
39
39
  end
40
40
 
41
41
  def new_page(**options)
@@ -4,11 +4,14 @@ module Playwright
4
4
  include Utils::Errors::SafeCloseError
5
5
  attr_writer :browser, :owner_page, :options
6
6
 
7
- def after_initialize
7
+ private def after_initialize
8
8
  @pages = Set.new
9
9
 
10
10
  @channel.once('close', ->(_) { on_close })
11
- @channel.on('page', ->(hash) { on_page(ChannelOwners::Page.from(hash['page']) )})
11
+ @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
12
+ @channel.on('route', ->(params) {
13
+ on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
14
+ })
12
15
  end
13
16
 
14
17
  private def on_page(page)
@@ -17,6 +20,11 @@ module Playwright
17
20
  emit(Events::BrowserContext::Page, page)
18
21
  end
19
22
 
23
+ private def on_route(route, request)
24
+ # @routes.each ...
25
+ route.continue_
26
+ end
27
+
20
28
  def pages
21
29
  @pages.to_a
22
30
  end
@@ -0,0 +1,27 @@
1
+ module Playwright
2
+ define_channel_owner :Download do
3
+ def url
4
+ @initializer['url']
5
+ end
6
+
7
+ def suggested_filename
8
+ @initializer['suggestedFilename']
9
+ end
10
+
11
+ def delete
12
+ @channel.send_message_to_server('delete')
13
+ end
14
+
15
+ def failure
16
+ @channel.send_message_to_server('failure')
17
+ end
18
+
19
+ def path
20
+ @channel.send_message_to_server('path')
21
+ end
22
+
23
+ def save_as(path)
24
+ @channel.send_message_to_server('saveAs', path: path)
25
+ end
26
+ end
27
+ end
@@ -3,6 +3,91 @@ require_relative './js_handle'
3
3
  module Playwright
4
4
  module ChannelOwners
5
5
  class ElementHandle < JSHandle
6
+ def as_element
7
+ self
8
+ end
9
+
10
+ def owner_frame
11
+ resp = @channel.send_message_to_server('ownerFrame')
12
+ ChannelOwners::Frame.from_nullable(resp)
13
+ end
14
+
15
+ def content_frame
16
+ resp = @channel.send_message_to_server('contentFrame')
17
+ ChannelOwners::Frame.from_nullable(resp)
18
+ end
19
+
20
+ def get_attribute(name)
21
+ @channel.send_message_to_server('getAttribute', name: name)
22
+ end
23
+
24
+ def text_content
25
+ @channel.send_message_to_server('textContent')
26
+ end
27
+
28
+ def inner_text
29
+ @channel.send_message_to_server('innerText')
30
+ end
31
+
32
+ def inner_html
33
+ @channel.send_message_to_server('innerHTML')
34
+ end
35
+
36
+ def checked?
37
+ @channel.send_message_to_server('isChecked')
38
+ end
39
+
40
+ def disabled?
41
+ @channel.send_message_to_server('isDisabled')
42
+ end
43
+
44
+ def editable?
45
+ @channel.send_message_to_server('isEditable')
46
+ end
47
+
48
+ def enabled?
49
+ @channel.send_message_to_server('isEnabled')
50
+ end
51
+
52
+ def hidden?
53
+ @channel.send_message_to_server('isHidden')
54
+ end
55
+
56
+ def visible?
57
+ @channel.send_message_to_server('isVisible')
58
+ end
59
+
60
+ def dispatch_event(type, eventInit: nil)
61
+ params = {
62
+ type: type,
63
+ eventInit: JavaScript::ValueSerializer.new(eventInit).serialize,
64
+ }.compact
65
+ @channel.send_message_to_server('dispatchEvent', params)
66
+
67
+ nil
68
+ end
69
+
70
+ def scroll_into_view_if_needed(timeout: nil)
71
+ params = {
72
+ timeout: timeout,
73
+ }.compact
74
+ @channel.send_message_to_server('scrollIntoViewIfNeeded', params)
75
+
76
+ nil
77
+ end
78
+
79
+ def hover(force: nil, modifiers: nil, position: nil, timeout: nil)
80
+ params = {
81
+ force: force,
82
+ modifiers: modifiers,
83
+ position: position,
84
+ timeout: timeout,
85
+ }
86
+ @channel.send_message_to_server('hover', params)
87
+
88
+ nil
89
+ end
90
+
6
91
  def click(
7
92
  button: nil,
8
93
  clickCount: nil,
@@ -28,7 +113,100 @@ module Playwright
28
113
  nil
29
114
  end
30
115
 
31
- def type_text(text, delay: nil, noWaitAfter: nil, timeout: nil)
116
+ def dblclick(
117
+ button: nil,
118
+ delay: nil,
119
+ force: nil,
120
+ modifiers: nil,
121
+ noWaitAfter: nil,
122
+ position: nil,
123
+ timeout: nil)
124
+
125
+ params = {
126
+ button: button,
127
+ delay: delay,
128
+ force: force,
129
+ modifiers: modifiers,
130
+ noWaitAfter: noWaitAfter,
131
+ position: position,
132
+ timeout: timeout,
133
+ }.compact
134
+ @channel.send_message_to_server('dblclick', params)
135
+
136
+ nil
137
+ end
138
+
139
+ def select_option(
140
+ element: nil,
141
+ index: nil,
142
+ value: nil,
143
+ label: nil,
144
+ noWaitAfter: nil,
145
+ timeout: nil)
146
+ base_params = SelectOptionValues.new(
147
+ element: element,
148
+ index: index,
149
+ value: value,
150
+ label: label,
151
+ ).as_params
152
+ params = base_params + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
153
+ @channel.send_message_to_server('selectOption', params)
154
+
155
+ nil
156
+ end
157
+
158
+ def tap_point(
159
+ force: nil,
160
+ modifiers: nil,
161
+ noWaitAfter: nil,
162
+ position: nil,
163
+ timeout: nil)
164
+
165
+ params = {
166
+ force: force,
167
+ modifiers: modifiers,
168
+ noWaitAfter: noWaitAfter,
169
+ position: position,
170
+ timeout: timeout,
171
+ }.compact
172
+ @channel.send_message_to_server('tap', params)
173
+
174
+ nil
175
+ end
176
+
177
+ def fill(value, noWaitAfter: nil, timeout: nil)
178
+ params = {
179
+ value: value,
180
+ noWaitAfter: noWaitAfter,
181
+ timeout: timeout,
182
+ }
183
+ @channel.send_message_to_server('fill', params)
184
+
185
+ nil
186
+ end
187
+
188
+ def select_text(timeout: nil)
189
+ params = { timeout: timeout }.compact
190
+ @channel.send_message_to_server('selectText', params)
191
+
192
+ nil
193
+ end
194
+
195
+ def set_input_files(files, noWaitAfter: nil, timeout: nil)
196
+ file_payloads = InputFiles.new(files).as_params
197
+ params = { files: file_payloads, noWaitAfter: noWaitAfter, timeout: timeout }.compact
198
+ @channel.send_message_to_server('setInputFiles', params)
199
+
200
+ nil
201
+ end
202
+
203
+ def focus
204
+ @channel.send_message_to_server('focus')
205
+
206
+ nil
207
+ end
208
+
209
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
32
210
  params = {
33
211
  text: text,
34
212
  delay: delay,
@@ -52,6 +230,56 @@ module Playwright
52
230
  nil
53
231
  end
54
232
 
233
+ def check(force: nil, noWaitAfter: nil, timeout: nil)
234
+ params = {
235
+ force: force,
236
+ noWaitAfter: noWaitAfter,
237
+ timeout: timeout,
238
+ }.compact
239
+ @channel.send_message_to_server('check', params)
240
+
241
+ nil
242
+ end
243
+
244
+ def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
245
+ params = {
246
+ force: force,
247
+ noWaitAfter: noWaitAfter,
248
+ timeout: timeout,
249
+ }.compact
250
+ @channel.send_message_to_server('uncheck', params)
251
+
252
+ nil
253
+ end
254
+
255
+ def bounding_box
256
+ @channel.send_message_to_server('boundingBox')
257
+ end
258
+
259
+ def screenshot(
260
+ omitBackground: nil,
261
+ path: nil,
262
+ quality: nil,
263
+ timeout: nil,
264
+ type: nil)
265
+
266
+ params = {
267
+ omitBackground: omitBackground,
268
+ path: path,
269
+ quality: quality,
270
+ timeout: timeout,
271
+ type: type,
272
+ }.compact
273
+ encoded_binary = @channel.send_message_to_server('screenshot', params)
274
+ decoded_binary = Base64.strict_decode64(encoded_binary)
275
+ if path
276
+ File.open(path, 'wb') do |f|
277
+ f.write(decoded_binary)
278
+ end
279
+ end
280
+ decoded_binary
281
+ end
282
+
55
283
  def query_selector(selector)
56
284
  resp = @channel.send_message_to_server('querySelector', selector: selector)
57
285
  ChannelOwners::ElementHandle.from_nullable(resp)
@@ -78,6 +306,20 @@ module Playwright
78
306
  JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
79
307
  end
80
308
  end
309
+
310
+ def wait_for_element_state(state, timeout: nil)
311
+ params = { state: state, timeout: timeout }.compact
312
+ @channel.send_message_to_server('waitForElementState', params)
313
+
314
+ nil
315
+ end
316
+
317
+ def wait_for_selector(selector, state: nil, timeout: nil)
318
+ params = { selector: selector, state: state, timeout: timeout }.compact
319
+ resp = @channel.send_message_to_server('waitForSelector', params)
320
+
321
+ ChannelOwners::ElementHandle.from_nullable(resp)
322
+ end
81
323
  end
82
324
  end
83
325
  end