playwright-ruby-client 0.0.3 → 0.0.8

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -12
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +8 -0
  5. data/lib/playwright/channel_owner.rb +16 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +163 -0
  8. data/lib/playwright/channel_owners/browser.rb +22 -29
  9. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  10. data/lib/playwright/channel_owners/console_message.rb +21 -0
  11. data/lib/playwright/channel_owners/element_handle.rb +314 -0
  12. data/lib/playwright/channel_owners/frame.rb +466 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +55 -0
  14. data/lib/playwright/channel_owners/page.rb +353 -5
  15. data/lib/playwright/channel_owners/request.rb +90 -0
  16. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  17. data/lib/playwright/connection.rb +15 -14
  18. data/lib/playwright/errors.rb +1 -1
  19. data/lib/playwright/event_emitter.rb +13 -0
  20. data/lib/playwright/input_files.rb +42 -0
  21. data/lib/playwright/input_type.rb +19 -0
  22. data/lib/playwright/input_types/android_input.rb +19 -0
  23. data/lib/playwright/input_types/keyboard.rb +32 -0
  24. data/lib/playwright/input_types/mouse.rb +4 -0
  25. data/lib/playwright/input_types/touchscreen.rb +4 -0
  26. data/lib/playwright/javascript.rb +13 -0
  27. data/lib/playwright/javascript/expression.rb +67 -0
  28. data/lib/playwright/javascript/function.rb +67 -0
  29. data/lib/playwright/javascript/value_parser.rb +75 -0
  30. data/lib/playwright/javascript/value_serializer.rb +54 -0
  31. data/lib/playwright/playwright_api.rb +45 -25
  32. data/lib/playwright/select_option_values.rb +32 -0
  33. data/lib/playwright/timeout_settings.rb +19 -0
  34. data/lib/playwright/url_matcher.rb +19 -0
  35. data/lib/playwright/utils.rb +37 -0
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +73 -0
  38. data/lib/playwright_api/accessibility.rb +60 -6
  39. data/lib/playwright_api/android.rb +33 -0
  40. data/lib/playwright_api/android_device.rb +78 -0
  41. data/lib/playwright_api/android_input.rb +25 -0
  42. data/lib/playwright_api/binding_call.rb +18 -0
  43. data/lib/playwright_api/browser.rb +136 -44
  44. data/lib/playwright_api/browser_context.rb +378 -51
  45. data/lib/playwright_api/browser_type.rb +137 -55
  46. data/lib/playwright_api/cdp_session.rb +32 -7
  47. data/lib/playwright_api/chromium_browser_context.rb +31 -0
  48. data/lib/playwright_api/console_message.rb +27 -7
  49. data/lib/playwright_api/dialog.rb +47 -3
  50. data/lib/playwright_api/download.rb +29 -5
  51. data/lib/playwright_api/element_handle.rb +429 -143
  52. data/lib/playwright_api/file_chooser.rb +13 -2
  53. data/lib/playwright_api/frame.rb +633 -179
  54. data/lib/playwright_api/js_handle.rb +97 -17
  55. data/lib/playwright_api/keyboard.rb +152 -24
  56. data/lib/playwright_api/mouse.rb +28 -3
  57. data/lib/playwright_api/page.rb +1183 -317
  58. data/lib/playwright_api/playwright.rb +174 -13
  59. data/lib/playwright_api/request.rb +115 -30
  60. data/lib/playwright_api/response.rb +22 -3
  61. data/lib/playwright_api/route.rb +63 -4
  62. data/lib/playwright_api/selectors.rb +29 -7
  63. data/lib/playwright_api/touchscreen.rb +2 -1
  64. data/lib/playwright_api/video.rb +11 -1
  65. data/lib/playwright_api/web_socket.rb +5 -5
  66. data/lib/playwright_api/worker.rb +29 -5
  67. data/playwright.gemspec +3 -0
  68. metadata +68 -2
data/lib/playwright.rb CHANGED
@@ -10,12 +10,20 @@ require 'concurrent'
10
10
  require 'playwright/errors'
11
11
  require 'playwright/events'
12
12
  require 'playwright/event_emitter'
13
+ require 'playwright/javascript'
14
+ require 'playwright/utils'
13
15
 
14
16
  require 'playwright/channel'
15
17
  require 'playwright/channel_owner'
18
+ require 'playwright/input_files'
19
+ require 'playwright/input_type'
16
20
  require 'playwright/connection'
21
+ require 'playwright/select_option_values'
22
+ require 'playwright/timeout_settings'
17
23
  require 'playwright/transport'
24
+ require 'playwright/url_matcher'
18
25
  require 'playwright/version'
26
+ require 'playwright/wait_helper'
19
27
 
20
28
  require 'playwright/playwright_api'
21
29
  # load generated files
@@ -6,6 +6,10 @@ module Playwright
6
6
  channel.object
7
7
  end
8
8
 
9
+ def self.from_nullable(channel)
10
+ channel&.object
11
+ end
12
+
9
13
  # @param parent [Playwright::ChannelOwner|Playwright::Connection]
10
14
  # @param type [String]
11
15
  # @param guid [String]
@@ -35,16 +39,26 @@ module Playwright
35
39
 
36
40
  attr_reader :channel
37
41
 
38
- def dispose
42
+ # used only from Connection. Not intended for public use. So keep private.
43
+ private def dispose!
39
44
  # Clean up from parent and connection.
40
45
  @parent&.send(:delete_object_from_child, @guid)
41
46
  @connection.send(:delete_object_from_channel_owner, @guid)
42
47
 
43
48
  # Dispose all children.
44
- @objects.each_value(&:dispose)
49
+ @objects.each_value { |object| object.send(:dispose!) }
45
50
  @objects.clear
46
51
  end
47
52
 
53
+ # Suppress long long inspect log and avoid RSpec from hanging up...
54
+ def inspect
55
+ to_s
56
+ end
57
+
58
+ def to_s
59
+ "#<#{@guid}>"
60
+ end
61
+
48
62
  private
49
63
 
50
64
  def after_initialize
@@ -1,3 +1,12 @@
1
1
  module Playwright
2
- define_channel_owner :Android
2
+ define_channel_owner :Android do
3
+ 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
+ def after_initialize
6
+ @input = InputTypes::AndroidInput.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
+ logger: nil,
108
+ offline: nil,
109
+ permissions: nil,
110
+ proxy: nil,
111
+ recordHar: nil,
112
+ recordVideo: nil,
113
+ storageState: nil,
114
+ timezoneId: nil,
115
+ userAgent: nil,
116
+ videoSize: nil,
117
+ videosPath: 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
+ logger: logger,
135
+ offline: offline,
136
+ permissions: permissions,
137
+ proxy: proxy,
138
+ recordHar: recordHar,
139
+ recordVideo: recordVideo,
140
+ storageState: storageState,
141
+ timezoneId: timezoneId,
142
+ userAgent: userAgent,
143
+ videoSize: videoSize,
144
+ videosPath: videosPath,
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
@@ -1,9 +1,12 @@
1
1
  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
+ include Utils::Errors::SafeCloseError
5
+ include Utils::PrepareBrowserContextOptions
6
+
4
7
  def after_initialize
5
8
  @contexts = Set.new
6
- @channel.on('close', method(:handle_close))
9
+ @channel.on('close', method(:on_close))
7
10
  end
8
11
 
9
12
  def contexts
@@ -14,30 +17,29 @@ module Playwright
14
17
  @connected
15
18
  end
16
19
 
17
- def new_context(**options)
20
+ def new_context(**options, &block)
18
21
  params = options.dup
19
- # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
20
- if params[:viewport] == 0
21
- params.delete(:viewport)
22
- params[:noDefaultViewport] = true
23
- end
24
- if params[:extraHTTPHeaders]
25
- # TODO
26
- end
27
- if params[:storageState].is_a?(String)
28
- params[:storageState] = JSON.parse(File.read(params[:storageState]))
29
- end
22
+ prepare_browser_context_options(params)
30
23
 
31
24
  resp = @channel.send_message_to_server('newContext', params.compact)
32
25
  context = ChannelOwners::BrowserContext.from(resp)
33
26
  @contexts << context
34
27
  context.browser = self
35
28
  context.options = params
36
- context
29
+
30
+ if block
31
+ begin
32
+ block.call(context)
33
+ ensure
34
+ context.close
35
+ end
36
+ else
37
+ context
38
+ end
37
39
  end
38
40
 
39
- def new_page(*args)
40
- context = new_context(*args)
41
+ def new_page(**options)
42
+ context = new_context(**options)
41
43
  page = context.new_page
42
44
  page.owned_context = context
43
45
  context.owner_page = page
@@ -57,24 +59,15 @@ module Playwright
57
59
  @initializer['version']
58
60
  end
59
61
 
60
- private
61
-
62
- def handle_close(_ = {})
62
+ private def on_close(_ = {})
63
63
  @connected = false
64
64
  emit(Events::Browser::Disconnected)
65
65
  @closed_or_closing = false
66
66
  end
67
67
 
68
- # @param err [Exception]
69
- def safe_close_error?(err)
70
- return true if err.is_a?(Transport::AlreadyDisconnectedError)
71
-
72
- [
73
- 'Browser has been closed',
74
- 'Target page, context or browser has been closed',
75
- ].any? do |closed_message|
76
- err.message.end_with?(closed_message)
77
- end
68
+ # called from BrowserContext#on_close with send(:remove_context), so keep private.
69
+ private def remove_context(context)
70
+ @contexts.delete(context)
78
71
  end
79
72
  end
80
73
  end
@@ -1,13 +1,56 @@
1
1
  module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
3
3
  define_channel_owner :BrowserContext do
4
+ include Utils::Errors::SafeCloseError
4
5
  attr_writer :browser, :owner_page, :options
5
6
 
7
+ def after_initialize
8
+ @pages = Set.new
9
+
10
+ @channel.once('close', ->(_) { on_close })
11
+ @channel.on('page', ->(hash) { on_page(ChannelOwners::Page.from(hash['page']) )})
12
+ end
13
+
14
+ private def on_page(page)
15
+ page.send(:update_browser_context, self)
16
+ @pages << page
17
+ emit(Events::BrowserContext::Page, page)
18
+ end
19
+
20
+ def pages
21
+ @pages.to_a
22
+ end
23
+
6
24
  # @returns [Playwright::Page]
7
25
  def new_page
8
26
  raise 'Please use browser.new_context' if @owner_page
9
27
  resp = @channel.send_message_to_server('newPage')
10
28
  ChannelOwners::Page.from(resp)
11
29
  end
30
+
31
+ private def on_close
32
+ @closed_or_closing = true
33
+ @browser&.send(:remove_context, self)
34
+ emit(Events::BrowserContext::Close)
35
+ end
36
+
37
+ def close
38
+ return if @closed_or_closing
39
+ @closed_or_closing = true
40
+ @channel.send_message_to_server('close')
41
+ nil
42
+ rescue => err
43
+ raise unless safe_close_error?(err)
44
+ end
45
+
46
+ # called from Page#on_close with send(:remove_page, page), so keep private
47
+ private def remove_page(page)
48
+ @pages.delete(page)
49
+ end
50
+
51
+ # called from Page with send(:_timeout_settings), so keep private.
52
+ private def _timeout_settings
53
+ @timeout_settings
54
+ end
12
55
  end
13
56
  end
@@ -0,0 +1,21 @@
1
+ module Playwright
2
+ define_channel_owner :ConsoleMessage do
3
+ def type
4
+ @initializer['type']
5
+ end
6
+
7
+ def text
8
+ @initializer['text']
9
+ end
10
+
11
+ def args
12
+ @initializer['args']&.map do |arg|
13
+ ChannelOwner.from(arg)
14
+ end
15
+ end
16
+
17
+ def location
18
+ @initialize['location']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,314 @@
1
+ require_relative './js_handle'
2
+
3
+ module Playwright
4
+ module ChannelOwners
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
+
91
+ def click(
92
+ button: nil,
93
+ clickCount: nil,
94
+ delay: nil,
95
+ force: nil,
96
+ modifiers: nil,
97
+ noWaitAfter: nil,
98
+ position: nil,
99
+ timeout: nil)
100
+
101
+ params = {
102
+ button: button,
103
+ clickCount: clickCount,
104
+ delay: delay,
105
+ force: force,
106
+ modifiers: modifiers,
107
+ noWaitAfter: noWaitAfter,
108
+ position: position,
109
+ timeout: timeout,
110
+ }.compact
111
+ @channel.send_message_to_server('click', params)
112
+
113
+ nil
114
+ end
115
+
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(values, noWaitAfter: nil, timeout: nil)
140
+ base_params = SelectOptionValues.new(values).as_params
141
+ params = base_params + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
142
+ @channel.send_message_to_server('selectOption', params)
143
+
144
+ nil
145
+ end
146
+
147
+ def tap_point(
148
+ force: nil,
149
+ modifiers: nil,
150
+ noWaitAfter: nil,
151
+ position: nil,
152
+ timeout: nil)
153
+
154
+ params = {
155
+ force: force,
156
+ modifiers: modifiers,
157
+ noWaitAfter: noWaitAfter,
158
+ position: position,
159
+ timeout: timeout,
160
+ }.compact
161
+ @channel.send_message_to_server('tap', params)
162
+
163
+ nil
164
+ end
165
+
166
+ def fill(value, noWaitAfter: nil, timeout: nil)
167
+ params = {
168
+ value: value,
169
+ noWaitAfter: noWaitAfter,
170
+ timeout: timeout,
171
+ }
172
+ @channel.send_message_to_server('fill', params)
173
+
174
+ nil
175
+ end
176
+
177
+ def select_text(timeout: nil)
178
+ params = { timeout: timeout }.compact
179
+ @channel.send_message_to_server('selectText', params)
180
+
181
+ nil
182
+ end
183
+
184
+ def set_input_files(files, noWaitAfter: nil, timeout: nil)
185
+ base_params = InputFiles.new(values).as_params
186
+ params = base_params + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
187
+ @channel.send_message_to_server('setInputFiles', params)
188
+
189
+ nil
190
+ end
191
+
192
+ def focus
193
+ @channel.send_message_to_server('focus')
194
+
195
+ nil
196
+ end
197
+
198
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
199
+ params = {
200
+ text: text,
201
+ delay: delay,
202
+ noWaitAfter: noWaitAfter,
203
+ timeout: timeout,
204
+ }.compact
205
+ @channel.send_message_to_server('type', params)
206
+
207
+ nil
208
+ end
209
+
210
+ def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
211
+ params = {
212
+ key: key,
213
+ delay: delay,
214
+ noWaitAfter: noWaitAfter,
215
+ timeout: timeout,
216
+ }.compact
217
+ @channel.send_message_to_server('press', params)
218
+
219
+ nil
220
+ end
221
+
222
+ def check(force: nil, noWaitAfter: nil, timeout: nil)
223
+ params = {
224
+ force: force,
225
+ noWaitAfter: noWaitAfter,
226
+ timeout: timeout,
227
+ }.compact
228
+ @channel.send_message_to_server('check', params)
229
+
230
+ nil
231
+ end
232
+
233
+ def uncheck(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('uncheck', params)
240
+
241
+ nil
242
+ end
243
+
244
+ def bounding_box
245
+ @channel.send_message_to_server('boundingBox')
246
+ end
247
+
248
+ def screenshot(
249
+ omitBackground: nil,
250
+ path: nil,
251
+ quality: nil,
252
+ timeout: nil,
253
+ type: nil)
254
+
255
+ params = {
256
+ omitBackground: omitBackground,
257
+ path: path,
258
+ quality: quality,
259
+ timeout: timeout,
260
+ type: type,
261
+ }.compact
262
+ encoded_binary = @channel.send_message_to_server('screenshot', params)
263
+ decoded_binary = Base64.strict_decode64(encoded_binary)
264
+ if path
265
+ File.open(path, 'wb') do |f|
266
+ f.write(decoded_binary)
267
+ end
268
+ end
269
+ decoded_binary
270
+ end
271
+
272
+ def query_selector(selector)
273
+ resp = @channel.send_message_to_server('querySelector', selector: selector)
274
+ ChannelOwners::ElementHandle.from_nullable(resp)
275
+ end
276
+
277
+ def query_selector_all(selector)
278
+ @channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
279
+ ChannelOwners::ElementHandle.from(el)
280
+ end
281
+ end
282
+
283
+ def eval_on_selector(selector, pageFunction, arg: nil)
284
+ if JavaScript.function?(pageFunction)
285
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
286
+ else
287
+ JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
288
+ end
289
+ end
290
+
291
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
292
+ if JavaScript.function?(pageFunction)
293
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
294
+ else
295
+ JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
296
+ end
297
+ end
298
+
299
+ def wait_for_element_state(state, timeout: nil)
300
+ params = { state: state, timeout: timeout }.compact
301
+ @channel.send_message_to_server('waitForElementState', params)
302
+
303
+ nil
304
+ end
305
+
306
+ def wait_for_selector(selector, state: nil, timeout: nil)
307
+ params = { selector: selector, state: state, timeout: timeout }.compact
308
+ resp = @channel.send_message_to_server('waitForSelector', params)
309
+
310
+ ChannelOwners::ElementHandle.from_nullable(resp)
311
+ end
312
+ end
313
+ end
314
+ end