playwright-ruby-client 0.0.6 → 0.0.7

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -2
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +2 -0
  5. data/lib/playwright/channel_owner.rb +3 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +93 -0
  8. data/lib/playwright/channel_owners/browser.rb +13 -13
  9. data/lib/playwright/channel_owners/element_handle.rb +232 -1
  10. data/lib/playwright/channel_owners/frame.rb +41 -3
  11. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  12. data/lib/playwright/channel_owners/page.rb +48 -10
  13. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  14. data/lib/playwright/connection.rb +9 -6
  15. data/lib/playwright/errors.rb +1 -1
  16. data/lib/playwright/input_files.rb +42 -0
  17. data/lib/playwright/input_types/keyboard.rb +1 -1
  18. data/lib/playwright/javascript/value_serializer.rb +11 -11
  19. data/lib/playwright/playwright_api.rb +8 -0
  20. data/lib/playwright/select_option_values.rb +32 -0
  21. data/lib/playwright/utils.rb +18 -0
  22. data/lib/playwright/version.rb +1 -1
  23. data/lib/playwright_api/android.rb +33 -0
  24. data/lib/playwright_api/android_device.rb +48 -0
  25. data/lib/playwright_api/binding_call.rb +3 -3
  26. data/lib/playwright_api/browser.rb +7 -6
  27. data/lib/playwright_api/browser_context.rb +6 -6
  28. data/lib/playwright_api/browser_type.rb +4 -4
  29. data/lib/playwright_api/chromium_browser_context.rb +3 -3
  30. data/lib/playwright_api/console_message.rb +3 -8
  31. data/lib/playwright_api/dialog.rb +2 -2
  32. data/lib/playwright_api/element_handle.rb +40 -39
  33. data/lib/playwright_api/frame.rb +23 -27
  34. data/lib/playwright_api/js_handle.rb +15 -9
  35. data/lib/playwright_api/keyboard.rb +6 -6
  36. data/lib/playwright_api/page.rb +35 -54
  37. data/lib/playwright_api/playwright.rb +7 -7
  38. data/lib/playwright_api/request.rb +3 -3
  39. data/lib/playwright_api/response.rb +3 -3
  40. data/lib/playwright_api/selectors.rb +3 -3
  41. data/playwright.gemspec +1 -0
  42. metadata +22 -2
data/lib/playwright.rb CHANGED
@@ -15,8 +15,10 @@ require 'playwright/utils'
15
15
 
16
16
  require 'playwright/channel'
17
17
  require 'playwright/channel_owner'
18
+ require 'playwright/input_files'
18
19
  require 'playwright/input_type'
19
20
  require 'playwright/connection'
21
+ require 'playwright/select_option_values'
20
22
  require 'playwright/timeout_settings'
21
23
  require 'playwright/transport'
22
24
  require 'playwright/url_matcher'
@@ -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
 
@@ -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,93 @@
1
+ module Playwright
2
+ define_channel_owner :AndroidDevice do
3
+ include Utils::PrepareBrowserContextOptions
4
+
5
+ def serial
6
+ @initializer['serial']
7
+ end
8
+
9
+ def model
10
+ @initializer['model']
11
+ end
12
+
13
+ def shell(command)
14
+ resp = @channel.send_message_to_server('shell', command: command)
15
+ Base64.strict_decode64(resp)
16
+ end
17
+
18
+ def close
19
+ @channel.send_message_to_server('close')
20
+ emit(Events::AndroidDevice::Close)
21
+ end
22
+
23
+ def launch_browser(
24
+ pkg: nil,
25
+ acceptDownloads: nil,
26
+ bypassCSP: nil,
27
+ colorScheme: nil,
28
+ deviceScaleFactor: nil,
29
+ extraHTTPHeaders: nil,
30
+ geolocation: nil,
31
+ hasTouch: nil,
32
+ httpCredentials: nil,
33
+ ignoreHTTPSErrors: nil,
34
+ isMobile: nil,
35
+ javaScriptEnabled: nil,
36
+ locale: nil,
37
+ logger: nil,
38
+ offline: nil,
39
+ permissions: nil,
40
+ proxy: nil,
41
+ recordHar: nil,
42
+ recordVideo: nil,
43
+ storageState: nil,
44
+ timezoneId: nil,
45
+ userAgent: nil,
46
+ videoSize: nil,
47
+ videosPath: nil,
48
+ viewport: nil,
49
+ &block)
50
+ params = {
51
+ pkg: pkg,
52
+ acceptDownloads: acceptDownloads,
53
+ bypassCSP: bypassCSP,
54
+ colorScheme: colorScheme,
55
+ deviceScaleFactor: deviceScaleFactor,
56
+ extraHTTPHeaders: extraHTTPHeaders,
57
+ geolocation: geolocation,
58
+ hasTouch: hasTouch,
59
+ httpCredentials: httpCredentials,
60
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
61
+ isMobile: isMobile,
62
+ javaScriptEnabled: javaScriptEnabled,
63
+ locale: locale,
64
+ logger: logger,
65
+ offline: offline,
66
+ permissions: permissions,
67
+ proxy: proxy,
68
+ recordHar: recordHar,
69
+ recordVideo: recordVideo,
70
+ storageState: storageState,
71
+ timezoneId: timezoneId,
72
+ userAgent: userAgent,
73
+ videoSize: videoSize,
74
+ videosPath: videosPath,
75
+ viewport: viewport,
76
+ }.compact
77
+ prepare_browser_context_options(params)
78
+
79
+ resp = @channel.send_message_to_server('launchBrowser', params)
80
+ context = ChannelOwners::BrowserContext.from(resp)
81
+
82
+ if block
83
+ begin
84
+ block.call(context)
85
+ ensure
86
+ context.close
87
+ end
88
+ else
89
+ context
90
+ end
91
+ end
92
+ end
93
+ end
@@ -2,6 +2,7 @@ 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
7
  def after_initialize
7
8
  @contexts = Set.new
@@ -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)
@@ -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,89 @@ 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(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)
32
199
  params = {
33
200
  text: text,
34
201
  delay: delay,
@@ -52,6 +219,56 @@ module Playwright
52
219
  nil
53
220
  end
54
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
+
55
272
  def query_selector(selector)
56
273
  resp = @channel.send_message_to_server('querySelector', selector: selector)
57
274
  ChannelOwners::ElementHandle.from_nullable(resp)
@@ -78,6 +295,20 @@ module Playwright
78
295
  JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
79
296
  end
80
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
81
312
  end
82
313
  end
83
314
  end
@@ -62,7 +62,7 @@ module Playwright
62
62
  end
63
63
  end
64
64
 
65
- def wait_for_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
65
+ def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
66
66
  option_wait_until = waitUntil || 'load'
67
67
  option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
68
68
  time_start = Time.now
@@ -100,6 +100,7 @@ module Playwright
100
100
 
101
101
  def wait_for_load_state(state: nil, timeout: nil)
102
102
  option_state = state || 'load'
103
+ option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
103
104
  unless %w(load domcontentloaded networkidle).include?(option_state)
104
105
  raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
105
106
  end
@@ -107,7 +108,7 @@ module Playwright
107
108
  return
108
109
  end
109
110
 
110
- wait_helper = setup_navigation_wait_helper(timeout: timeout)
111
+ wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
111
112
 
112
113
  predicate = ->(state) { state == option_state }
113
114
  wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
@@ -202,13 +203,50 @@ module Playwright
202
203
  nil
203
204
  end
204
205
 
206
+ def dblclick(
207
+ selector,
208
+ button: nil,
209
+ delay: nil,
210
+ force: nil,
211
+ modifiers: nil,
212
+ noWaitAfter: nil,
213
+ position: nil,
214
+ timeout: nil)
215
+
216
+ params = {
217
+ selector: selector,
218
+ button: button,
219
+ delay: delay,
220
+ force: force,
221
+ modifiers: modifiers,
222
+ noWaitAfter: noWaitAfter,
223
+ position: position,
224
+ timeout: timeout,
225
+ }.compact
226
+ @channel.send_message_to_server('dblclick', params)
227
+
228
+ nil
229
+ end
230
+
231
+ def fill(selector, value, noWaitAfter: nil, timeout: nil)
232
+ params = {
233
+ selector: selector,
234
+ value: value,
235
+ noWaitAfter: noWaitAfter,
236
+ timeout: timeout,
237
+ }.compact
238
+ @channel.send_message_to_server('fill', params)
239
+
240
+ nil
241
+ end
242
+
205
243
  def focus(selector, timeout: nil)
206
244
  params = { selector: selector, timeout: timeout }.compact
207
245
  @channel.send_message_to_server('focus', params)
208
246
  nil
209
247
  end
210
248
 
211
- def type_text(
249
+ def type(
212
250
  selector,
213
251
  text,
214
252
  delay: nil,