playwright-ruby-client 0.3.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) 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/channel.rb +19 -9
  5. data/lib/playwright/channel_owners/artifact.rb +30 -0
  6. data/lib/playwright/channel_owners/browser.rb +21 -0
  7. data/lib/playwright/channel_owners/browser_context.rb +5 -0
  8. data/lib/playwright/channel_owners/browser_type.rb +28 -0
  9. data/lib/playwright/channel_owners/element_handle.rb +1 -1
  10. data/lib/playwright/channel_owners/frame.rb +25 -2
  11. data/lib/playwright/channel_owners/js_handle.rb +2 -2
  12. data/lib/playwright/channel_owners/page.rb +78 -15
  13. data/lib/playwright/channel_owners/playwright.rb +22 -29
  14. data/lib/playwright/channel_owners/stream.rb +15 -0
  15. data/lib/playwright/connection.rb +11 -32
  16. data/lib/playwright/download.rb +27 -0
  17. data/lib/playwright/errors.rb +6 -0
  18. data/lib/playwright/events.rb +2 -5
  19. data/lib/playwright/keyboard_impl.rb +1 -1
  20. data/lib/playwright/mouse_impl.rb +41 -0
  21. data/lib/playwright/route_handler_entry.rb +1 -9
  22. data/lib/playwright/transport.rb +27 -6
  23. data/lib/playwright/url_matcher.rb +1 -1
  24. data/lib/playwright/utils.rb +8 -0
  25. data/lib/playwright/version.rb +1 -1
  26. data/lib/playwright/video.rb +51 -0
  27. data/lib/playwright/wait_helper.rb +2 -2
  28. data/lib/playwright.rb +47 -9
  29. data/lib/playwright_api/accessibility.rb +39 -1
  30. data/lib/playwright_api/android.rb +10 -10
  31. data/lib/playwright_api/android_device.rb +10 -9
  32. data/lib/playwright_api/browser.rb +83 -8
  33. data/lib/playwright_api/browser_context.rb +163 -15
  34. data/lib/playwright_api/browser_type.rb +35 -4
  35. data/lib/playwright_api/console_message.rb +6 -6
  36. data/lib/playwright_api/dialog.rb +28 -8
  37. data/lib/playwright_api/element_handle.rb +111 -37
  38. data/lib/playwright_api/file_chooser.rb +5 -0
  39. data/lib/playwright_api/frame.rb +228 -37
  40. data/lib/playwright_api/js_handle.rb +26 -3
  41. data/lib/playwright_api/keyboard.rb +48 -1
  42. data/lib/playwright_api/mouse.rb +26 -5
  43. data/lib/playwright_api/page.rb +456 -48
  44. data/lib/playwright_api/playwright.rb +26 -9
  45. data/lib/playwright_api/request.rb +34 -6
  46. data/lib/playwright_api/response.rb +8 -8
  47. data/lib/playwright_api/route.rb +30 -6
  48. data/lib/playwright_api/selectors.rb +32 -6
  49. data/lib/playwright_api/touchscreen.rb +1 -1
  50. data/lib/playwright_api/worker.rb +25 -1
  51. data/playwright.gemspec +4 -2
  52. metadata +37 -14
  53. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  54. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  55. data/lib/playwright/channel_owners/download.rb +0 -27
  56. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  57. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  58. data/lib/playwright_api/binding_call.rb +0 -32
  59. data/lib/playwright_api/chromium_browser_context.rb +0 -59
  60. data/lib/playwright_api/download.rb +0 -95
  61. data/lib/playwright_api/video.rb +0 -24
@@ -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',
@@ -10,7 +10,7 @@ module Playwright
10
10
  end
11
11
 
12
12
  def up(key)
13
- @channel.send_message_to_server('keyboardDown', key: key)
13
+ @channel.send_message_to_server('keyboardUp', key: key)
14
14
  end
15
15
 
16
16
  def insert_text(text)
@@ -3,5 +3,46 @@ module Playwright
3
3
  def initialize(channel)
4
4
  @channel = channel
5
5
  end
6
+
7
+ def move(x, y, steps: nil)
8
+ params = { x: x, y: y, steps: steps }.compact
9
+ @channel.send_message_to_server('mouseMove', params)
10
+ nil
11
+ end
12
+
13
+ def down(button: nil, clickCount: nil)
14
+ params = { button: button, clickCount: clickCount }.compact
15
+ @channel.send_message_to_server('mouseDown', params)
16
+ nil
17
+ end
18
+
19
+ def up(button: nil, clickCount: nil)
20
+ params = { button: button, clickCount: clickCount }.compact
21
+ @channel.send_message_to_server('mouseUp', params)
22
+ nil
23
+ end
24
+
25
+ def click(
26
+ x,
27
+ y,
28
+ button: nil,
29
+ clickCount: nil,
30
+ delay: nil)
31
+
32
+ params = {
33
+ x: x,
34
+ y: y,
35
+ button: button,
36
+ clickCount: clickCount,
37
+ delay: delay,
38
+ }.compact
39
+ @channel.send_message_to_server('mouseClick', params)
40
+
41
+ nil
42
+ end
43
+
44
+ def dblclick(x, y, button: nil, delay: nil)
45
+ click(x, y, button: button, clickCount: 2, delay: delay)
46
+ end
6
47
  end
7
48
  end
@@ -9,7 +9,7 @@ module Playwright
9
9
  end
10
10
 
11
11
  def handle(route, request)
12
- if url_match?(request.url)
12
+ if @url_matcher.match?(request.url)
13
13
  @handler.call(route, request)
14
14
  true
15
15
  else
@@ -24,13 +24,5 @@ module Playwright
24
24
  @url_value == url
25
25
  end
26
26
  end
27
-
28
- private def url_match?(request_url)
29
- if @url_value.is_a?(Regexp)
30
- @url_matcher.match?(request_url)
31
- else
32
- @url_matcher.match?(request_url) || File.fnmatch?(@url_value, request_url)
33
- end
34
- end
35
27
  end
36
28
  end
@@ -12,20 +12,27 @@ module Playwright
12
12
  def initialize(playwright_cli_executable_path:)
13
13
  @driver_executable_path = playwright_cli_executable_path
14
14
  @debug = ENV['DEBUG'].to_s == 'true' || ENV['DEBUG'].to_s == '1'
15
+ @mutex = Mutex.new
15
16
  end
16
17
 
17
18
  def on_message_received(&block)
18
19
  @on_message = block
19
20
  end
20
21
 
22
+ def on_driver_crashed(&block)
23
+ @on_driver_crashed = block
24
+ end
25
+
21
26
  class AlreadyDisconnectedError < StandardError ; end
22
27
 
23
28
  # @param message [Hash]
24
29
  def send_message(message)
25
30
  debug_send_message(message) if @debug
26
31
  msg = JSON.dump(message)
27
- @stdin.write([msg.size].pack('V')) # unsigned 32bit, little endian
28
- @stdin.write(msg)
32
+ @mutex.synchronize {
33
+ @stdin.write([msg.size].pack('V')) # unsigned 32bit, little endian
34
+ @stdin.write(msg)
35
+ }
29
36
  rescue Errno::EPIPE
30
37
  raise AlreadyDisconnectedError.new('send_message failed')
31
38
  end
@@ -33,18 +40,17 @@ module Playwright
33
40
  # Terminate playwright-cli driver.
34
41
  def stop
35
42
  [@stdin, @stdout, @stderr].each { |io| io.close unless io.closed? }
43
+ @thread&.terminate
36
44
  end
37
45
 
38
46
  # Start `playwright-cli run-driver`
39
47
  #
40
48
  # @note This method blocks until playwright-cli exited. Consider using Thread or Future.
41
- def run
42
- @stdin, @stdout, @stderr, @thread = Open3.popen3(@driver_executable_path, 'run-driver')
49
+ def async_run
50
+ @stdin, @stdout, @stderr, @thread = Open3.popen3("#{@driver_executable_path} run-driver")
43
51
 
44
52
  Thread.new { handle_stdout }
45
53
  Thread.new { handle_stderr }
46
-
47
- @thread.join
48
54
  end
49
55
 
50
56
  private
@@ -69,6 +75,21 @@ module Playwright
69
75
 
70
76
  def handle_stderr
71
77
  while err = @stderr.read
78
+ # sometimed driver crashes with the error below.
79
+ # --------
80
+ # undefined:1
81
+ # �
82
+ # ^
83
+
84
+ # SyntaxError: Unexpected token � in JSON at position 0
85
+ # at JSON.parse (<anonymous>)
86
+ # at Transport.transport.onmessage (/home/runner/work/playwright-ruby-client/playwright-ruby-client/node_modules/playwright/lib/cli/driver.js:42:73)
87
+ # at Immediate.<anonymous> (/home/runner/work/playwright-ruby-client/playwright-ruby-client/node_modules/playwright/lib/protocol/transport.js:74:26)
88
+ # at processImmediate (internal/timers.js:461:21)
89
+ if err.include?('undefined:1')
90
+ @on_driver_crashed&.call
91
+ break
92
+ end
72
93
  $stderr.write(err)
73
94
  end
74
95
  rescue IOError
@@ -8,7 +8,7 @@ module Playwright
8
8
  def match?(target_url)
9
9
  case @url
10
10
  when String
11
- @url == target_url
11
+ @url == target_url || File.fnmatch?(@url, target_url)
12
12
  when Regexp
13
13
  @url.match?(target_url)
14
14
  else
@@ -11,6 +11,14 @@ module Playwright
11
11
  if params[:extraHTTPHeaders]
12
12
  params[:extraHTTPHeaders] = ::Playwright::HttpHeaders.new(params[:extraHTTPHeaders]).as_serialized
13
13
  end
14
+ if params[:record_video_dir]
15
+ params[:recordVideo] = {
16
+ dir: params.delete(:record_video_dir)
17
+ }
18
+ if params[:record_video_size]
19
+ params[:recordVideo][:size] = params.delete(:record_video_size)
20
+ end
21
+ end
14
22
  if params[:storageState].is_a?(String)
15
23
  params[:storageState] = JSON.parse(File.read(params[:storageState]))
16
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.2'
5
5
  end
@@ -0,0 +1,51 @@
1
+ module Playwright
2
+ class Video
3
+ def initialize(page)
4
+ @page = page
5
+ @artifact = Concurrent::Promises.resolvable_future
6
+ if @page.closed?
7
+ on_page_closed
8
+ else
9
+ page.once('close', -> { on_page_closed })
10
+ end
11
+ end
12
+
13
+ private def on_page_closed
14
+ unless @artifact.resolved?
15
+ @artifact.reject('Page closed')
16
+ end
17
+ end
18
+
19
+ # called only from Page#on_video via send(:set_artifact, artifact)
20
+ private def set_artifact(artifact)
21
+ @artifact.fulfill(artifact)
22
+ end
23
+
24
+ def path
25
+ wait_for_artifact_and do |artifact|
26
+ artifact.absolute_path
27
+ end
28
+ end
29
+
30
+ def save_as(path)
31
+ wait_for_artifact_and do |artifact|
32
+ artifact.save_as(path)
33
+ end
34
+ end
35
+
36
+ def delete
37
+ wait_for_artifact_and do |artifact|
38
+ artifact.delete
39
+ end
40
+ end
41
+
42
+ private def wait_for_artifact_and(&block)
43
+ artifact = @artifact.value!
44
+ unless artifact
45
+ raise 'Page did not produce any video frames'
46
+ end
47
+
48
+ block.call(artifact)
49
+ end
50
+ end
51
+ end
@@ -22,9 +22,9 @@ module Playwright
22
22
  def reject_on_timeout(timeout_ms, message)
23
23
  return if timeout_ms <= 0
24
24
 
25
- Concurrent::Promises.schedule(timeout_ms / 1000.0) {
25
+ Concurrent::Promises.schedule(timeout_ms / 1000.0) do
26
26
  reject(TimeoutError.new(message: message))
27
- }
27
+ end
28
28
 
29
29
  self
30
30
  end
data/lib/playwright.rb CHANGED
@@ -17,6 +17,7 @@ require 'playwright/utils'
17
17
  require 'playwright/api_implementation'
18
18
  require 'playwright/channel'
19
19
  require 'playwright/channel_owner'
20
+ require 'playwright/download'
20
21
  require 'playwright/http_headers'
21
22
  require 'playwright/input_files'
22
23
  require 'playwright/connection'
@@ -26,6 +27,7 @@ require 'playwright/timeout_settings'
26
27
  require 'playwright/transport'
27
28
  require 'playwright/url_matcher'
28
29
  require 'playwright/version'
30
+ require 'playwright/video'
29
31
  require 'playwright/wait_helper'
30
32
 
31
33
  require 'playwright/playwright_api'
@@ -33,18 +35,54 @@ require 'playwright/playwright_api'
33
35
  Dir[File.join(__dir__, 'playwright_api', '*.rb')].each { |f| require f }
34
36
 
35
37
  module Playwright
36
- module_function def create(playwright_cli_executable_path:, &block)
37
- raise ArgumentError.new("block must be provided") unless block
38
+ class Execution
39
+ def initialize(connection, playwright)
40
+ @connection = connection
41
+ @playwright = playwright
42
+ end
43
+
44
+ def stop
45
+ @connection.stop
46
+ end
47
+
48
+ attr_reader :playwright
49
+ end
38
50
 
51
+ # Recommended to call this method with block.
52
+ #
53
+ # Playwright.create(...) do |playwright|
54
+ # browser = playwright.chromium.launch
55
+ # ...
56
+ # end
57
+ #
58
+ # When we use this method without block, an instance of Puppeteer::Execution is returned
59
+ # and we *must* call execution.stop on the end.
60
+ # The instance of playwright is available by calling execution.playwright
61
+ module_function def create(playwright_cli_executable_path:, &block)
39
62
  connection = Connection.new(playwright_cli_executable_path: playwright_cli_executable_path)
63
+ connection.async_run
64
+
65
+ execution =
66
+ begin
67
+ playwright = connection.wait_for_object_with_known_name('Playwright')
68
+ Execution.new(connection, PlaywrightApi.wrap(playwright))
69
+ rescue
70
+ connection.stop
71
+ raise
72
+ end
40
73
 
41
- playwright_promise = connection.async_wait_for_object_with_known_name('Playwright')
42
- Thread.new { connection.run }
43
- playwright = PlaywrightApi.wrap(playwright_promise.value!)
44
- begin
45
- block.call(playwright)
46
- ensure
47
- connection.stop
74
+ if block
75
+ begin
76
+ block.call(execution.playwright)
77
+ ensure
78
+ execution.stop
79
+ end
80
+ else
81
+ execution
48
82
  end
49
83
  end
84
+
85
+ module_function def instance
86
+ @playwright_instance
87
+ end
50
88
  end
@@ -6,7 +6,7 @@ module Playwright
6
6
  # Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might
7
7
  # have wildly different output.
8
8
  #
9
- # Rendering engines of Chromium, Firefox and Webkit have a concept of "accessibility tree", which is then translated into
9
+ # Rendering engines of Chromium, Firefox and WebKit have a concept of "accessibility tree", which is then translated into
10
10
  # different platform-specific APIs. Accessibility namespace gives access to this Accessibility Tree.
11
11
  #
12
12
  # Most of the accessibility tree gets filtered out when converting from internal browser AX Tree to Platform-specific
@@ -28,6 +28,11 @@ module Playwright
28
28
  # console.log(snapshot);
29
29
  # ```
30
30
  #
31
+ # ```java
32
+ # String snapshot = page.accessibility().snapshot();
33
+ # System.out.println(snapshot);
34
+ # ```
35
+ #
31
36
  # ```python async
32
37
  # snapshot = await page.accessibility.snapshot()
33
38
  # print(snapshot)
@@ -38,6 +43,11 @@ module Playwright
38
43
  # print(snapshot)
39
44
  # ```
40
45
  #
46
+ # ```csharp
47
+ # var accessibilitySnapshot = await Page.Accessibility.SnapshotAsync();
48
+ # Console.WriteLine(accessibilitySnapshot);
49
+ # ```
50
+ #
41
51
  # An example of logging the focused node's name:
42
52
  #
43
53
  #
@@ -57,6 +67,34 @@ module Playwright
57
67
  # }
58
68
  # ```
59
69
  #
70
+ # ```csharp
71
+ # Func<AccessibilitySnapshotResult, AccessibilitySnapshotResult> findFocusedNode = root =>
72
+ # {
73
+ # var nodes = new Stack<AccessibilitySnapshotResult>(new[] { root });
74
+ # while (nodes.Count > 0)
75
+ # {
76
+ # var node = nodes.Pop();
77
+ # if (node.Focused) return node;
78
+ # foreach (var innerNode in node.Children)
79
+ # {
80
+ # nodes.Push(innerNode);
81
+ # }
82
+ # }
83
+ #
84
+ # return null;
85
+ # };
86
+ #
87
+ # var accessibilitySnapshot = await Page.Accessibility.SnapshotAsync();
88
+ # var focusedNode = findFocusedNode(accessibilitySnapshot);
89
+ # if(focusedNode != null)
90
+ # Console.WriteLine(focusedNode.Name);
91
+ # ```
92
+ #
93
+ # ```java
94
+ # // FIXME
95
+ # String snapshot = page.accessibility().snapshot();
96
+ # ```
97
+ #
60
98
  # ```python async
61
99
  # def find_focused_node(node):
62
100
  # if (node.get("focused"))
@@ -3,18 +3,18 @@ module Playwright
3
3
  #
4
4
  #
5
5
  # ```js
6
- # const { _android } = require('playwright');
6
+ # const { _android: android } = require('playwright');
7
7
  # ```
8
8
  #
9
9
  # An example of the Android automation script would be:
10
10
  #
11
11
  #
12
12
  # ```js
13
- # const { _android } = require('playwright');
13
+ # const { _android: android } = require('playwright');
14
14
  #
15
15
  # (async () => {
16
16
  # // Connect to the device.
17
- # const [device] = await playwright._android.devices();
17
+ # const [device] = await android.devices();
18
18
  # console.log(`Model: ${device.model()}`);
19
19
  # console.log(`Serial: ${device.serial()}`);
20
20
  # // Take screenshot of the whole device.
@@ -35,7 +35,7 @@ module Playwright
35
35
  #
36
36
  # // Work with WebView's page as usual.
37
37
  # const page = await webview.page();
38
- # await page.page.waitForNavigation({ url: /.*microsoft\/playwright.*/ });
38
+ # await page.waitForNavigation({ url: /.*microsoft\/playwright.*/ });
39
39
  # console.log(await page.title());
40
40
  # }
41
41
  #
@@ -79,12 +79,6 @@ module Playwright
79
79
  end
80
80
  alias_method :default_timeout=, :set_default_timeout
81
81
 
82
- # -- inherited from EventEmitter --
83
- # @nodoc
84
- def off(event, callback)
85
- event_emitter_proxy.off(event, callback)
86
- end
87
-
88
82
  # -- inherited from EventEmitter --
89
83
  # @nodoc
90
84
  def once(event, callback)
@@ -97,6 +91,12 @@ module Playwright
97
91
  event_emitter_proxy.on(event, callback)
98
92
  end
99
93
 
94
+ # -- inherited from EventEmitter --
95
+ # @nodoc
96
+ def off(event, callback)
97
+ event_emitter_proxy.off(event, callback)
98
+ end
99
+
100
100
  private def event_emitter_proxy
101
101
  @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
102
102
  end
@@ -59,11 +59,12 @@ module Playwright
59
59
  record_har_path: nil,
60
60
  record_video_dir: nil,
61
61
  record_video_size: nil,
62
+ screen: nil,
62
63
  timezoneId: nil,
63
64
  userAgent: nil,
64
65
  viewport: nil,
65
66
  &block)
66
- wrap_impl(@impl.launch_browser(acceptDownloads: unwrap_impl(acceptDownloads), bypassCSP: unwrap_impl(bypassCSP), colorScheme: unwrap_impl(colorScheme), command: unwrap_impl(command), deviceScaleFactor: unwrap_impl(deviceScaleFactor), extraHTTPHeaders: unwrap_impl(extraHTTPHeaders), geolocation: unwrap_impl(geolocation), hasTouch: unwrap_impl(hasTouch), httpCredentials: unwrap_impl(httpCredentials), ignoreHTTPSErrors: unwrap_impl(ignoreHTTPSErrors), isMobile: unwrap_impl(isMobile), javaScriptEnabled: unwrap_impl(javaScriptEnabled), locale: unwrap_impl(locale), noViewport: unwrap_impl(noViewport), offline: unwrap_impl(offline), permissions: unwrap_impl(permissions), record_har_omit_content: unwrap_impl(record_har_omit_content), record_har_path: unwrap_impl(record_har_path), record_video_dir: unwrap_impl(record_video_dir), record_video_size: unwrap_impl(record_video_size), timezoneId: unwrap_impl(timezoneId), userAgent: unwrap_impl(userAgent), viewport: unwrap_impl(viewport), &wrap_block_call(block)))
67
+ wrap_impl(@impl.launch_browser(acceptDownloads: unwrap_impl(acceptDownloads), bypassCSP: unwrap_impl(bypassCSP), colorScheme: unwrap_impl(colorScheme), command: unwrap_impl(command), deviceScaleFactor: unwrap_impl(deviceScaleFactor), extraHTTPHeaders: unwrap_impl(extraHTTPHeaders), geolocation: unwrap_impl(geolocation), hasTouch: unwrap_impl(hasTouch), httpCredentials: unwrap_impl(httpCredentials), ignoreHTTPSErrors: unwrap_impl(ignoreHTTPSErrors), isMobile: unwrap_impl(isMobile), javaScriptEnabled: unwrap_impl(javaScriptEnabled), locale: unwrap_impl(locale), noViewport: unwrap_impl(noViewport), offline: unwrap_impl(offline), permissions: unwrap_impl(permissions), record_har_omit_content: unwrap_impl(record_har_omit_content), record_har_path: unwrap_impl(record_har_path), record_video_dir: unwrap_impl(record_video_dir), record_video_size: unwrap_impl(record_video_size), screen: unwrap_impl(screen), timezoneId: unwrap_impl(timezoneId), userAgent: unwrap_impl(userAgent), viewport: unwrap_impl(viewport), &wrap_block_call(block)))
67
68
  end
68
69
 
69
70
  # Performs a long tap on the widget defined by `selector`.
@@ -159,20 +160,14 @@ module Playwright
159
160
  raise NotImplementedError.new('web_views is not implemented yet.')
160
161
  end
161
162
 
162
- # @nodoc
163
- def tree
164
- wrap_impl(@impl.tree)
165
- end
166
-
167
163
  # @nodoc
168
164
  def tap_on(selector, duration: nil, timeout: nil)
169
165
  wrap_impl(@impl.tap_on(unwrap_impl(selector), duration: unwrap_impl(duration), timeout: unwrap_impl(timeout)))
170
166
  end
171
167
 
172
- # -- inherited from EventEmitter --
173
168
  # @nodoc
174
- def off(event, callback)
175
- event_emitter_proxy.off(event, callback)
169
+ def tree
170
+ wrap_impl(@impl.tree)
176
171
  end
177
172
 
178
173
  # -- inherited from EventEmitter --
@@ -187,6 +182,12 @@ module Playwright
187
182
  event_emitter_proxy.on(event, callback)
188
183
  end
189
184
 
185
+ # -- inherited from EventEmitter --
186
+ # @nodoc
187
+ def off(event, callback)
188
+ event_emitter_proxy.off(event, callback)
189
+ end
190
+
190
191
  private def event_emitter_proxy
191
192
  @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
192
193
  end