playwright-ruby-client 0.3.0 → 0.5.2

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 (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