puppeteer-bidi 0.0.1.beta10 → 0.0.1

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +44 -0
  3. data/API_COVERAGE.md +345 -0
  4. data/CLAUDE/porting_puppeteer.md +20 -0
  5. data/CLAUDE.md +2 -1
  6. data/DEVELOPMENT.md +14 -0
  7. data/README.md +47 -415
  8. data/development/generate_api_coverage.rb +411 -0
  9. data/development/puppeteer_revision.txt +1 -0
  10. data/lib/puppeteer/bidi/browser.rb +118 -22
  11. data/lib/puppeteer/bidi/browser_context.rb +185 -2
  12. data/lib/puppeteer/bidi/connection.rb +16 -5
  13. data/lib/puppeteer/bidi/cookie_utils.rb +192 -0
  14. data/lib/puppeteer/bidi/core/browsing_context.rb +83 -40
  15. data/lib/puppeteer/bidi/core/realm.rb +6 -0
  16. data/lib/puppeteer/bidi/core/request.rb +79 -35
  17. data/lib/puppeteer/bidi/core/user_context.rb +5 -3
  18. data/lib/puppeteer/bidi/element_handle.rb +200 -8
  19. data/lib/puppeteer/bidi/errors.rb +4 -0
  20. data/lib/puppeteer/bidi/frame.rb +115 -11
  21. data/lib/puppeteer/bidi/http_request.rb +577 -0
  22. data/lib/puppeteer/bidi/http_response.rb +161 -10
  23. data/lib/puppeteer/bidi/locator.rb +792 -0
  24. data/lib/puppeteer/bidi/page.rb +859 -7
  25. data/lib/puppeteer/bidi/query_handler.rb +1 -1
  26. data/lib/puppeteer/bidi/version.rb +1 -1
  27. data/lib/puppeteer/bidi.rb +39 -6
  28. data/sig/puppeteer/bidi/browser.rbs +53 -6
  29. data/sig/puppeteer/bidi/browser_context.rbs +36 -0
  30. data/sig/puppeteer/bidi/cookie_utils.rbs +64 -0
  31. data/sig/puppeteer/bidi/core/browsing_context.rbs +16 -6
  32. data/sig/puppeteer/bidi/core/request.rbs +14 -11
  33. data/sig/puppeteer/bidi/core/user_context.rbs +2 -2
  34. data/sig/puppeteer/bidi/element_handle.rbs +28 -0
  35. data/sig/puppeteer/bidi/errors.rbs +4 -0
  36. data/sig/puppeteer/bidi/frame.rbs +17 -0
  37. data/sig/puppeteer/bidi/http_request.rbs +162 -0
  38. data/sig/puppeteer/bidi/http_response.rbs +67 -8
  39. data/sig/puppeteer/bidi/locator.rbs +267 -0
  40. data/sig/puppeteer/bidi/page.rbs +170 -0
  41. data/sig/puppeteer/bidi.rbs +15 -3
  42. metadata +12 -1
data/README.md CHANGED
@@ -1,25 +1,24 @@
1
- # Puppeteer::BiDi
2
-
3
- A Ruby port of [Puppeteer](https://pptr.dev/) using the WebDriver BiDi protocol for Firefox automation.
4
-
5
- ## Overview
1
+ [![Gem Version](https://badge.fury.io/rb/puppeteer-bidi.svg)](https://badge.fury.io/rb/puppeteer-bidi)
6
2
 
7
- `puppeteer-bidi` is a Ruby implementation of Puppeteer that leverages the [WebDriver BiDi protocol](https://w3c.github.io/webdriver-bidi/) to automate Firefox browsers. Unlike the existing [puppeteer-ruby](https://github.com/YusukeIwaki/puppeteer-ruby) gem which uses the Chrome DevTools Protocol (CDP), this gem focuses specifically on BiDi protocol support for cross-browser automation.
8
-
9
- ### Why BiDi?
3
+ # Puppeteer::BiDi
10
4
 
11
- - **Cross-browser compatibility**: BiDi is a W3C standard protocol designed to work across different browsers
12
- - **Firefox-first**: While CDP is Chrome-centric, BiDi provides better support for Firefox automation
13
- - **Future-proof**: BiDi represents the future direction of browser automation standards
5
+ Ruby bindings for the WebDriver BiDi-powered version of [Puppeteer](https://pptr.dev/), focused on modern Firefox automation with a familiar, synchronous API.
14
6
 
15
- ### Relationship with puppeteer-ruby
7
+ ## At a glance
16
8
 
17
- This gem complements the existing `puppeteer-ruby` ecosystem:
9
+ - **Standards-first**: Uses the [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) protocol so scripts stay compatible as browsers converge on the standard.
10
+ - **Firefox-native**: Optimized for Firefox (Nightly recommended), including capabilities that are not available through CDP.
11
+ - **Async-powered, less flaky**: Fiber-backed concurrency instead of threads makes the synchronous-feeling API resilient against timing flakiness common in UI tests.
12
+ - **Typed-friendly Ruby**: Ships RBS signatures so editors and type checkers can guide you while coding.
13
+ - **Puppeteer ergonomics in Ruby**: Automatic waits and expressive helpers for navigation, selectors, and input.
14
+ - **Full-browser automation**: Screenshots, network interception, multi-context control, and event subscriptions without extra drivers.
18
15
 
19
- - **puppeteer-ruby**: Uses CDP (Chrome DevTools Protocol) → Best for Chrome/Chromium automation
20
- - **puppeteer-bidi** (this gem): Uses BiDi protocol → Best for Firefox automation
16
+ ### How it compares
21
17
 
22
- This gem ports only the BiDi-related portions of Puppeteer to Ruby, intentionally excluding CDP implementations.
18
+ - **Capybara (Ruby)**: Great for acceptance tests and Rack/Test drivers, but headless browser control depends on adapters like Selenium or Cuprite. Puppeteer::BiDi ships a single, consistent API aimed at end-to-end browser automation (screenshots, tracing, network control) rather than HTML-only flows.
19
+ - **Selenium WebDriver (Ruby)**: Broad browser coverage via the classic WebDriver protocol. Puppeteer::BiDi trades some breadth for higher-level defaults (auto-waiting, rich screenshot helpers) and BiDi-first features like network interception without extra extensions.
20
+ - **Playwright / Puppeteer (Node.js)**: Similar mental model and API surface, but require JavaScript. Puppeteer::BiDi keeps that productivity while fitting naturally into Ruby apps and test suites.
21
+ - **puppeteer-ruby (CDP)**: Use `puppeteer-ruby` for Chrome/Chromium via the DevTools Protocol. Puppeteer::BiDi intentionally focuses on the BiDi path.
23
22
 
24
23
  ## Installation
25
24
 
@@ -38,452 +37,85 @@ gem install puppeteer-bidi
38
37
  Or add this line to your application's Gemfile:
39
38
 
40
39
  ```ruby
41
- gem 'puppeteer-bidi'
40
+ gem "puppeteer-bidi"
42
41
  ```
43
42
 
44
43
  ## Prerequisites
45
44
 
46
- - Ruby 3.2 or higher (required by socketry/async dependency)
47
- - Firefox browser with BiDi support
45
+ - Ruby 3.2 or higher (required by the `async` dependency)
46
+ - Firefox with BiDi support (Nightly recommended for the newest protocol features)
48
47
 
49
- ## Usage
48
+ ## Quickstart
50
49
 
51
- ### High-Level Page API (Recommended)
50
+ ### High-level page API (recommended)
52
51
 
53
52
  ```ruby
54
- require 'puppeteer/bidi'
53
+ require "puppeteer/bidi"
55
54
 
56
- # Launch Firefox with BiDi protocol
55
+ # Launch Firefox with BiDi
57
56
  Puppeteer::Bidi.launch(headless: false) do |browser|
58
- # Create a new page
59
57
  page = browser.new_page
58
+ page.goto("https://example.com")
59
+ puts page.title
60
60
 
61
- # Set viewport size
62
- page.set_viewport(width: 1280, height: 720)
63
-
64
- # Navigate to a URL
65
- page.goto('https://example.com')
66
-
67
- # Take a screenshot
68
- page.screenshot(path: 'screenshot.png')
69
-
70
- # Take a full page screenshot
71
- page.screenshot(path: 'fullpage.png', full_page: true)
72
-
73
- # Screenshot with clipping
74
- page.screenshot(
75
- path: 'clip.png',
76
- clip: { x: 0, y: 0, width: 100, height: 100 }
77
- )
78
-
79
- # Evaluate JavaScript expressions
80
- title = page.evaluate('document.title')
81
- puts "Page title: #{title}"
82
-
83
- # Evaluate JavaScript functions with arguments
84
- sum = page.evaluate('(a, b) => a + b', 3, 4)
85
- puts "Sum: #{sum}" # => 7
86
-
87
- # Access frame and evaluate
88
- frame = page.main_frame
89
- result = frame.evaluate('() => window.innerWidth')
90
-
91
- # Query selectors
92
- section = page.query_selector('section')
93
- divs = page.query_selector_all('div')
94
-
95
- # Evaluate on selectors (convenience methods)
96
- # Equivalent to Puppeteer's $eval and $$eval
97
- id = page.eval_on_selector('section', 'e => e.id')
98
- count = page.eval_on_selector_all('div', 'divs => divs.length')
99
-
100
- # Set page content
101
- page.set_content('<h1>Hello, World!</h1>')
102
-
103
- # Wait for navigation (Async/Fiber-based, no race conditions)
104
- # Block pattern - executes code and waits for resulting navigation
105
- response = page.wait_for_navigation do
106
- page.click('a#navigation-link')
107
- end
108
-
109
- # Wait for fragment navigation (#hash changes)
110
- page.wait_for_navigation do
111
- page.click('a[href="#section"]')
112
- end # => nil (fragment navigation returns nil)
113
-
114
- # Wait for History API navigation
115
- page.wait_for_navigation do
116
- page.evaluate('history.pushState({}, "", "/new-url")')
117
- end # => nil (History API returns nil)
118
-
119
- # Wait with different conditions
120
- page.wait_for_navigation(wait_until: 'domcontentloaded') do
121
- page.click('a')
122
- end
123
-
124
- # User input simulation
125
- page.click('button#submit')
126
- page.type('input[name="email"]', 'user@example.com', delay: 100)
127
- page.focus('textarea')
128
-
129
- # Close the page
130
- page.close
131
- end
132
- ```
133
-
134
- ### Low-Level BiDi API
135
-
136
- ```ruby
137
- require 'puppeteer/bidi'
138
-
139
- # Launch Firefox with BiDi protocol
140
- Puppeteer::Bidi.launch(headless: false) do |browser|
141
- # Create a new browsing context (tab)
142
- result = browser.new_context(type: 'tab')
143
- context_id = result['context']
144
-
145
- # Navigate to a URL
146
- browser.navigate(
147
- context: context_id,
148
- url: 'https://example.com',
149
- wait: 'complete'
150
- )
151
-
152
- # Close the browsing context
153
- browser.close_context(context_id)
61
+ page.click("button#get-started")
62
+ page.type("input[name='email']", "user@example.com")
63
+ page.screenshot(path: "hero.png", full_page: true)
154
64
  end
155
65
  ```
156
66
 
157
- ### Launch Options
67
+ ### Launch options
158
68
 
159
69
  ```ruby
160
70
  Puppeteer::Bidi.launch(
161
- headless: true, # Run in headless mode (default: true)
162
- executable_path: '/path/to/firefox', # Path to Firefox executable (optional)
163
- user_data_dir: '/path/to/profile', # User data directory (optional)
164
- args: ['--width=1280', '--height=720'] # Additional Firefox arguments
71
+ headless: true,
72
+ executable_path: "/path/to/firefox",
73
+ user_data_dir: "/path/to/profile",
74
+ args: ["--width=1280", "--height=720"]
165
75
  )
166
76
  ```
167
77
 
168
- ### Event Handling
78
+ ### Connect to an existing browser
169
79
 
170
80
  ```ruby
171
- require 'puppeteer/bidi'
81
+ browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
82
+ ws_endpoint = browser.ws_endpoint
83
+ browser.disconnect
172
84
 
173
- Puppeteer::Bidi.launch(headless: false) do |browser|
174
- # Subscribe to BiDi events
175
- browser.subscribe([
176
- 'browsingContext.navigationStarted',
177
- 'browsingContext.navigationComplete'
178
- ])
179
-
180
- # Register event handlers
181
- browser.on('browsingContext.navigationStarted') do |params|
182
- puts "Navigation started: #{params['url']}"
183
- end
184
-
185
- browser.on('browsingContext.navigationComplete') do |params|
186
- puts "Navigation completed: #{params['url']}"
187
- end
188
-
189
- # Create context and navigate
190
- result = browser.new_context(type: 'tab')
191
- browser.navigate(context: result['context'], url: 'https://example.com')
85
+ Puppeteer::Bidi.connect(ws_endpoint) do |session|
86
+ puts "Reconnected to browser"
87
+ session.new_page.goto("https://example.com")
192
88
  end
193
89
  ```
194
90
 
195
- ### Connecting to Existing Browser
196
-
197
- ```ruby
198
- # Connect to an already running Firefox instance with BiDi
199
- browser = Puppeteer::Bidi.connect('ws://localhost:9222/session')
91
+ ### Core layer (advanced)
200
92
 
201
- # Use the browser
202
- status = browser.status
203
- puts "Connected to browser: #{status.inspect}"
204
-
205
- browser.close
206
- ```
207
-
208
- ### Using Core Layer (Advanced)
209
-
210
- The Core layer provides a structured API over BiDi protocol:
93
+ Need protocol-level control? Build on the Core layer for explicit BiDi calls while keeping a structured API:
211
94
 
212
95
  ```ruby
213
- require 'puppeteer/bidi'
96
+ require "puppeteer/bidi"
214
97
 
215
- # Launch browser and access connection
216
98
  Puppeteer::Bidi.launch(headless: false) do |browser|
217
- # Create Core layer objects
218
- session_info = { 'sessionId' => 'default-session', 'capabilities' => {} }
99
+ session_info = { "sessionId" => "default-session", "capabilities" => {} }
219
100
  session = Puppeteer::Bidi::Core::Session.new(browser.connection, session_info)
220
101
  core_browser = Puppeteer::Bidi::Core::Browser.from(session)
221
102
  session.browser = core_browser
222
103
 
223
- # Get default user context
224
104
  context = core_browser.default_user_context
225
-
226
- # Create browsing context with Core API
227
- browsing_context = context.create_browsing_context('tab')
228
-
229
- # Subscribe to events
230
- browsing_context.on(:load) do
231
- puts "Page loaded!"
232
- end
233
-
234
- browsing_context.subscribe(['browsingContext.load'])
235
-
236
- # Navigate
237
- browsing_context.navigate('https://example.com', wait: 'complete')
238
-
239
- # Evaluate JavaScript
240
- result = browsing_context.default_realm.evaluate('document.title', true)
241
- puts "Title: #{result['value']}"
242
-
243
- # Take screenshot
244
- image_data = browsing_context.capture_screenshot(format: 'png')
245
-
246
- # Error handling with custom exceptions
247
- begin
248
- browsing_context.navigate('https://example.com')
249
- rescue Puppeteer::Bidi::Core::BrowsingContextClosedError => e
250
- puts "Context was closed: #{e.reason}"
251
- end
252
-
253
- # Clean up
254
- browsing_context.close
255
- core_browser.close
105
+ browsing_context = context.create_browsing_context("tab")
106
+ browsing_context.subscribe(["browsingContext.load"])
107
+ browsing_context.navigate("https://example.com", wait: "complete")
256
108
  end
257
109
  ```
258
110
 
259
- For more details on the Core layer, see `lib/puppeteer/bidi/core/README.md`.
260
-
261
111
  For more examples, see the [examples](examples/) directory and integration tests in [spec/integration/](spec/integration/).
262
112
 
263
- ## Testing
264
-
265
- ### Running Tests
266
-
267
- ```bash
268
- # Run all tests
269
- bundle exec rspec
270
-
271
- # Run integration tests (launches actual Firefox browser)
272
- bundle exec rspec spec/integration/
273
-
274
- # Run evaluation tests (23 examples, ~7 seconds)
275
- bundle exec rspec spec/integration/evaluation_spec.rb
276
-
277
- # Run screenshot tests (12 examples, ~8 seconds)
278
- bundle exec rspec spec/integration/screenshot_spec.rb
279
-
280
- # Run all integration tests (35 examples, ~10 seconds)
281
- bundle exec rspec spec/integration/evaluation_spec.rb spec/integration/screenshot_spec.rb
282
-
283
- # Run in non-headless mode for debugging
284
- HEADLESS=false bundle exec rspec spec/integration/
285
- ```
286
-
287
- ### Integration Tests
288
-
289
- Integration tests in `spec/integration/` demonstrate real-world usage by launching Firefox and performing browser automation tasks. These tests are useful for:
290
-
291
- - Verifying end-to-end functionality
292
- - Learning by example
293
- - Ensuring browser compatibility
294
-
295
- **Performance Note**: Integration tests are optimized to reuse a single browser instance across all tests (~19x faster than launching per test). Each test gets a fresh page (tab) for proper isolation.
296
-
297
- #### Test Coverage
298
-
299
- **Integration Tests**: 136 examples covering end-to-end functionality
300
-
301
- - **Evaluation Tests** (`evaluation_spec.rb`): 23 tests ported from Puppeteer
302
- - JavaScript expression and function evaluation
303
- - Argument serialization (numbers, arrays, objects, special values)
304
- - Result deserialization (NaN, Infinity, -0, Maps)
305
- - Exception handling and thrown values
306
- - IIFE (Immediately Invoked Function Expression) support
307
- - Frame.evaluate functionality
308
-
309
- - **Screenshot Tests** (`screenshot_spec.rb`): 12 tests ported from Puppeteer
310
- - Basic screenshots and clipping regions
311
- - Full page screenshots with viewport management
312
- - Parallel execution across single/multiple pages
313
- - Retina display compatibility
314
- - Viewport restoration
315
- - All tests use golden image comparison with tolerance for cross-platform compatibility
316
-
317
- - **Navigation Tests** (`navigation_spec.rb`): 8 tests ported from Puppeteer
318
- - Full page navigation with HTTPResponse
319
- - Fragment navigation (#hash changes)
320
- - History API navigation (pushState, replaceState, back, forward)
321
- - Multiple wait conditions (load, domcontentloaded)
322
- - Async/Fiber-based concurrent navigation waiting
323
-
324
- - **Click Tests** (`click_spec.rb`): 20 tests ported from Puppeteer
325
- - Element clicking (buttons, links, SVG, checkboxes)
326
- - Scrolling and viewport handling
327
- - Wrapped element clicks (multi-line text)
328
- - Obscured element detection
329
- - Rotated element clicks
330
- - Mouse button variations (left, right, middle)
331
- - Click counts (single, double, triple)
332
-
333
- - **Keyboard Tests** (`keyboard_spec.rb`): Tests for keyboard input simulation
334
- - Text typing with customizable delay
335
- - Special key presses
336
- - Key combinations
337
-
338
- ## Project Status
339
-
340
- This project is in early development. The API may change as the implementation progresses.
341
-
342
- ### Implemented Features
343
-
344
- #### High-Level Page API (`lib/puppeteer/bidi/`)
345
- Puppeteer-compatible API for browser automation:
346
-
347
- - ✅ **Browser**: Browser instance management
348
- - ✅ **BrowserContext**: Isolated browsing sessions
349
- - ✅ **Page**: High-level page automation
350
- - ✅ Navigation (`goto`, `set_content`, `wait_for_navigation`)
351
- - ✅ Wait for full page navigation, fragment navigation (#hash), and History API (pushState/replaceState)
352
- - ✅ Async/Fiber-based concurrency (no race conditions, Thread-safe)
353
- - ✅ Multiple wait conditions (`load`, `domcontentloaded`)
354
- - ✅ JavaScript evaluation (`evaluate`, `evaluate_handle`) with functions, arguments, and IIFE support
355
- - ✅ Element querying (`query_selector`, `query_selector_all`)
356
- - ✅ Selector evaluation (`eval_on_selector`, `eval_on_selector_all`) - Ruby equivalents of Puppeteer's `$eval` and `$$eval`
357
- - ✅ User input (`click`, `type`, `focus`)
358
- - ✅ Mouse operations (click with offset, double-click, context menu, middle-click)
359
- - ✅ Keyboard operations (type with delay, press, key combinations)
360
- - ✅ Screenshots (basic, clipping, full page, parallel)
361
- - ✅ Viewport management with automatic restoration
362
- - ✅ Page state queries (`title`, `url`, `viewport`)
363
- - ✅ Frame access (`main_frame`, `focused_frame`)
364
- - ✅ **Frame**: Frame-level operations
365
- - ✅ JavaScript evaluation with full feature parity to Page
366
- - ✅ Element querying and selector evaluation
367
- - ✅ Navigation waiting (`wait_for_navigation`)
368
- - ✅ User input (`click`, `type`, `focus`)
369
- - ✅ **JSHandle & ElementHandle**: JavaScript object references
370
- - ✅ Handle creation, disposal, and property access
371
- - ✅ Element operations (click, bounding box, scroll into view)
372
- - ✅ Type-safe custom exceptions for error handling
373
- - ✅ **Mouse & Keyboard**: User input simulation
374
- - ✅ Mouse clicks (single, double, triple) with customizable delay
375
- - ✅ Mouse movements and button states
376
- - ✅ Keyboard typing with per-character delay
377
- - ✅ Special key support
378
-
379
- #### Screenshot Features
380
- Comprehensive screenshot functionality with 12 passing tests:
381
-
382
- - ✅ Basic screenshots
383
- - ✅ Clipping regions (document/viewport coordinates)
384
- - ✅ Full page screenshots (with/without viewport expansion)
385
- - ✅ Thread-safe parallel execution (single/multiple pages)
386
- - ✅ Retina display compatibility (odd-sized clips)
387
- - ✅ Automatic viewport restoration
388
- - ✅ Base64 encoding
389
-
390
- #### Foundation Layer
391
- - ✅ Browser launching with Firefox
392
- - ✅ BiDi protocol connection (WebSocket-based)
393
- - ✅ WebSocket transport with async/await support
394
- - ✅ Command execution with timeout
395
- - ✅ Event subscription and handling
396
-
397
- #### Core Layer (`lib/puppeteer/bidi/core/`)
398
- A low-level object-oriented abstraction over the WebDriver BiDi protocol:
399
-
400
- - ✅ **Infrastructure**: EventEmitter, Disposable, Custom Exceptions
401
- - ✅ **Session Management**: BiDi session lifecycle
402
- - ✅ **Browser & Contexts**: Browser, UserContext, BrowsingContext
403
- - ✅ **Navigation**: Navigation lifecycle tracking
404
- - ✅ **JavaScript Execution**: Realm classes (Window, Worker)
405
- - ✅ **Network**: Request/Response management with interception
406
- - ✅ **User Interaction**: UserPrompt handling (alert/confirm/prompt)
407
-
408
- #### BiDi Operations
409
- - ✅ Browsing context management (create/close tabs/windows)
410
- - ✅ Page navigation with wait conditions
411
- - ✅ JavaScript evaluation (`script.evaluate`, `script.callFunction`)
412
- - ✅ Expression evaluation
413
- - ✅ Function calls with argument serialization
414
- - ✅ Result deserialization (numbers, strings, arrays, objects, Maps, special values)
415
- - ✅ Exception handling and propagation
416
- - ✅ IIFE support
417
- - ✅ Screenshot capture
418
- - ✅ PDF generation
419
- - ✅ Cookie management (get/set/delete)
420
- - ✅ Network request interception
421
- - ✅ Geolocation and timezone emulation
422
-
423
- ### Custom Exception Handling
424
-
425
- The gem provides type-safe custom exceptions for better error handling:
426
-
427
- ```ruby
428
- begin
429
- page.eval_on_selector('.missing', 'e => e.id')
430
- rescue Puppeteer::Bidi::SelectorNotFoundError => e
431
- puts "Selector '#{e.selector}' not found"
432
- rescue Puppeteer::Bidi::JSHandleDisposedError
433
- puts "Handle was disposed"
434
- rescue Puppeteer::Bidi::PageClosedError
435
- puts "Page is closed"
436
- rescue Puppeteer::Bidi::FrameDetachedError
437
- puts "Frame was detached"
438
- end
439
- ```
440
-
441
- Available custom exceptions:
442
- - `JSHandleDisposedError` - JSHandle or ElementHandle is disposed
443
- - `PageClosedError` - Page is closed
444
- - `FrameDetachedError` - Frame is detached
445
- - `SelectorNotFoundError` - Selector doesn't match any elements (includes `selector` attribute)
446
-
447
- ### Planned Features
448
-
449
- - File upload handling
450
- - Enhanced network monitoring (NetworkManager)
451
- - Frame management (FrameManager with iframe support)
452
- - Service Worker support
453
- - Dialog handling (alert, confirm, prompt)
454
- - Advanced navigation options (referrer, AbortSignal support)
455
-
456
- ## Comparison with Puppeteer (Node.js)
457
-
458
- This gem aims to provide a Ruby-friendly API that closely mirrors the original Puppeteer API while following Ruby conventions and idioms.
459
-
460
- ## Development
461
-
462
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
463
-
464
- To install this gem onto your local machine, run `bundle exec rake install`.
465
-
466
- ### Type Annotations
467
-
468
- This gem includes RBS type definitions generated by [rbs-inline](https://github.com/soutaro/rbs-inline). Type checking is performed with [Steep](https://github.com/soutaro/steep).
469
-
470
- ```bash
471
- bundle exec rake rbs # Generate RBS files
472
- bundle exec steep check # Run type checker
473
- ```
474
-
475
- For development guidelines on type annotations, see `CLAUDE.md`.
113
+ For development and testing commands, see [DEVELOPMENT.md](DEVELOPMENT.md).
476
114
 
477
115
  ## Contributing
478
116
 
479
117
  Bug reports and pull requests are welcome on GitHub at https://github.com/YusukeIwaki/puppeteer-bidi.
480
118
 
481
- 1. Fork the repository
482
- 2. Create your feature branch (`git checkout -b my-new-feature`)
483
- 3. Commit your changes (`git commit -am 'Add some feature'`)
484
- 4. Push to the branch (`git push origin my-new-feature`)
485
- 5. Create a new Pull Request
486
-
487
119
  ## License
488
120
 
489
121
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).