playwright-ruby-client 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -2
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +7 -1
  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 +3 -2
  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 +13 -13
  12. data/lib/playwright/channel_owners/browser_context.rb +9 -1
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  15. data/lib/playwright/channel_owners/frame.rb +371 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +416 -19
  18. data/lib/playwright/channel_owners/request.rb +98 -0
  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 +37 -0
  28. data/lib/playwright/javascript/function.rb +37 -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/keyboard_impl.rb +36 -0
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +84 -29
  34. data/lib/playwright/select_option_values.rb +32 -0
  35. data/lib/playwright/timeout_settings.rb +2 -2
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/url_matcher.rb +19 -0
  38. data/lib/playwright/utils.rb +18 -0
  39. data/lib/playwright/version.rb +1 -1
  40. data/lib/playwright/wait_helper.rb +1 -1
  41. data/lib/playwright_api/accessibility.rb +46 -6
  42. data/lib/playwright_api/android.rb +37 -0
  43. data/lib/playwright_api/android_device.rb +82 -0
  44. data/lib/playwright_api/android_input.rb +25 -0
  45. data/lib/playwright_api/binding_call.rb +10 -6
  46. data/lib/playwright_api/browser.rb +85 -18
  47. data/lib/playwright_api/browser_context.rb +269 -37
  48. data/lib/playwright_api/browser_type.rb +60 -11
  49. data/lib/playwright_api/cdp_session.rb +23 -1
  50. data/lib/playwright_api/chromium_browser_context.rb +18 -6
  51. data/lib/playwright_api/console_message.rb +14 -15
  52. data/lib/playwright_api/dialog.rb +48 -2
  53. data/lib/playwright_api/download.rb +47 -10
  54. data/lib/playwright_api/element_handle.rb +269 -110
  55. data/lib/playwright_api/file_chooser.rb +23 -7
  56. data/lib/playwright_api/frame.rb +439 -154
  57. data/lib/playwright_api/js_handle.rb +69 -24
  58. data/lib/playwright_api/keyboard.rb +99 -9
  59. data/lib/playwright_api/mouse.rb +22 -0
  60. data/lib/playwright_api/page.rb +856 -229
  61. data/lib/playwright_api/playwright.rb +108 -20
  62. data/lib/playwright_api/request.rb +77 -29
  63. data/lib/playwright_api/response.rb +10 -13
  64. data/lib/playwright_api/route.rb +49 -0
  65. data/lib/playwright_api/selectors.rb +20 -8
  66. data/lib/playwright_api/video.rb +8 -0
  67. data/lib/playwright_api/web_socket.rb +0 -8
  68. data/lib/playwright_api/worker.rb +25 -13
  69. data/playwright.gemspec +1 -0
  70. metadata +33 -2
@@ -53,6 +53,70 @@ module Playwright
53
53
  ChannelOwners::Response.from_nullable(resp)
54
54
  end
55
55
 
56
+ private def setup_navigation_wait_helper(timeout:)
57
+ WaitHelper.new.tap do |helper|
58
+ helper.reject_on_event(@page, Events::Page::Close, AlreadyClosedError.new)
59
+ helper.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
60
+ helper.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new)
61
+ helper.reject_on_timeout(timeout, "Timeout #{timeout}ms exceeded.")
62
+ end
63
+ end
64
+
65
+ def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
66
+ option_wait_until = waitUntil || 'load'
67
+ option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
68
+ time_start = Time.now
69
+
70
+ wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
71
+
72
+ predicate =
73
+ if url
74
+ matcher = UrlMatcher.new(url)
75
+ ->(event) { event['error'] || matcher.match?(event['url']) }
76
+ else
77
+ ->(_) { true }
78
+ end
79
+
80
+ wait_helper.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
81
+
82
+ block&.call
83
+
84
+ event = wait_helper.promise.value!
85
+ if event['error']
86
+ raise event['error']
87
+ end
88
+
89
+ unless @load_states.include?(option_wait_until)
90
+ elapsed_time = Time.now - time_start
91
+ if elapsed_time < option_timeout
92
+ wait_for_load_state(state: option_wait_until, timeout: option_timeout - elapsed_time)
93
+ end
94
+ end
95
+
96
+ request_json = event.dig('newDocument', 'request')
97
+ request = ChannelOwners::Request.from_nullable(request_json)
98
+ request&.response
99
+ end
100
+
101
+ def wait_for_load_state(state: nil, timeout: nil)
102
+ option_state = state || 'load'
103
+ option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
104
+ unless %w(load domcontentloaded networkidle).include?(option_state)
105
+ raise ArgumentError.new('state: expected one of (load|domcontentloaded|networkidle)')
106
+ end
107
+ if @load_states.include?(option_state)
108
+ return
109
+ end
110
+
111
+ wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
112
+
113
+ predicate = ->(state) { state == option_state }
114
+ wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
115
+ wait_helper.promise.value!
116
+
117
+ nil
118
+ end
119
+
56
120
  def evaluate(pageFunction, arg: nil)
57
121
  if JavaScript.function?(pageFunction)
58
122
  JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
@@ -69,6 +133,82 @@ module Playwright
69
133
  end
70
134
  end
71
135
 
136
+ def query_selector(selector)
137
+ resp = @channel.send_message_to_server('querySelector', selector: selector)
138
+ ChannelOwners::ElementHandle.from_nullable(resp)
139
+ end
140
+
141
+ def query_selector_all(selector)
142
+ @channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
143
+ ChannelOwners::ElementHandle.from(el)
144
+ end
145
+ end
146
+
147
+ def wait_for_selector(selector, state: nil, timeout: nil)
148
+ params = { selector: selector, state: state, timeout: timeout }.compact
149
+ resp = @channel.send_message_to_server('waitForSelector', params)
150
+
151
+ ChannelOwners::ElementHandle.from_nullable(resp)
152
+ end
153
+
154
+ def checked?(selector, timeout: nil)
155
+ params = { selector: selector, timeout: timeout }.compact
156
+ @channel.send_message_to_server('isChecked', params)
157
+ end
158
+
159
+ def disabled?(selector, timeout: nil)
160
+ params = { selector: selector, timeout: timeout }.compact
161
+ @channel.send_message_to_server('isDisabled', params)
162
+ end
163
+
164
+ def editable?(selector, timeout: nil)
165
+ params = { selector: selector, timeout: timeout }.compact
166
+ @channel.send_message_to_server('isEditable', params)
167
+ end
168
+
169
+ def enabled?(selector, timeout: nil)
170
+ params = { selector: selector, timeout: timeout }.compact
171
+ @channel.send_message_to_server('isEnabled', params)
172
+ end
173
+
174
+ def hidden?(selector, timeout: nil)
175
+ params = { selector: selector, timeout: timeout }.compact
176
+ @channel.send_message_to_server('isHidden', params)
177
+ end
178
+
179
+ def visible?(selector, timeout: nil)
180
+ params = { selector: selector, timeout: timeout }.compact
181
+ @channel.send_message_to_server('isVisible', params)
182
+ end
183
+
184
+ def dispatch_event(selector, type, eventInit: nil, timeout: nil)
185
+ params = {
186
+ selector: selector,
187
+ type: type,
188
+ eventInit: JavaScript::ValueSerializer.new(eventInit).serialize,
189
+ timeout: timeout,
190
+ }.compact
191
+ @channel.send_message_to_server('dispatchEvent', params)
192
+
193
+ nil
194
+ end
195
+
196
+ def eval_on_selector(selector, pageFunction, arg: nil)
197
+ if JavaScript.function?(pageFunction)
198
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
199
+ else
200
+ JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
201
+ end
202
+ end
203
+
204
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
205
+ if JavaScript.function?(pageFunction)
206
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
207
+ else
208
+ JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
209
+ end
210
+ end
211
+
72
212
  def content
73
213
  @channel.send_message_to_server('content')
74
214
  end
@@ -85,18 +225,201 @@ module Playwright
85
225
  nil
86
226
  end
87
227
 
228
+ def name
229
+ @name || ''
230
+ end
231
+
232
+ def url
233
+ @url || ''
234
+ end
235
+
236
+ def child_frames
237
+ @child_frames.to_a
238
+ end
239
+
240
+ def detached?
241
+ @detached
242
+ end
243
+
244
+ def add_script_tag(content: nil, path: nil, type: nil, url: nil)
245
+ params = {
246
+ content: content,
247
+ type: type,
248
+ url: url,
249
+ }.compact
250
+ if path
251
+ params[:content] = "#{File.read(path)}\n//# sourceURL=#{path}"
252
+ end
253
+ resp = @channel.send_message_to_server('addScriptTag', params)
254
+ ChannelOwners::ElementHandle.from(resp)
255
+ end
256
+
257
+ def add_style_tag(content: nil, path: nil, url: nil)
258
+ params = {
259
+ content: content,
260
+ url: url,
261
+ }.compact
262
+ if path
263
+ params[:content] = "#{File.read(path)}\n/*# sourceURL=#{path}*/"
264
+ end
265
+ resp = @channel.send_message_to_server('addStyleTag', params)
266
+ ChannelOwners::ElementHandle.from(resp)
267
+ end
268
+
269
+ def click(
270
+ selector,
271
+ button: nil,
272
+ clickCount: nil,
273
+ delay: nil,
274
+ force: nil,
275
+ modifiers: nil,
276
+ noWaitAfter: nil,
277
+ position: nil,
278
+ timeout: nil)
279
+
280
+ params = {
281
+ selector: selector,
282
+ button: button,
283
+ clickCount: clickCount,
284
+ delay: delay,
285
+ force: force,
286
+ modifiers: modifiers,
287
+ noWaitAfter: noWaitAfter,
288
+ position: position,
289
+ timeout: timeout,
290
+ }.compact
291
+ @channel.send_message_to_server('click', params)
292
+
293
+ nil
294
+ end
295
+
296
+ def dblclick(
297
+ selector,
298
+ button: nil,
299
+ delay: nil,
300
+ force: nil,
301
+ modifiers: nil,
302
+ noWaitAfter: nil,
303
+ position: nil,
304
+ timeout: nil)
305
+
306
+ params = {
307
+ selector: selector,
308
+ button: button,
309
+ delay: delay,
310
+ force: force,
311
+ modifiers: modifiers,
312
+ noWaitAfter: noWaitAfter,
313
+ position: position,
314
+ timeout: timeout,
315
+ }.compact
316
+ @channel.send_message_to_server('dblclick', params)
317
+
318
+ nil
319
+ end
320
+
321
+ def tap_point(
322
+ selector,
323
+ force: nil,
324
+ modifiers: nil,
325
+ noWaitAfter: nil,
326
+ position: nil,
327
+ timeout: nil)
328
+ params = {
329
+ selector: selector,
330
+ force: force,
331
+ modifiers: modifiers,
332
+ noWaitAfter: noWaitAfter,
333
+ position: position,
334
+ timeout: timeout,
335
+ }.compact
336
+ @channel.send_message_to_server('tap', params)
337
+
338
+ nil
339
+ end
340
+
341
+ def fill(selector, value, noWaitAfter: nil, timeout: nil)
342
+ params = {
343
+ selector: selector,
344
+ value: value,
345
+ noWaitAfter: noWaitAfter,
346
+ timeout: timeout,
347
+ }.compact
348
+ @channel.send_message_to_server('fill', params)
349
+
350
+ nil
351
+ end
352
+
88
353
  def focus(selector, timeout: nil)
89
354
  params = { selector: selector, timeout: timeout }.compact
90
355
  @channel.send_message_to_server('focus', params)
91
356
  nil
92
357
  end
93
358
 
94
- def type_text(
95
- selector,
96
- text,
97
- delay: nil,
98
- noWaitAfter: nil,
99
- timeout: nil)
359
+ def text_content(selector, timeout: nil)
360
+ params = { selector: selector, timeout: timeout }.compact
361
+ @channel.send_message_to_server('textContent', params)
362
+ end
363
+
364
+ def inner_text(selector, timeout: nil)
365
+ params = { selector: selector, timeout: timeout }.compact
366
+ @channel.send_message_to_server('innerText', params)
367
+ end
368
+
369
+ def inner_html(selector, timeout: nil)
370
+ params = { selector: selector, timeout: timeout }.compact
371
+ @channel.send_message_to_server('innerHTML', params)
372
+ end
373
+
374
+ def get_attribute(selector, name, timeout: nil)
375
+ params = {
376
+ selector: selector,
377
+ name: name,
378
+ timeout: timeout,
379
+ }.compact
380
+ @channel.send_message_to_server('getAttribute', params)
381
+ end
382
+
383
+ def hover(
384
+ selector,
385
+ force: nil,
386
+ modifiers: nil,
387
+ position: nil,
388
+ timeout: nil)
389
+ params = {
390
+ selector: selector,
391
+ force: force,
392
+ modifiers: modifiers,
393
+ position: position,
394
+ timeout: timeout,
395
+ }.compact
396
+ @channel.send_message_to_server('hover', params)
397
+
398
+ nil
399
+ end
400
+
401
+ def select_option(selector, values, noWaitAfter: nil, timeout: nil)
402
+ base_params = SelectOptionValues.new(values).as_params
403
+ params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
404
+ @channel.send_message_to_server('selectOption', params)
405
+
406
+ nil
407
+ end
408
+
409
+ def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
410
+ file_payloads = InputFiles.new(files).as_params
411
+ params = { files: file_payloads, selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
412
+ @channel.send_message_to_server('setInputFiles', params)
413
+
414
+ nil
415
+ end
416
+
417
+ def type(
418
+ selector,
419
+ text,
420
+ delay: nil,
421
+ noWaitAfter: nil,
422
+ timeout: nil)
100
423
 
101
424
  params = {
102
425
  selector: selector,
@@ -105,16 +428,17 @@ module Playwright
105
428
  noWaitAfter: noWaitAfter,
106
429
  timeout: timeout,
107
430
  }.compact
108
-
109
431
  @channel.send_message_to_server('type', params)
432
+
433
+ nil
110
434
  end
111
435
 
112
436
  def press(
113
- selector,
114
- key,
115
- delay: nil,
116
- noWaitAfter: nil,
117
- timeout: nil)
437
+ selector,
438
+ key,
439
+ delay: nil,
440
+ noWaitAfter: nil,
441
+ timeout: nil)
118
442
 
119
443
  params = {
120
444
  selector: selector,
@@ -123,20 +447,48 @@ module Playwright
123
447
  noWaitAfter: noWaitAfter,
124
448
  timeout: timeout,
125
449
  }.compact
126
-
127
450
  @channel.send_message_to_server('press', params)
451
+
452
+ nil
128
453
  end
129
454
 
130
- def name
131
- @name || ''
455
+ def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
456
+ params = {
457
+ selector: selector,
458
+ force: force,
459
+ noWaitAfter: noWaitAfter,
460
+ timeout: timeout,
461
+ }.compact
462
+ @channel.send_message_to_server('check', params)
463
+
464
+ nil
132
465
  end
133
466
 
134
- def url
135
- @url || ''
467
+ def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
468
+ params = {
469
+ selector: selector,
470
+ force: force,
471
+ noWaitAfter: noWaitAfter,
472
+ timeout: timeout,
473
+ }.compact
474
+ @channel.send_message_to_server('uncheck', params)
475
+
476
+ nil
136
477
  end
137
478
 
138
- def child_frames
139
- @child_frames.to_a
479
+ def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
480
+ if polling.is_a?(String) && polling != 'raf'
481
+ raise ArgumentError.new("Unknown polling option: #{polling}")
482
+ end
483
+
484
+ function_or_expression =
485
+ if JavaScript.function?(pageFunction)
486
+ JavaScript::Function.new(pageFunction, arg)
487
+ else
488
+ JavaScript::Expression.new(pageFunction)
489
+ end
490
+
491
+ function_or_expression.wait_for_function(@channel, polling: polling, timeout: timeout)
140
492
  end
141
493
 
142
494
  def title
@@ -1,4 +1,55 @@
1
1
  module Playwright
2
2
  define_channel_owner :JSHandle do
3
+ def after_initialize
4
+ @preview = @initializer['preview']
5
+ @channel.on('previewUpdated', method(:on_preview_updated))
6
+ end
7
+
8
+ def to_s
9
+ @preview
10
+ end
11
+
12
+ private def on_preview_updated(preview)
13
+ @preview = preview
14
+ end
15
+
16
+ def evaluate(pageFunction, arg: nil)
17
+ if JavaScript.function?(pageFunction)
18
+ JavaScript::Function.new(pageFunction, arg).evaluate(@channel)
19
+ else
20
+ JavaScript::Expression.new(pageFunction).evaluate(@channel)
21
+ end
22
+ end
23
+
24
+ def evaluate_handle(pageFunction, arg: nil)
25
+ if JavaScript.function?(pageFunction)
26
+ JavaScript::Function.new(pageFunction, arg).evaluate_handle(@channel)
27
+ else
28
+ JavaScript::Expression.new(pageFunction).evaluate_handle(@channel)
29
+ end
30
+ end
31
+
32
+ def get_properties
33
+ resp = @channel.send_message_to_server('getPropertyList')
34
+ resp.map { |prop| [prop['name'], ChannelOwner.from(prop['value'])] }.to_h
35
+ end
36
+
37
+ def get_property(name)
38
+ resp = @channel.send_message_to_server('getProperty', name: name)
39
+ ChannelOwner.from(resp)
40
+ end
41
+
42
+ def as_element
43
+ nil
44
+ end
45
+
46
+ def dispose
47
+ @channel.send_message_to_server('dispose')
48
+ end
49
+
50
+ def json_value
51
+ value = @channel.send_message_to_server('jsonValue')
52
+ JavaScript::ValueParser.new(value).parse
53
+ end
3
54
  end
4
55
  end