playwright-ruby-client 0.3.0 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -5
  3. data/docs/api_coverage.md +14 -25
  4. data/lib/playwright.rb +47 -9
  5. data/lib/playwright/channel.rb +19 -9
  6. data/lib/playwright/channel_owners/artifact.rb +30 -0
  7. data/lib/playwright/channel_owners/browser.rb +21 -0
  8. data/lib/playwright/channel_owners/browser_context.rb +5 -0
  9. data/lib/playwright/channel_owners/browser_type.rb +28 -0
  10. data/lib/playwright/channel_owners/element_handle.rb +7 -7
  11. data/lib/playwright/channel_owners/frame.rb +26 -5
  12. data/lib/playwright/channel_owners/js_handle.rb +2 -2
  13. data/lib/playwright/channel_owners/page.rb +78 -15
  14. data/lib/playwright/channel_owners/playwright.rb +22 -29
  15. data/lib/playwright/channel_owners/stream.rb +15 -0
  16. data/lib/playwright/connection.rb +11 -32
  17. data/lib/playwright/download.rb +27 -0
  18. data/lib/playwright/errors.rb +6 -0
  19. data/lib/playwright/events.rb +2 -5
  20. data/lib/playwright/keyboard_impl.rb +1 -1
  21. data/lib/playwright/mouse_impl.rb +41 -0
  22. data/lib/playwright/playwright_api.rb +5 -3
  23. data/lib/playwright/route_handler_entry.rb +1 -9
  24. data/lib/playwright/select_option_values.rb +31 -22
  25. data/lib/playwright/transport.rb +29 -7
  26. data/lib/playwright/url_matcher.rb +1 -1
  27. data/lib/playwright/utils.rb +8 -0
  28. data/lib/playwright/version.rb +1 -1
  29. data/lib/playwright/video.rb +51 -0
  30. data/lib/playwright/wait_helper.rb +2 -2
  31. data/lib/playwright_api/accessibility.rb +39 -1
  32. data/lib/playwright_api/android.rb +10 -10
  33. data/lib/playwright_api/android_device.rb +10 -9
  34. data/lib/playwright_api/browser.rb +83 -8
  35. data/lib/playwright_api/browser_context.rb +157 -9
  36. data/lib/playwright_api/browser_type.rb +35 -4
  37. data/lib/playwright_api/console_message.rb +6 -6
  38. data/lib/playwright_api/dialog.rb +28 -8
  39. data/lib/playwright_api/element_handle.rb +111 -37
  40. data/lib/playwright_api/file_chooser.rb +5 -0
  41. data/lib/playwright_api/frame.rb +228 -37
  42. data/lib/playwright_api/js_handle.rb +26 -3
  43. data/lib/playwright_api/keyboard.rb +48 -1
  44. data/lib/playwright_api/mouse.rb +26 -5
  45. data/lib/playwright_api/page.rb +454 -46
  46. data/lib/playwright_api/playwright.rb +26 -9
  47. data/lib/playwright_api/request.rb +34 -6
  48. data/lib/playwright_api/response.rb +6 -6
  49. data/lib/playwright_api/route.rb +30 -6
  50. data/lib/playwright_api/selectors.rb +32 -6
  51. data/lib/playwright_api/touchscreen.rb +1 -1
  52. data/lib/playwright_api/worker.rb +25 -1
  53. data/playwright.gemspec +4 -2
  54. metadata +37 -14
  55. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  56. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  57. data/lib/playwright/channel_owners/download.rb +0 -27
  58. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  59. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  60. data/lib/playwright_api/binding_call.rb +0 -32
  61. data/lib/playwright_api/chromium_browser_context.rb +0 -59
  62. data/lib/playwright_api/download.rb +0 -95
  63. data/lib/playwright_api/video.rb +0 -24
@@ -98,6 +98,15 @@ module Playwright
98
98
  request&.response
99
99
  end
100
100
 
101
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
102
+ matcher = UrlMatcher.new(url)
103
+ if matcher.match?(@url)
104
+ wait_for_load_state(state: waitUntil, timeout: timeout)
105
+ else
106
+ expect_navigation(timeout: timeout, url: url, waitUntil: waitUntil)
107
+ end
108
+ end
109
+
101
110
  def wait_for_load_state(state: nil, timeout: nil)
102
111
  option_state = state || 'load'
103
112
  option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
@@ -412,10 +421,8 @@ module Playwright
412
421
  value: value,
413
422
  label: label,
414
423
  ).as_params
415
- params = base_params + { selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact
424
+ params = base_params.merge({ selector: selector, noWaitAfter: noWaitAfter, timeout: timeout }.compact)
416
425
  @channel.send_message_to_server('selectOption', params)
417
-
418
- nil
419
426
  end
420
427
 
421
428
  def set_input_files(selector, files, noWaitAfter: nil, timeout: nil)
@@ -464,11 +471,18 @@ module Playwright
464
471
  nil
465
472
  end
466
473
 
467
- def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
474
+ def check(
475
+ selector,
476
+ force: nil,
477
+ noWaitAfter: nil,
478
+ position: nil,
479
+ timeout: nil)
480
+
468
481
  params = {
469
482
  selector: selector,
470
483
  force: force,
471
484
  noWaitAfter: noWaitAfter,
485
+ position: position,
472
486
  timeout: timeout,
473
487
  }.compact
474
488
  @channel.send_message_to_server('check', params)
@@ -476,11 +490,18 @@ module Playwright
476
490
  nil
477
491
  end
478
492
 
479
- def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
493
+ def uncheck(
494
+ selector,
495
+ force: nil,
496
+ noWaitAfter: nil,
497
+ position: nil,
498
+ timeout: nil)
499
+
480
500
  params = {
481
501
  selector: selector,
482
502
  force: force,
483
503
  noWaitAfter: noWaitAfter,
504
+ position: position,
484
505
  timeout: timeout,
485
506
  }.compact
486
507
  @channel.send_message_to_server('uncheck', params)
@@ -9,8 +9,8 @@ module Playwright
9
9
  @preview
10
10
  end
11
11
 
12
- private def on_preview_updated(preview)
13
- @preview = preview
12
+ private def on_preview_updated(params)
13
+ @preview = params['preview']
14
14
  end
15
15
 
16
16
  def evaluate(pageFunction, arg: nil)
@@ -28,6 +28,7 @@ module Playwright
28
28
  @main_frame.send(:update_page_from_page, self)
29
29
  @frames = Set.new
30
30
  @frames << @main_frame
31
+ @opener = ChannelOwners::Page.from_nullable(@initializer['opener'])
31
32
 
32
33
  @channel.once('close', ->(_) { on_close })
33
34
  @channel.on('console', ->(params) {
@@ -37,9 +38,7 @@ module Playwright
37
38
  @channel.on('crash', ->(_) { emit(Events::Page::Crash) })
38
39
  @channel.on('dialog', method(:on_dialog))
39
40
  @channel.on('domcontentloaded', ->(_) { emit(Events::Page::DOMContentLoaded) })
40
- @channel.on('download', ->(params) {
41
- emit(Events::Page::Download, ChannelOwners::Download.from(params['download']))
42
- })
41
+ @channel.on('download', method(:on_download))
43
42
  @channel.on('fileChooser', ->(params) {
44
43
  chooser = FileChooserImpl.new(
45
44
  page: self,
@@ -57,9 +56,6 @@ module Playwright
57
56
  @channel.on('pageError', ->(params) {
58
57
  emit(Events::Page::PageError, Error.parse(params['error']['error']))
59
58
  })
60
- @channel.on('popup', ->(params) {
61
- emit(Events::Page::Popup, ChannelOwners::Page.from(params['page']))
62
- })
63
59
  @channel.on('request', ->(params) {
64
60
  emit(Events::Page::Request, ChannelOwners::Request.from(params['request']))
65
61
  })
@@ -82,9 +78,7 @@ module Playwright
82
78
  @channel.on('route', ->(params) {
83
79
  on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
84
80
  })
85
- @channel.on('video', ->(params) {
86
- video.send(:update_relative_path, params['relativePath'])
87
- })
81
+ @channel.on('video', method(:on_video))
88
82
  @channel.on('webSocket', ->(params) {
89
83
  emit(Events::Page::WebSocket, ChannelOwners::WebSocket.from(params['webSocket']))
90
84
  })
@@ -149,6 +143,20 @@ module Playwright
149
143
  end
150
144
  end
151
145
 
146
+ private def on_download(params)
147
+ download = Download.new(
148
+ url: params['url'],
149
+ suggested_filename: params['suggestedFilename'],
150
+ artifact: ChannelOwners::Artifact.from(params['artifact']),
151
+ )
152
+ emit(Events::Page::Download, download)
153
+ end
154
+
155
+ private def on_video(params)
156
+ artifact = ChannelOwners::Artifact.from(params['artifact'])
157
+ video.send(:set_artifact, artifact)
158
+ end
159
+
152
160
  # @override
153
161
  def on(event, callback)
154
162
  if event == Events::Page::FileChooser && listener_count(event) == 0
@@ -178,8 +186,17 @@ module Playwright
178
186
  end
179
187
 
180
188
  def opener
181
- resp = @channel.send_message_to_server('opener')
182
- ChannelOwners::Page.from(resp)
189
+ if @opener&.closed?
190
+ nil
191
+ else
192
+ @opener
193
+ end
194
+ end
195
+
196
+ private def emit_popup_event_from_browser_context
197
+ if @opener && !@opener.closed?
198
+ @opener.emit(Events::Page::Popup, self)
199
+ end
183
200
  end
184
201
 
185
202
  def frame(name: nil, url: nil)
@@ -326,6 +343,10 @@ module Playwright
326
343
  @main_frame.wait_for_load_state(state: state, timeout: timeout)
327
344
  end
328
345
 
346
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
347
+ @main_frame.wait_for_url(url, timeout: timeout, waitUntil: waitUntil)
348
+ end
349
+
329
350
  def go_back(timeout: nil, waitUntil: nil)
330
351
  params = { timeout: timeout, waitUntil: waitUntil }.compact
331
352
  resp = @channel.send_message_to_server('goBack', params)
@@ -578,12 +599,24 @@ module Playwright
578
599
  @main_frame.press(selector, key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
579
600
  end
580
601
 
581
- def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
582
- @main_frame.check(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
602
+ def check(
603
+ selector,
604
+ force: nil,
605
+ noWaitAfter: nil,
606
+ position: nil,
607
+ timeout: nil)
608
+
609
+ @main_frame.check(selector, force: force, noWaitAfter: noWaitAfter, position: position, timeout: timeout)
583
610
  end
584
611
 
585
- def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
586
- @main_frame.uncheck(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
612
+ def uncheck(
613
+ selector,
614
+ force: nil,
615
+ noWaitAfter: nil,
616
+ position: nil,
617
+ timeout: nil)
618
+
619
+ @main_frame.uncheck(selector, force: force, noWaitAfter: noWaitAfter, position: position, timeout: timeout)
587
620
  end
588
621
 
589
622
  def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
@@ -629,6 +662,36 @@ module Playwright
629
662
  decoded_binary
630
663
  end
631
664
 
665
+ def video
666
+ return nil unless @browser_context.send(:has_record_video_option?)
667
+ @video ||= Video.new(self)
668
+ end
669
+
670
+ def start_js_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
671
+ params = {
672
+ resetOnNavigation: resetOnNavigation,
673
+ reportAnonymousScripts: reportAnonymousScripts,
674
+ }.compact
675
+
676
+ @channel.send_message_to_server('startJSCoverage', params)
677
+ end
678
+
679
+ def stop_js_coverage
680
+ @channel.send_message_to_server('stopJSCoverage')
681
+ end
682
+
683
+ def start_css_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
684
+ params = {
685
+ resetOnNavigation: resetOnNavigation,
686
+ }.compact
687
+
688
+ @channel.send_message_to_server('startCSSCoverage', params)
689
+ end
690
+
691
+ def stop_css_coverage
692
+ @channel.send_message_to_server('stopCSSCoverage')
693
+ end
694
+
632
695
  class CrashedError < StandardError
633
696
  def initialize
634
697
  super('Page crashed')
@@ -24,38 +24,31 @@ module Playwright
24
24
  @selectors ||= ::Playwright::ChannelOwners::Selectors.from(@initializer['selectors'])
25
25
  end
26
26
 
27
- class DeviceDescriptor
28
- class Viewport
29
- def initialize(hash)
30
- @width = hash['width']
31
- @heirhgt = hash['height']
32
- end
33
- attr_reader :width, :height
34
- end
35
-
36
- def initialize(hash)
37
- @user_agent = hash["userAgent"]
38
- @viewport = Viewport.new(hash["viewport"])
39
- @device_scale_factor = hash["deviceScaleFactor"]
40
- @is_mobile = hash["isMobile"]
41
- @has_touch = hash["hasTouch"]
42
- end
43
-
44
- attr_reader :user_agent, :viewport, :device_scale_factor
45
-
46
- def mobile?
47
- @is_mobile
48
- end
49
-
50
- def has_touch?
51
- @has_touch
52
- end
53
- end
54
-
55
27
  def devices
56
28
  @devices ||= @initializer['deviceDescriptors'].map do |item|
57
- [item['name'], DeviceDescriptor.new(item['descriptor'])]
29
+ [item['name'], parse_device_descriptor(item['descriptor'])]
58
30
  end.to_h
59
31
  end
32
+
33
+ private def parse_device_descriptor(descriptor)
34
+ # This return value can be passed into Browser#new_context as it is.
35
+ # ex:
36
+ # ```
37
+ # iPhone = playwright.devices['iPhone 6']
38
+ # context = browser.new_context(**iPhone)
39
+ # page = context.new_page
40
+ #
41
+ # ```
42
+ {
43
+ userAgent: descriptor['userAgent'],
44
+ viewport: {
45
+ width: descriptor['viewport']['width'],
46
+ height: descriptor['viewport']['height'],
47
+ },
48
+ deviceScaleFactor: descriptor['deviceScaleFactor'],
49
+ isMobile: descriptor['isMobile'],
50
+ hasTouch: descriptor['hasTouch'],
51
+ }
52
+ end
60
53
  end
61
54
  end
@@ -0,0 +1,15 @@
1
+ require 'base64'
2
+
3
+ module Playwright
4
+ define_channel_owner :Stream do
5
+ def save_as(path)
6
+ File.open(path, 'wb') do |f|
7
+ loop do
8
+ binary = @channel.send_message_to_server('read')
9
+ break if !binary || binary.length == 0
10
+ f.write(Base64.strict_decode64(binary))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -12,6 +12,12 @@ module Playwright
12
12
  @transport.on_message_received do |message|
13
13
  dispatch(message)
14
14
  end
15
+ @transport.on_driver_crashed do
16
+ @callbacks.each_value do |callback|
17
+ callback.reject(::Playwright::DriverCrashedError.new)
18
+ end
19
+ raise ::Playwright::DriverCrashedError.new
20
+ end
15
21
 
16
22
  @objects = {} # Hash[ guid => ChannelOwner ]
17
23
  @waiting_for_object = {} # Hash[ guid => Promise<ChannelOwner> ]
@@ -19,26 +25,22 @@ module Playwright
19
25
  @root_object = RootChannelOwner.new(self)
20
26
  end
21
27
 
22
- def run
23
- @transport.run
28
+ def async_run
29
+ @transport.async_run
24
30
  end
25
31
 
26
32
  def stop
27
33
  @transport.stop
28
34
  end
29
35
 
30
- def async_wait_for_object_with_known_name(guid)
36
+ def wait_for_object_with_known_name(guid)
31
37
  if @objects[guid]
32
38
  return @objects[guid]
33
39
  end
34
40
 
35
41
  callback = Concurrent::Promises.resolvable_future
36
42
  @waiting_for_object[guid] = callback
37
- callback
38
- end
39
-
40
- def wait_for_object_with_known_name(guid)
41
- async_wait_for_object_with_known_name.value!
43
+ callback.value!
42
44
  end
43
45
 
44
46
  def async_send_message_to_server(guid, method, params)
@@ -195,32 +197,9 @@ module Playwright
195
197
  end
196
198
  initializer = replace_guids_with_channels(initializer)
197
199
 
198
- class_name = case type
199
- when 'Browser'
200
- case initializer['name']
201
- when 'chromium'
202
- 'ChromiumBrowser'
203
- when 'webkit'
204
- 'WebKitBrowser'
205
- when 'firefox'
206
- 'FirefoxBrowser'
207
- else
208
- 'Browser'
209
- end
210
- when 'BrowserContext'
211
- browser_name = initializer['browserName']
212
- if browser_name == 'chromium'
213
- 'ChromiumBrowserContext'
214
- else
215
- 'BrowserContext'
216
- end
217
- else
218
- type
219
- end
220
-
221
200
  result =
222
201
  begin
223
- ChannelOwners.const_get(class_name).new(
202
+ ChannelOwners.const_get(type).new(
224
203
  parent,
225
204
  type,
226
205
  guid,
@@ -0,0 +1,27 @@
1
+ module Playwright
2
+ class Download
3
+ def initialize(url:, suggested_filename:, artifact:)
4
+ @url = url
5
+ @suggested_filename = suggested_filename
6
+ @artifact = artifact
7
+ end
8
+
9
+ attr_reader :url, :suggested_filename
10
+
11
+ def delete
12
+ @artifact.delete
13
+ end
14
+
15
+ def failure
16
+ @artifact.failure
17
+ end
18
+
19
+ def path
20
+ @artifact.path_after_finished
21
+ end
22
+
23
+ def save_as(path)
24
+ @artifact.save_as(path)
25
+ end
26
+ end
27
+ end
@@ -27,6 +27,12 @@ module Playwright
27
27
  end
28
28
  end
29
29
 
30
+ class DriverCrashedError < StandardError
31
+ def initialize
32
+ super("[BUG] Playwright driver is crashed!")
33
+ end
34
+ end
35
+
30
36
  class TimeoutError < Error
31
37
  def initialize(message:, stack: [])
32
38
  super(name: 'TimeoutError', message: message, stack: stack)
@@ -24,8 +24,10 @@ end
24
24
  },
25
25
 
26
26
  BrowserContext: {
27
+ BackgroundPage: 'backgroundpage',
27
28
  Close: 'close',
28
29
  Page: 'page',
30
+ ServiceWorker: 'serviceworker',
29
31
  },
30
32
 
31
33
  BrowserServer: {
@@ -67,11 +69,6 @@ end
67
69
  Close: 'close',
68
70
  },
69
71
 
70
- ChromiumBrowserContext: {
71
- BackgroundPage: 'backgroundpage',
72
- ServiceWorker: 'serviceworker',
73
- },
74
-
75
72
  ElectronApplication: {
76
73
  Close: 'close',
77
74
  Window: 'window',