puppeteer-bidi 0.0.1.beta1

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +13 -0
  4. data/CLAUDE/README.md +158 -0
  5. data/CLAUDE/async_programming.md +158 -0
  6. data/CLAUDE/click_implementation.md +340 -0
  7. data/CLAUDE/core_layer_gotchas.md +136 -0
  8. data/CLAUDE/error_handling.md +232 -0
  9. data/CLAUDE/file_chooser.md +95 -0
  10. data/CLAUDE/frame_architecture.md +346 -0
  11. data/CLAUDE/javascript_evaluation.md +341 -0
  12. data/CLAUDE/jshandle_implementation.md +505 -0
  13. data/CLAUDE/keyboard_implementation.md +250 -0
  14. data/CLAUDE/mouse_implementation.md +140 -0
  15. data/CLAUDE/navigation_waiting.md +234 -0
  16. data/CLAUDE/porting_puppeteer.md +214 -0
  17. data/CLAUDE/query_handler.md +194 -0
  18. data/CLAUDE/rspec_pending_vs_skip.md +262 -0
  19. data/CLAUDE/selector_evaluation.md +198 -0
  20. data/CLAUDE/test_server_routes.md +263 -0
  21. data/CLAUDE/testing_strategy.md +236 -0
  22. data/CLAUDE/two_layer_architecture.md +180 -0
  23. data/CLAUDE/wrapped_element_click.md +247 -0
  24. data/CLAUDE.md +185 -0
  25. data/LICENSE.txt +21 -0
  26. data/README.md +488 -0
  27. data/Rakefile +21 -0
  28. data/lib/puppeteer/bidi/async_utils.rb +151 -0
  29. data/lib/puppeteer/bidi/browser.rb +285 -0
  30. data/lib/puppeteer/bidi/browser_context.rb +53 -0
  31. data/lib/puppeteer/bidi/browser_launcher.rb +240 -0
  32. data/lib/puppeteer/bidi/connection.rb +182 -0
  33. data/lib/puppeteer/bidi/core/README.md +169 -0
  34. data/lib/puppeteer/bidi/core/browser.rb +230 -0
  35. data/lib/puppeteer/bidi/core/browsing_context.rb +601 -0
  36. data/lib/puppeteer/bidi/core/disposable.rb +69 -0
  37. data/lib/puppeteer/bidi/core/errors.rb +64 -0
  38. data/lib/puppeteer/bidi/core/event_emitter.rb +83 -0
  39. data/lib/puppeteer/bidi/core/navigation.rb +128 -0
  40. data/lib/puppeteer/bidi/core/realm.rb +315 -0
  41. data/lib/puppeteer/bidi/core/request.rb +300 -0
  42. data/lib/puppeteer/bidi/core/session.rb +153 -0
  43. data/lib/puppeteer/bidi/core/user_context.rb +208 -0
  44. data/lib/puppeteer/bidi/core/user_prompt.rb +102 -0
  45. data/lib/puppeteer/bidi/core.rb +45 -0
  46. data/lib/puppeteer/bidi/deserializer.rb +132 -0
  47. data/lib/puppeteer/bidi/element_handle.rb +602 -0
  48. data/lib/puppeteer/bidi/errors.rb +42 -0
  49. data/lib/puppeteer/bidi/file_chooser.rb +52 -0
  50. data/lib/puppeteer/bidi/frame.rb +597 -0
  51. data/lib/puppeteer/bidi/http_response.rb +23 -0
  52. data/lib/puppeteer/bidi/injected.js +1 -0
  53. data/lib/puppeteer/bidi/injected_source.rb +21 -0
  54. data/lib/puppeteer/bidi/js_handle.rb +302 -0
  55. data/lib/puppeteer/bidi/keyboard.rb +265 -0
  56. data/lib/puppeteer/bidi/lazy_arg.rb +23 -0
  57. data/lib/puppeteer/bidi/mouse.rb +170 -0
  58. data/lib/puppeteer/bidi/page.rb +613 -0
  59. data/lib/puppeteer/bidi/query_handler.rb +397 -0
  60. data/lib/puppeteer/bidi/realm.rb +242 -0
  61. data/lib/puppeteer/bidi/serializer.rb +139 -0
  62. data/lib/puppeteer/bidi/target.rb +81 -0
  63. data/lib/puppeteer/bidi/task_manager.rb +44 -0
  64. data/lib/puppeteer/bidi/timeout_settings.rb +20 -0
  65. data/lib/puppeteer/bidi/transport.rb +129 -0
  66. data/lib/puppeteer/bidi/version.rb +7 -0
  67. data/lib/puppeteer/bidi/wait_task.rb +322 -0
  68. data/lib/puppeteer/bidi.rb +49 -0
  69. data/scripts/update_injected_source.rb +57 -0
  70. data/sig/puppeteer/bidi/browser.rbs +80 -0
  71. data/sig/puppeteer/bidi/element_handle.rbs +238 -0
  72. data/sig/puppeteer/bidi/frame.rbs +205 -0
  73. data/sig/puppeteer/bidi/js_handle.rbs +90 -0
  74. data/sig/puppeteer/bidi/page.rbs +247 -0
  75. data/sig/puppeteer/bidi.rbs +15 -0
  76. metadata +176 -0
@@ -0,0 +1,322 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puppeteer
4
+ module Bidi
5
+ # WaitTask orchestrates polling for a predicate using Puppeteer's Poller classes.
6
+ # This is a faithful port of Puppeteer's WaitTask implementation:
7
+ # https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/common/WaitTask.ts
8
+ #
9
+ # Note: signal and AbortSignal are not implemented as they are JavaScript-specific
10
+ class WaitTask
11
+ # Corresponds to Puppeteer's WaitTask constructor
12
+ # @param world [Realm] The realm to execute in (matches Puppeteer's World abstraction)
13
+ # @param options [Hash] Options for waiting
14
+ # @option options [String, Numeric] :polling Polling strategy ('raf', 'mutation', or interval in ms)
15
+ # @option options [Numeric] :timeout Timeout in milliseconds
16
+ # @option options [ElementHandle] :root Root element for mutation polling
17
+ # @param fn [String] JavaScript function to evaluate
18
+ # @param args [Array] Arguments to pass to the function
19
+ def initialize(world, options, fn, *args)
20
+ @world = world
21
+ @polling = options[:polling]
22
+ @root = options[:root]
23
+
24
+ # Convert function to string format
25
+ # Corresponds to Puppeteer's switch (typeof fn)
26
+ if fn.is_a?(String)
27
+ # Check if the string is already a function (starts with "function ", "(", or "async ")
28
+ # If so, use it as-is. Otherwise, wrap it as an expression.
29
+ if fn.strip.match?(/\A(?:function\s|\(|async\s)/)
30
+ @fn = fn
31
+ else
32
+ @fn = "() => {return (#{fn});}"
33
+ end
34
+ else
35
+ raise ArgumentError, 'fn must be a string'
36
+ end
37
+ @args = args
38
+
39
+ # Corresponds to Puppeteer's #timeout and #timeoutError
40
+ @timeout_task = nil
41
+ @generic_error = StandardError.new('Waiting failed')
42
+ @timeout_error = nil
43
+
44
+ # Corresponds to Puppeteer's #result = Deferred.create<HandleFor<T>>()
45
+ @result = Async::Promise.new
46
+
47
+ # Corresponds to Puppeteer's #poller?: JSHandle<Poller<T>>
48
+ @poller = nil
49
+
50
+ # Track the active rerun Async task so we can cancel it when rerunning
51
+ @rerun_task = nil
52
+
53
+ # Validate polling interval
54
+ if @polling.is_a?(Numeric) && @polling < 0
55
+ raise ArgumentError, "Cannot poll with non-positive interval: #{@polling}"
56
+ end
57
+
58
+ # Corresponds to Puppeteer's this.#world.taskManager.add(this)
59
+ @world.task_manager.add(self)
60
+
61
+ # Store timeout value and start timeout task
62
+ # Corresponds to Puppeteer's setTimeout(() => { void this.terminate(this.#timeoutError); }, options.timeout)
63
+ default_timeout = if @world.respond_to?(:default_timeout)
64
+ @world.default_timeout
65
+ elsif @world.respond_to?(:page)
66
+ @world.page.default_timeout
67
+ end
68
+ @timeout_ms = options.key?(:timeout) ? options[:timeout] : default_timeout
69
+ if @timeout_ms && @timeout_ms > 0
70
+ @timeout_error = Puppeteer::Bidi::TimeoutError.new(
71
+ "Waiting failed: #{@timeout_ms}ms exceeded"
72
+ )
73
+ # Start timeout task in background
74
+ @timeout_task = Async do |task|
75
+ task.sleep(@timeout_ms / 1000.0)
76
+ @timeout_task = nil # prevent stopping in terminate
77
+ terminate(@timeout_error)
78
+ end
79
+ end
80
+
81
+ # Start polling
82
+ # Corresponds to Puppeteer's void this.rerun()
83
+ rerun
84
+ end
85
+
86
+ # Get the result as a promise
87
+ # Corresponds to Puppeteer's get result(): Promise<HandleFor<T>>
88
+ # @return [Async::Promise] Promise that resolves to JSHandle
89
+ def result
90
+ @result
91
+ end
92
+
93
+ # Rerun the polling task
94
+ # Corresponds to Puppeteer's async rerun(): Promise<void>
95
+ def rerun
96
+ # Cancel previous rerun task if one is active
97
+ if (previous_task = @rerun_task)
98
+ @rerun_task = nil if @rerun_task.equal?(previous_task)
99
+ previous_task.stop
100
+ end
101
+
102
+ # Launch the rerun asynchronously so it can be cancelled like Puppeteer's AbortController
103
+ @rerun_task = Async do |task|
104
+ begin
105
+ perform_rerun
106
+ rescue Async::Stop
107
+ # Rerun was cancelled; poller cleanup happens in ensure block
108
+ ensure
109
+ @rerun_task = nil if @rerun_task.equal?(task)
110
+ end
111
+ end
112
+ end
113
+
114
+ # Terminate the task
115
+ # Corresponds to Puppeteer's async terminate(error?: Error): Promise<void>
116
+ # @param error [Exception, nil] Error to reject with
117
+ def terminate(error = nil)
118
+ # Corresponds to Puppeteer's this.#world.taskManager.delete(this)
119
+ @world.task_manager.delete(self)
120
+
121
+ # Note: this.#signal?.removeEventListener('abort', this.#onAbortSignal) is skipped
122
+ # AbortSignal is not implemented
123
+
124
+ # Clear timeout task
125
+ # Corresponds to Puppeteer's clearTimeout(this.#timeout)
126
+ if @timeout_task
127
+ @timeout_task.stop
128
+ @timeout_task = nil
129
+ end
130
+
131
+ # Reject result if not finished
132
+ # Corresponds to Puppeteer's if (error && !this.#result.finished()) { this.#result.reject(error); }
133
+ if error && !@result.resolved?
134
+ @result.reject(error)
135
+ end
136
+
137
+ # Stop and dispose poller
138
+ # Corresponds to Puppeteer's if (this.#poller) { ... }
139
+ if @poller
140
+ stop_and_dispose_poller(@poller)
141
+ @poller = nil
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ # Run a single rerun cycle. Mirrors the async rerun logic from Puppeteer.
148
+ def perform_rerun
149
+ poller = nil
150
+ schedule_rerun = false
151
+
152
+ begin
153
+ # Create Poller instance based on polling mode
154
+ # Corresponds to Puppeteer's switch (this.#polling)
155
+ poller = case @polling
156
+ when 'raf'
157
+ create_raf_poller
158
+ when 'mutation'
159
+ create_mutation_poller
160
+ else
161
+ create_interval_poller
162
+ end
163
+
164
+ @poller = poller
165
+
166
+ # Start the poller
167
+ # Corresponds to Puppeteer's await this.#poller.evaluate(poller => { void poller.start(); });
168
+ poller.evaluate('poller => { void poller.start(); }')
169
+
170
+ # Get the result
171
+ # Corresponds to Puppeteer's const result = await this.#poller.evaluateHandle(poller => { return poller.result(); });
172
+ # Note: poller.result() returns a Promise, so we need to await it
173
+ # evaluateHandle with awaitPromise: true will wait for the Promise to resolve
174
+ result_handle = poller.evaluate_handle('poller => { return poller.result(); }')
175
+
176
+ # Resolve the result
177
+ # Corresponds to Puppeteer's this.#result.resolve(result);
178
+ @result.resolve(result_handle)
179
+
180
+ # Terminate cleanly
181
+ # Corresponds to Puppeteer's await this.terminate();
182
+ terminate
183
+ rescue Async::Stop
184
+ # Propagate cancellation so caller can distinguish from regular errors
185
+ raise
186
+ rescue => error
187
+ # Check if this is a bad error
188
+ # Corresponds to Puppeteer's const badError = this.getBadError(error);
189
+ bad_error = get_bad_error(error)
190
+ if bad_error
191
+ # Corresponds to Puppeteer's this.#genericError.cause = badError;
192
+ @generic_error = StandardError.new(@generic_error.message)
193
+ @generic_error.set_backtrace([bad_error.message] + bad_error.backtrace)
194
+ # Corresponds to Puppeteer's await this.terminate(this.#genericError);
195
+ terminate(@generic_error)
196
+ else
197
+ schedule_rerun = true
198
+ end
199
+ # If badError is nil, it's a recoverable error and we don't terminate
200
+ # Puppeteer would rerun automatically via realm 'updated' event
201
+ ensure
202
+ if poller && @poller.equal?(poller)
203
+ stop_and_dispose_poller(poller)
204
+ @poller = nil
205
+ end
206
+ end
207
+
208
+ rerun if schedule_rerun && !@result.resolved?
209
+ end
210
+
211
+ # Create RAFPoller instance
212
+ # Corresponds to Puppeteer's evaluateHandle call for RAFPoller
213
+ def create_raf_poller
214
+ util_handle = @world.puppeteer_util
215
+
216
+ # Corresponds to Puppeteer's evaluateHandle with LazyArg
217
+ script = <<~JAVASCRIPT
218
+ ({RAFPoller, createFunction}, fn, ...args) => {
219
+ const fun = createFunction(fn);
220
+ return new RAFPoller(() => {
221
+ return fun(...args);
222
+ });
223
+ }
224
+ JAVASCRIPT
225
+
226
+ handle = @world.evaluate_handle(script, util_handle, @fn, *@args)
227
+ handle
228
+ end
229
+
230
+ # Create MutationPoller instance
231
+ # Corresponds to Puppeteer's evaluateHandle call for MutationPoller
232
+ def create_mutation_poller
233
+ util_handle = @world.puppeteer_util
234
+
235
+ # Corresponds to Puppeteer's evaluateHandle with LazyArg
236
+ script = <<~JAVASCRIPT
237
+ ({MutationPoller, createFunction}, root, fn, ...args) => {
238
+ const fun = createFunction(fn);
239
+ return new MutationPoller(() => {
240
+ return fun(...args);
241
+ }, root || document);
242
+ }
243
+ JAVASCRIPT
244
+
245
+ @world.evaluate_handle(script, util_handle, @root, @fn, *@args)
246
+ end
247
+
248
+ # Create IntervalPoller instance
249
+ # Corresponds to Puppeteer's evaluateHandle call for IntervalPoller
250
+ def create_interval_poller
251
+ util_handle = @world.puppeteer_util
252
+ interval = @polling.is_a?(Numeric) ? @polling : 100
253
+
254
+ # Corresponds to Puppeteer's evaluateHandle with LazyArg
255
+ script = <<~JAVASCRIPT
256
+ ({IntervalPoller, createFunction}, ms, fn, ...args) => {
257
+ const fun = createFunction(fn);
258
+ return new IntervalPoller(() => {
259
+ return fun(...args);
260
+ }, ms);
261
+ }
262
+ JAVASCRIPT
263
+
264
+ @world.evaluate_handle(script, util_handle, interval, @fn, *@args)
265
+ end
266
+
267
+ # Check if error should terminate task
268
+ # Corresponds to Puppeteer's getBadError(error: unknown): Error | undefined
269
+ # @param error [Exception] Error to check
270
+ # @return [Exception, nil] Error if it should terminate, nil if recoverable
271
+ def get_bad_error(error)
272
+ return nil unless error
273
+
274
+ error_message = error.message
275
+
276
+ # Frame detachment is fatal
277
+ # Corresponds to Puppeteer's error.message.includes('Execution context is not available in detached frame')
278
+ if error_message.include?('Execution context is not available in detached frame')
279
+ return StandardError.new('Waiting failed: Frame detached')
280
+ end
281
+
282
+ # These are recoverable (realm was destroyed/recreated)
283
+ # Corresponds to Puppeteer's error.message.includes('Execution context was destroyed')
284
+ return nil if error_message.include?('Execution context was destroyed')
285
+
286
+ # Corresponds to Puppeteer's error.message.includes('Cannot find context with specified id')
287
+ return nil if error_message.include?('Cannot find context with specified id')
288
+
289
+ # Corresponds to Puppeteer's error.message.includes('DiscardedBrowsingContextError')
290
+ return nil if error_message.include?('DiscardedBrowsingContextError')
291
+
292
+ # Recoverable when the browsing context is torn down during navigation.
293
+ return nil if error_message.include?('Browsing Context with id')
294
+ return nil if error_message.include?('no such frame')
295
+
296
+ # Happens when handles become invalid after realm/navigation changes.
297
+ return nil if error_message.include?('Unable to find an object reference for "handle"')
298
+
299
+ # All other errors are fatal
300
+ # Corresponds to Puppeteer's return error;
301
+ error
302
+ end
303
+
304
+ # Safely stop and dispose a poller handle, ignoring cleanup errors
305
+ def stop_and_dispose_poller(poller)
306
+ return unless poller
307
+
308
+ begin
309
+ poller.evaluate('async poller => { await poller.stop(); }')
310
+ rescue StandardError
311
+ # Ignore errors from stopping the poller
312
+ end
313
+
314
+ begin
315
+ poller.dispose
316
+ rescue StandardError
317
+ # Ignore dispose errors as they are low-level cleanup
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "puppeteer/bidi/version"
5
+ require "puppeteer/bidi/errors"
6
+
7
+ require "puppeteer/bidi/async_utils"
8
+ require "puppeteer/bidi/timeout_settings"
9
+ require "puppeteer/bidi/task_manager"
10
+ require "puppeteer/bidi/serializer"
11
+ require "puppeteer/bidi/deserializer"
12
+ require "puppeteer/bidi/injected_source"
13
+ require "puppeteer/bidi/lazy_arg"
14
+ require "puppeteer/bidi/js_handle"
15
+ require "puppeteer/bidi/keyboard"
16
+ require "puppeteer/bidi/mouse"
17
+ require "puppeteer/bidi/http_response"
18
+ require "puppeteer/bidi/element_handle"
19
+ require "puppeteer/bidi/query_handler"
20
+ require "puppeteer/bidi/wait_task"
21
+ require "puppeteer/bidi/realm"
22
+ require "puppeteer/bidi/frame"
23
+ require "puppeteer/bidi/file_chooser"
24
+ require "puppeteer/bidi/page"
25
+ require "puppeteer/bidi/target"
26
+ require "puppeteer/bidi/browser_context"
27
+ require "puppeteer/bidi/transport"
28
+ require "puppeteer/bidi/connection"
29
+ require "puppeteer/bidi/browser_launcher"
30
+ require "puppeteer/bidi/core"
31
+ require "puppeteer/bidi/browser"
32
+
33
+ module Puppeteer
34
+ module Bidi
35
+ # Launch a new browser instance
36
+ # @rbs **options: untyped
37
+ # @rbs return: Browser
38
+ def self.launch(**options)
39
+ Browser.launch(**options)
40
+ end
41
+
42
+ # Connect to an existing browser instance
43
+ # @rbs ws_endpoint: String
44
+ # @rbs return: Browser
45
+ def self.connect(ws_endpoint)
46
+ Browser.connect(ws_endpoint)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Script to update Puppeteer injected source from unpkg
5
+ # Usage: bundle exec ruby scripts/update_injected_source.rb [VERSION]
6
+ #
7
+ # Example:
8
+ # bundle exec ruby scripts/update_injected_source.rb 24.31.0
9
+
10
+ require 'net/http'
11
+ require 'json'
12
+
13
+ VERSION = ARGV[0] || '24.31.0'
14
+ URL = "https://unpkg.com/puppeteer-core@#{VERSION}/lib/esm/puppeteer/generated/injected.js"
15
+ OUTPUT_FILE = File.join(__dir__, '..', 'lib', 'puppeteer', 'bidi', 'injected.js')
16
+
17
+ puts "Downloading Puppeteer injected source..."
18
+ puts " URL: #{URL}"
19
+ puts " Version: #{VERSION}"
20
+ puts
21
+
22
+ # Download the file
23
+ uri = URI(URL)
24
+ response = Net::HTTP.get_response(uri)
25
+
26
+ unless response.is_a?(Net::HTTPSuccess)
27
+ puts "ERROR: Failed to download file (#{response.code} #{response.message})"
28
+ exit 1
29
+ end
30
+
31
+ content = response.body
32
+
33
+ # Extract the source string value from: export const source = "...";
34
+ match = content.match(/export const source = (\".*\");/m)
35
+ unless match
36
+ puts "ERROR: Could not find 'export const source =' in downloaded file"
37
+ exit 1
38
+ end
39
+
40
+ source_value = match[1]
41
+
42
+ # Parse JSON to unescape the string
43
+ begin
44
+ js_code = JSON.parse(source_value)
45
+ rescue JSON::ParserError => e
46
+ puts "ERROR: Failed to parse JSON: #{e.message}"
47
+ exit 1
48
+ end
49
+
50
+ # Write to output file
51
+ File.write(OUTPUT_FILE, js_code)
52
+
53
+ puts "✓ Successfully updated #{OUTPUT_FILE}"
54
+ puts " Size: #{js_code.length} bytes"
55
+ puts
56
+ puts "To verify the update:"
57
+ puts " bundle exec ruby -e \"require './lib/puppeteer/bidi/injected_source'; puts PUPPETEER_INJECTED_SOURCE[0..100]\""
@@ -0,0 +1,80 @@
1
+ # Generated from lib/puppeteer/bidi/browser.rb with RBS::Inline
2
+
3
+ module Puppeteer
4
+ module Bidi
5
+ # Browser represents a browser instance with BiDi connection
6
+ class Browser
7
+ attr_reader connection: Connection
8
+
9
+ attr_reader process: untyped
10
+
11
+ attr_reader default_browser_context: BrowserContext
12
+
13
+ # @rbs connection: Connection
14
+ # @rbs launcher: BrowserLauncher?
15
+ # @rbs return: Browser
16
+ def self.create: (connection: Connection, ?launcher: BrowserLauncher?) -> Browser
17
+
18
+ # @rbs connection: Connection
19
+ # @rbs launcher: BrowserLauncher?
20
+ # @rbs core_browser: Core::Browser
21
+ # @rbs session: Core::Session
22
+ # @rbs return: void
23
+ def initialize: (connection: Connection, launcher: BrowserLauncher?, core_browser: Core::Browser, session: Core::Session) -> void
24
+
25
+ # Launch a new Firefox browser instance
26
+ # @rbs **options: untyped
27
+ # @rbs return: Browser
28
+ def self.launch: (**untyped options) -> Browser
29
+
30
+ # Get BiDi session status
31
+ # @rbs return: untyped
32
+ def status: () -> untyped
33
+
34
+ # Create a new page (Puppeteer-like API)
35
+ # @rbs return: Page
36
+ def new_page: () -> Page
37
+
38
+ # Get all pages
39
+ # @rbs return: Array[Page]
40
+ def pages: () -> Array[Page]
41
+
42
+ # Register event handler
43
+ # @rbs event: String | Symbol
44
+ # @rbs &block: (untyped) -> void
45
+ # @rbs return: void
46
+ def on: (String | Symbol event) { (untyped) -> void } -> void
47
+
48
+ # Close the browser
49
+ # @rbs return: void
50
+ def close: () -> void
51
+
52
+ # @rbs return: bool
53
+ def closed?: () -> bool
54
+
55
+ # Wait until a target (top-level browsing context) satisfies the predicate.
56
+ # @rbs timeout: Integer?
57
+ # @rbs &predicate: (Target) -> boolish
58
+ # @rbs return: Target
59
+ def wait_for_target: (?timeout: Integer?) { (Target) -> boolish } -> Target
60
+
61
+ # Wait for browser process to exit
62
+ # @rbs return: void
63
+ def wait_for_exit: () -> void
64
+
65
+ private
66
+
67
+ # @rbs &block: (Target) -> void
68
+ # @rbs return: Enumerator[Target, void]
69
+ def each_target: () { (Target) -> void } -> Enumerator[Target, void]
70
+
71
+ # @rbs predicate: (Target) -> boolish
72
+ # @rbs return: Target?
73
+ def find_target: (Target predicate) -> Target?
74
+
75
+ # @rbs user_context: Core::UserContext
76
+ # @rbs return: BrowserContext?
77
+ def browser_context_for: (Core::UserContext user_context) -> BrowserContext?
78
+ end
79
+ end
80
+ end