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
@@ -15,8 +15,9 @@ module Playwright
15
15
 
16
16
  # @param method [String]
17
17
  # @param params [Hash]
18
+ # @return [Playwright::ChannelOwner|nil]
18
19
  def send_message_to_server(method, params = {})
19
- result = @connection.send_message_to_server(@guid, method, params)
20
+ result = send_message_to_server_result(method, params)
20
21
  if result.is_a?(Hash)
21
22
  _type, channel_owner = result.first
22
23
  channel_owner
@@ -27,8 +28,17 @@ module Playwright
27
28
 
28
29
  # @param method [String]
29
30
  # @param params [Hash]
30
- def send_no_reply(method, params)
31
+ # @return [Hash]
32
+ def send_message_to_server_result(method, params)
33
+ @connection.send_message_to_server(@guid, method, params)
34
+ end
35
+
36
+ # @param method [String]
37
+ # @param params [Hash]
38
+ # @returns nil
39
+ def async_send_message_to_server(method, params = {})
31
40
  @connection.async_send_message_to_server(@guid, method, params)
41
+
32
42
  nil
33
43
  end
34
44
  end
@@ -59,16 +59,14 @@ module Playwright
59
59
  "#<#{@guid}>"
60
60
  end
61
61
 
62
- private
63
-
64
- def after_initialize
62
+ private def after_initialize
65
63
  end
66
64
 
67
- def update_object_from_child(guid, child)
65
+ private def update_object_from_child(guid, child)
68
66
  @objects[guid] = child
69
67
  end
70
68
 
71
- def delete_object_from_child(guid)
69
+ private def delete_object_from_child(guid)
72
70
  @objects.delete(guid)
73
71
  end
74
72
  end
@@ -1,6 +1,6 @@
1
1
  module Playwright
2
2
  define_channel_owner :Android do
3
- def after_initialize
3
+ private def after_initialize
4
4
  @timeout_settings = TimeoutSettings.new
5
5
  end
6
6
 
@@ -2,7 +2,7 @@ module Playwright
2
2
  define_channel_owner :AndroidDevice do
3
3
  include Utils::PrepareBrowserContextOptions
4
4
 
5
- def after_initialize
5
+ private def after_initialize
6
6
  @input = AndroidInputImpl.new(@channel)
7
7
  end
8
8
 
@@ -104,17 +104,17 @@ module Playwright
104
104
  isMobile: nil,
105
105
  javaScriptEnabled: nil,
106
106
  locale: nil,
107
- logger: nil,
107
+ noViewport: nil,
108
108
  offline: nil,
109
109
  permissions: nil,
110
110
  proxy: nil,
111
- recordHar: nil,
112
- recordVideo: nil,
111
+ record_har_omit_content: nil,
112
+ record_har_path: nil,
113
+ record_video_dir: nil,
114
+ record_video_size: nil,
113
115
  storageState: nil,
114
116
  timezoneId: nil,
115
117
  userAgent: nil,
116
- videoSize: nil,
117
- videosPath: nil,
118
118
  viewport: nil,
119
119
  &block)
120
120
  params = {
@@ -131,17 +131,17 @@ module Playwright
131
131
  isMobile: isMobile,
132
132
  javaScriptEnabled: javaScriptEnabled,
133
133
  locale: locale,
134
- logger: logger,
134
+ noViewport: noViewport,
135
135
  offline: offline,
136
136
  permissions: permissions,
137
137
  proxy: proxy,
138
- recordHar: recordHar,
139
- recordVideo: recordVideo,
138
+ record_har_omit_content: record_har_omit_content,
139
+ record_har_path: record_har_path,
140
+ record_video_dir: record_video_dir,
141
+ record_video_size: record_video_size,
140
142
  storageState: storageState,
141
143
  timezoneId: timezoneId,
142
144
  userAgent: userAgent,
143
- videoSize: videoSize,
144
- videosPath: videosPath,
145
145
  viewport: viewport,
146
146
  }.compact
147
147
  prepare_browser_context_options(params)
@@ -0,0 +1,30 @@
1
+ module Playwright
2
+ define_channel_owner :Artifact do
3
+ private def after_initialize
4
+ @is_remote = false
5
+ @absolute_path = @initializer['absolutePath']
6
+ end
7
+
8
+ attr_reader :absolute_path
9
+
10
+ def path_after_finished
11
+ if @is_remote
12
+ raise "Path is not available when using browser_type.connect(). Use save_as() to save a local copy."
13
+ end
14
+ @channel.send_message_to_server('pathAfterFinished')
15
+ end
16
+
17
+ def save_as(path)
18
+ stream = ChannelOwners::Stream.from(@channel.send_message_to_server('saveAsStream'))
19
+ stream.save_as(path)
20
+ end
21
+
22
+ def failure
23
+ @channel.send_message_to_server('failure')
24
+ end
25
+
26
+ def delete
27
+ @channel.send_message_to_server('delete')
28
+ end
29
+ end
30
+ end
@@ -1,4 +1,7 @@
1
1
  module Playwright
2
2
  define_channel_owner :BindingCall do
3
+ def name
4
+ @initializer['name']
5
+ end
3
6
  end
4
7
  end
@@ -4,7 +4,7 @@ module Playwright
4
4
  include Utils::Errors::SafeCloseError
5
5
  include Utils::PrepareBrowserContextOptions
6
6
 
7
- def after_initialize
7
+ private def after_initialize
8
8
  @contexts = Set.new
9
9
  @channel.on('close', method(:on_close))
10
10
  end
@@ -59,12 +59,33 @@ module Playwright
59
59
  @initializer['version']
60
60
  end
61
61
 
62
+ def start_tracing(page: nil, categories: nil, path: nil, screenshots: nil)
63
+ params = {
64
+ page: page&.channel,
65
+ categories: categories,
66
+ path: path,
67
+ screenshots: screenshots,
68
+ }.compact
69
+
70
+ @channel.send_message_to_server('startTracing', params)
71
+ end
72
+
73
+ def stop_tracing
74
+ encoded_binary = @channel.send_message_to_server("stopTracing")
75
+ return Base64.strict_decode64(encoded_binary)
76
+ end
77
+
62
78
  private def on_close(_ = {})
63
79
  @connected = false
64
80
  emit(Events::Browser::Disconnected)
65
81
  @closed_or_closing = false
66
82
  end
67
83
 
84
+ # called from BrowserType#connectOverCDP
85
+ private def add_context(context)
86
+ @contexts << context
87
+ end
88
+
68
89
  # called from BrowserContext#on_close with send(:remove_context), so keep private.
69
90
  private def remove_context(context)
70
91
  @contexts.delete(context)
@@ -2,11 +2,16 @@ 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
4
  include Utils::Errors::SafeCloseError
5
- attr_writer :browser, :owner_page, :options
5
+ attr_accessor :browser
6
+ attr_writer :owner_page, :options
6
7
 
7
- def after_initialize
8
+ private def after_initialize
8
9
  @pages = Set.new
10
+ @routes = Set.new
11
+ @bindings = {}
12
+ @timeout_settings = TimeoutSettings.new
9
13
 
14
+ @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
10
15
  @channel.once('close', ->(_) { on_close })
11
16
  @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
12
17
  @channel.on('route', ->(params) {
@@ -18,11 +23,36 @@ module Playwright
18
23
  page.send(:update_browser_context, self)
19
24
  @pages << page
20
25
  emit(Events::BrowserContext::Page, page)
26
+ page.send(:emit_popup_event_from_browser_context)
21
27
  end
22
28
 
23
29
  private def on_route(route, request)
24
- # @routes.each ...
25
- route.continue_
30
+ # It is not desired to use PlaywrightApi.wrap directly.
31
+ # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
32
+ # Just a workaround...
33
+ wrapped_route = PlaywrightApi.wrap(route)
34
+ wrapped_request = PlaywrightApi.wrap(request)
35
+
36
+ if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
37
+ route.continue
38
+ end
39
+ end
40
+
41
+ private def on_binding(binding_call)
42
+ func = @binding[binding_call.name]
43
+ if func
44
+ binding_call.call(func)
45
+ end
46
+ end
47
+
48
+ def set_default_navigation_timeout(timeout)
49
+ @timeout_settings.default_navigation_timeout = timeout
50
+ @channel.send_message_to_server('setDefaultNavigationTimeoutNoReply', timeout: timeout)
51
+ end
52
+
53
+ def set_default_timeout(timeout)
54
+ @timeout_settings.default_timeout = timeout
55
+ @channel.send_message_to_server('setDefaultTimeoutNoReply', timeout: timeout)
26
56
  end
27
57
 
28
58
  def pages
@@ -36,6 +66,111 @@ module Playwright
36
66
  ChannelOwners::Page.from(resp)
37
67
  end
38
68
 
69
+ def cookies(urls: nil)
70
+ target_urls =
71
+ if urls.nil?
72
+ []
73
+ elsif urls.is_a?(Enumerable)
74
+ urls
75
+ else
76
+ [urls]
77
+ end
78
+ @channel.send_message_to_server('cookies', urls: urls)
79
+ end
80
+
81
+ def add_cookies(cookies)
82
+ @channel.send_message_to_server('addCookies', cookies: cookies)
83
+ end
84
+
85
+ def clear_cookies
86
+ @channel.send_message_to_server('clearCookies')
87
+ end
88
+
89
+ def grant_permissions(permissions, origin: nil)
90
+ params = {
91
+ permissions: permissions,
92
+ origin: origin,
93
+ }.compact
94
+ @channel.send_message_to_server('grantPermissions', params)
95
+ end
96
+
97
+ def clear_permissions
98
+ @channel.send_message_to_server('clearPermissions')
99
+ end
100
+
101
+ def set_geolocation(geolocation)
102
+ @channel.send_message_to_server('setGeolocation', geolocation: geolocation)
103
+ end
104
+
105
+ def set_extra_http_headers(headers)
106
+ @channel.send_message_to_server('setExtraHTTPHeaders',
107
+ headers: HttpHeaders.new(headers).as_serialized)
108
+ end
109
+
110
+ def set_offline(offline)
111
+ @channel.send_message_to_server('setOffline', offline: offline)
112
+ end
113
+
114
+ def add_init_script(path: nil, script: nil)
115
+ source =
116
+ if path
117
+ File.read(path)
118
+ elsif script
119
+ script
120
+ else
121
+ raise ArgumentError.new('Either path or script parameter must be specified')
122
+ end
123
+
124
+ @channel.send_message_to_server('addInitScript', source: script)
125
+ nil
126
+ end
127
+
128
+ def expose_binding(name, callback, handle: nil)
129
+ if @pages.any? { |page| page.send(:has_bindings?, name) }
130
+ raise ArgumentError.new("Function \"#{name}\" has been already registered in one of the pages")
131
+ end
132
+ if @bindings.key?(name)
133
+ raise ArgumentError.new("Function \"#{name}\" has been already registered")
134
+ end
135
+ params = {
136
+ name: name,
137
+ needsHandle: handle,
138
+ }.compact
139
+ @bindings[name] = callback
140
+ @channel.send_message_to_server('exposeBinding', params)
141
+ end
142
+
143
+ def expose_function(name, callback)
144
+ expose_binding(name, ->(_source, *args) { callback.call(*args) }, )
145
+ end
146
+
147
+ def route(url, handler)
148
+ entry = RouteHandlerEntry.new(url, handler)
149
+ @routes << entry
150
+ if @routes.count >= 1
151
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
152
+ end
153
+ end
154
+
155
+ def unroute(url, handler: nil)
156
+ @routes.reject! do |handler_entry|
157
+ handler_entry.same_value?(url: url, handler: handler)
158
+ end
159
+ if @routes.count == 0
160
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
161
+ end
162
+ end
163
+
164
+ def expect_event(event, predicate: nil, timeout: nil, &block)
165
+ wait_helper = WaitHelper.new
166
+ wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
167
+ wait_helper.wait_for_event(self, event, predicate: predicate)
168
+
169
+ block&.call
170
+
171
+ wait_helper.promise.value!
172
+ end
173
+
39
174
  private def on_close
40
175
  @closed_or_closing = true
41
176
  @browser&.send(:remove_context, self)
@@ -51,6 +186,18 @@ module Playwright
51
186
  raise unless safe_close_error?(err)
52
187
  end
53
188
 
189
+ def pause
190
+ @channel.send_message_to_server('pause')
191
+ end
192
+
193
+ def expect_page(predicate: nil, timeout: nil)
194
+ params = {
195
+ predicate: predicate,
196
+ timeout: timeout,
197
+ }.compact
198
+ expect_event(Events::BrowserContext::Page, params)
199
+ end
200
+
54
201
  # called from Page#on_close with send(:remove_page, page), so keep private
55
202
  private def remove_page(page)
56
203
  @pages.delete(page)
@@ -60,5 +207,9 @@ module Playwright
60
207
  private def _timeout_settings
61
208
  @timeout_settings
62
209
  end
210
+
211
+ private def has_record_video_option?
212
+ @options.key?(:recordVideo)
213
+ end
63
214
  end
64
215
  end
@@ -22,5 +22,33 @@ module Playwright
22
22
  browser
23
23
  end
24
24
  end
25
+
26
+ def connect_over_cdp(endpointURL, slowMo: nil, timeout: nil, &block)
27
+ raise 'Connecting over CDP is only supported in Chromium.' unless name == 'chromium'
28
+
29
+ params = {
30
+ sdkLanguage: 'ruby',
31
+ endpointURL: endpointURL,
32
+ slowMo: slowMo,
33
+ timeout: timeout,
34
+ }.compact
35
+ result = @channel.send_message_to_server_result('connectOverCDP', params)
36
+ browser = ChannelOwners::Browser.from(result['browser'])
37
+
38
+ if result['defaultContext']
39
+ context = ChannelOwners::BrowserContext.from(result['defaultContext'])
40
+ browser.send(:add_context, context)
41
+ end
42
+
43
+ if block
44
+ begin
45
+ block.call(browser)
46
+ ensure
47
+ browser.close
48
+ end
49
+ else
50
+ browser
51
+ end
52
+ end
25
53
  end
26
54
  end
@@ -0,0 +1,28 @@
1
+ module Playwright
2
+ define_channel_owner :Dialog do
3
+ def type
4
+ @initializer['type']
5
+ end
6
+
7
+ def message
8
+ @initializer['message']
9
+ end
10
+
11
+ def default_value
12
+ @initializer['defaultValue']
13
+ end
14
+
15
+ def accept(promptText: nil)
16
+ accept_async(prompt_text: promptText).value!
17
+ end
18
+
19
+ def accept_async(promptText: nil)
20
+ params = { promptText: promptText }.compact
21
+ @channel.async_send_message_to_server('accept', params)
22
+ end
23
+
24
+ def dismiss
25
+ @channel.async_send_message_to_server('dismiss')
26
+ end
27
+ end
28
+ end