playwright-ruby-client 0.1.0 → 0.5.3

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -8
  3. data/docs/api_coverage.md +123 -73
  4. data/lib/playwright.rb +48 -9
  5. data/lib/playwright/channel.rb +12 -2
  6. data/lib/playwright/channel_owner.rb +3 -5
  7. data/lib/playwright/channel_owners/android.rb +1 -1
  8. data/lib/playwright/channel_owners/android_device.rb +11 -11
  9. data/lib/playwright/channel_owners/artifact.rb +30 -0
  10. data/lib/playwright/channel_owners/binding_call.rb +3 -0
  11. data/lib/playwright/channel_owners/browser.rb +22 -1
  12. data/lib/playwright/channel_owners/browser_context.rb +155 -4
  13. data/lib/playwright/channel_owners/browser_type.rb +28 -0
  14. data/lib/playwright/channel_owners/dialog.rb +28 -0
  15. data/lib/playwright/channel_owners/element_handle.rb +18 -5
  16. data/lib/playwright/channel_owners/frame.rb +40 -5
  17. data/lib/playwright/channel_owners/js_handle.rb +3 -3
  18. data/lib/playwright/channel_owners/page.rb +172 -51
  19. data/lib/playwright/channel_owners/playwright.rb +24 -27
  20. data/lib/playwright/channel_owners/request.rb +27 -3
  21. data/lib/playwright/channel_owners/response.rb +60 -0
  22. data/lib/playwright/channel_owners/route.rb +78 -0
  23. data/lib/playwright/channel_owners/selectors.rb +19 -1
  24. data/lib/playwright/channel_owners/stream.rb +15 -0
  25. data/lib/playwright/connection.rb +11 -32
  26. data/lib/playwright/download.rb +27 -0
  27. data/lib/playwright/errors.rb +6 -0
  28. data/lib/playwright/events.rb +2 -5
  29. data/lib/playwright/keyboard_impl.rb +1 -1
  30. data/lib/playwright/mouse_impl.rb +41 -0
  31. data/lib/playwright/playwright_api.rb +3 -1
  32. data/lib/playwright/route_handler_entry.rb +28 -0
  33. data/lib/playwright/select_option_values.rb +14 -4
  34. data/lib/playwright/transport.rb +28 -7
  35. data/lib/playwright/url_matcher.rb +1 -1
  36. data/lib/playwright/utils.rb +11 -2
  37. data/lib/playwright/version.rb +1 -1
  38. data/lib/playwright/video.rb +51 -0
  39. data/lib/playwright/wait_helper.rb +2 -2
  40. data/lib/playwright_api/accessibility.rb +39 -1
  41. data/lib/playwright_api/android.rb +72 -5
  42. data/lib/playwright_api/android_device.rb +139 -26
  43. data/lib/playwright_api/android_input.rb +17 -13
  44. data/lib/playwright_api/android_socket.rb +16 -0
  45. data/lib/playwright_api/android_web_view.rb +21 -0
  46. data/lib/playwright_api/browser.rb +87 -19
  47. data/lib/playwright_api/browser_context.rb +216 -32
  48. data/lib/playwright_api/browser_type.rb +45 -58
  49. data/lib/playwright_api/dialog.rb +54 -7
  50. data/lib/playwright_api/element_handle.rb +113 -33
  51. data/lib/playwright_api/file_chooser.rb +6 -1
  52. data/lib/playwright_api/frame.rb +238 -43
  53. data/lib/playwright_api/js_handle.rb +20 -2
  54. data/lib/playwright_api/keyboard.rb +48 -1
  55. data/lib/playwright_api/mouse.rb +26 -5
  56. data/lib/playwright_api/page.rb +534 -63
  57. data/lib/playwright_api/playwright.rb +43 -47
  58. data/lib/playwright_api/request.rb +38 -12
  59. data/lib/playwright_api/response.rb +27 -10
  60. data/lib/playwright_api/route.rb +51 -6
  61. data/lib/playwright_api/selectors.rb +28 -2
  62. data/lib/playwright_api/touchscreen.rb +1 -1
  63. data/lib/playwright_api/web_socket.rb +15 -0
  64. data/lib/playwright_api/worker.rb +25 -1
  65. data/playwright.gemspec +4 -2
  66. metadata +42 -14
  67. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  68. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  69. data/lib/playwright/channel_owners/download.rb +0 -27
  70. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  71. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  72. data/lib/playwright_api/binding_call.rb +0 -27
  73. data/lib/playwright_api/chromium_browser_context.rb +0 -59
  74. data/lib/playwright_api/download.rb +0 -100
  75. data/lib/playwright_api/video.rb +0 -24
@@ -20,38 +20,35 @@ module Playwright
20
20
  @electron ||= ::Playwright::ChannelOwners::Electron.from(@initializer['electron'])
21
21
  end
22
22
 
23
- class DeviceDescriptor
24
- class Viewport
25
- def initialize(hash)
26
- @width = hash['width']
27
- @heirhgt = hash['height']
28
- end
29
- attr_reader :width, :height
30
- end
31
-
32
- def initialize(hash)
33
- @user_agent = hash["userAgent"]
34
- @viewport = Viewport.new(hash["viewport"])
35
- @device_scale_factor = hash["deviceScaleFactor"]
36
- @is_mobile = hash["isMobile"]
37
- @has_touch = hash["hasTouch"]
38
- end
39
-
40
- attr_reader :user_agent, :viewport, :device_scale_factor
41
-
42
- def mobile?
43
- @is_mobile
44
- end
45
-
46
- def has_touch?
47
- @has_touch
48
- end
23
+ def selectors
24
+ @selectors ||= ::Playwright::ChannelOwners::Selectors.from(@initializer['selectors'])
49
25
  end
50
26
 
51
27
  def devices
52
28
  @devices ||= @initializer['deviceDescriptors'].map do |item|
53
- [item['name'], DeviceDescriptor.new(item['descriptor'])]
29
+ [item['name'], parse_device_descriptor(item['descriptor'])]
54
30
  end.to_h
55
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
56
53
  end
57
54
  end
@@ -3,7 +3,7 @@ require 'base64'
3
3
  module Playwright
4
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
5
5
  define_channel_owner :Request do
6
- def after_initialize
6
+ private def after_initialize
7
7
  @redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
8
8
  @redirected_from&.send(:update_redirected_to, self)
9
9
  @timing = {
@@ -29,7 +29,7 @@ module Playwright
29
29
  end
30
30
 
31
31
  def method
32
- @initialize['method']
32
+ @initializer['method']
33
33
  end
34
34
 
35
35
  def post_data
@@ -69,7 +69,7 @@ module Playwright
69
69
  end
70
70
 
71
71
  def frame
72
- @initializer['frame']
72
+ ChannelOwners::Frame.from(@initializer['frame'])
73
73
  end
74
74
 
75
75
  def navigation_request?
@@ -90,6 +90,30 @@ module Playwright
90
90
  @failure_text = failure_text
91
91
  end
92
92
 
93
+ private def update_timings(
94
+ start_time:,
95
+ domain_lookup_start:,
96
+ domain_lookup_end:,
97
+ connect_start:,
98
+ secure_connection_start:,
99
+ connect_end:,
100
+ request_start:,
101
+ response_start:)
102
+
103
+ @timing["startTime"] = start_time
104
+ @timing["domainLookupStart"] = domain_lookup_start
105
+ @timing["domainLookupEnd"] = domain_lookup_end
106
+ @timing["connectStart"] = connect_start
107
+ @timing["secureConnectionStart"] = secure_connection_start
108
+ @timing["connectEnd"] = connect_end
109
+ @timing["requestStart"] = request_start
110
+ @timing["responseStart"] = response_start
111
+ end
112
+
113
+ private def update_headers(headers)
114
+ @headers = parse_headers(headers)
115
+ end
116
+
93
117
  private def update_response_end_timing(response_end_timing)
94
118
  @timing[:responseEnd] = response_end_timing
95
119
  end
@@ -1,5 +1,65 @@
1
+ require 'base64'
2
+ require 'json'
3
+
1
4
  module Playwright
2
5
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
3
6
  define_channel_owner :Response do
7
+ def after_initialize
8
+ @request = ChannelOwners::Request.from(@initializer['request'])
9
+ timing = @initializer['timing']
10
+ @request.send(:update_timings,
11
+ start_time: timing["startTime"],
12
+ domain_lookup_start: timing["domainLookupStart"],
13
+ domain_lookup_end: timing["domainLookupEnd"],
14
+ connect_start: timing["connectStart"],
15
+ secure_connection_start: timing["secureConnectionStart"],
16
+ connect_end: timing["connectEnd"],
17
+ request_start: timing["requestStart"],
18
+ response_start: timing["responseStart"],
19
+ )
20
+ @request.send(:update_headers, @initializer['requestHeaders'])
21
+ end
22
+ attr_reader :request
23
+
24
+ def url
25
+ @initializer['url']
26
+ end
27
+
28
+ def ok
29
+ status == 0 || (200...300).include?(status)
30
+ end
31
+ alias_method :ok?, :ok
32
+
33
+ def status
34
+ @initializer['status']
35
+ end
36
+
37
+ def status_text
38
+ @initializer['statusText']
39
+ end
40
+
41
+ def headers
42
+ @initializer['headers'].map do |header|
43
+ [header['name'].downcase, header['value']]
44
+ end.to_h
45
+ end
46
+
47
+ def finished
48
+ @channel.send_message_to_server('finished')
49
+ end
50
+
51
+ def body
52
+ binary = @channel.send_message_to_server("body")
53
+ Base64.strict_decode64(binary)
54
+ end
55
+ alias_method :text, :body
56
+
57
+ def json
58
+ JSON.parse(text)
59
+ end
60
+
61
+ def frame
62
+ @request.frame
63
+ end
4
64
  end
5
65
  end
@@ -0,0 +1,78 @@
1
+ require 'base64'
2
+ require 'mime/types'
3
+
4
+ module Playwright
5
+ define_channel_owner :Route do
6
+ def request
7
+ ChannelOwners::Request.from(@initializer['request'])
8
+ end
9
+
10
+ def abort(errorCode: nil)
11
+ params = { errorCode: errorCode }.compact
12
+ @channel.async_send_message_to_server('abort', params)
13
+ end
14
+
15
+ def fulfill(
16
+ body: nil,
17
+ contentType: nil,
18
+ headers: nil,
19
+ path: nil,
20
+ status: nil)
21
+ params = {
22
+ contentType: contentType,
23
+ status: status,
24
+ }.compact
25
+
26
+ length = 0
27
+ content =
28
+ if body
29
+ body
30
+ elsif path
31
+ File.read(path)
32
+ else
33
+ nil
34
+ end
35
+
36
+ param_headers = headers || {}
37
+ if contentType
38
+ param_headers['content-type'] = contentType
39
+ elsif path
40
+ param_headers['content-type'] = mime_type_for(path)
41
+ end
42
+
43
+ if content
44
+ if content.is_a?(String)
45
+ params[:body] = content
46
+ params[:isBase64] = false
47
+ else
48
+ params[:body] = Base64.strict_encode64(content)
49
+ params[:isBase64] = true
50
+ end
51
+ param_headers['content-length'] ||= content.length.to_s
52
+ end
53
+
54
+ params[:headers] = HttpHeaders.new(param_headers).as_serialized
55
+
56
+ @channel.async_send_message_to_server('fulfill', params)
57
+ end
58
+
59
+ def continue(headers: nil, method: nil, postData: nil, url: nil)
60
+ overrides = { url: url, method: method }.compact
61
+
62
+ if headers
63
+ overrides[:headers] = HttpHeaders.new(headers).as_serialized
64
+ end
65
+
66
+ if postData
67
+ overrides[:postData] = Base64.strict_encode64(postData)
68
+ end
69
+
70
+ @channel.async_send_message_to_server('continue', overrides)
71
+ end
72
+
73
+ private def mime_type_for(filepath)
74
+ mime_types = MIME::Types.type_for(filepath)
75
+ mime_types.first.to_s || 'application/octet-stream'
76
+ end
77
+ end
78
+ end
@@ -1,4 +1,22 @@
1
1
  module Playwright
2
2
  # https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_selectors.py
3
- define_channel_owner :Selectors
3
+ define_channel_owner :Selectors do
4
+ def register(name, contentScript: nil, path: nil, script: nil)
5
+ source =
6
+ if path
7
+ File.read(path)
8
+ elsif script
9
+ script
10
+ else
11
+ raise ArgumentError.new('Either path or script parameter must be specified')
12
+ end
13
+ params = { name: name, source: source }
14
+ if contentScript
15
+ params[:contentScript] = true
16
+ end
17
+ @channel.send_message_to_server('register', params)
18
+
19
+ nil
20
+ end
21
+ end
4
22
  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',