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.
- checksums.yaml +4 -4
- data/AGENTS.md +44 -0
- data/API_COVERAGE.md +345 -0
- data/CLAUDE/porting_puppeteer.md +20 -0
- data/CLAUDE.md +2 -1
- data/DEVELOPMENT.md +14 -0
- data/README.md +47 -415
- data/development/generate_api_coverage.rb +411 -0
- data/development/puppeteer_revision.txt +1 -0
- data/lib/puppeteer/bidi/browser.rb +118 -22
- data/lib/puppeteer/bidi/browser_context.rb +185 -2
- data/lib/puppeteer/bidi/connection.rb +16 -5
- data/lib/puppeteer/bidi/cookie_utils.rb +192 -0
- data/lib/puppeteer/bidi/core/browsing_context.rb +83 -40
- data/lib/puppeteer/bidi/core/realm.rb +6 -0
- data/lib/puppeteer/bidi/core/request.rb +79 -35
- data/lib/puppeteer/bidi/core/user_context.rb +5 -3
- data/lib/puppeteer/bidi/element_handle.rb +200 -8
- data/lib/puppeteer/bidi/errors.rb +4 -0
- data/lib/puppeteer/bidi/frame.rb +115 -11
- data/lib/puppeteer/bidi/http_request.rb +577 -0
- data/lib/puppeteer/bidi/http_response.rb +161 -10
- data/lib/puppeteer/bidi/locator.rb +792 -0
- data/lib/puppeteer/bidi/page.rb +859 -7
- data/lib/puppeteer/bidi/query_handler.rb +1 -1
- data/lib/puppeteer/bidi/version.rb +1 -1
- data/lib/puppeteer/bidi.rb +39 -6
- data/sig/puppeteer/bidi/browser.rbs +53 -6
- data/sig/puppeteer/bidi/browser_context.rbs +36 -0
- data/sig/puppeteer/bidi/cookie_utils.rbs +64 -0
- data/sig/puppeteer/bidi/core/browsing_context.rbs +16 -6
- data/sig/puppeteer/bidi/core/request.rbs +14 -11
- data/sig/puppeteer/bidi/core/user_context.rbs +2 -2
- data/sig/puppeteer/bidi/element_handle.rbs +28 -0
- data/sig/puppeteer/bidi/errors.rbs +4 -0
- data/sig/puppeteer/bidi/frame.rbs +17 -0
- data/sig/puppeteer/bidi/http_request.rbs +162 -0
- data/sig/puppeteer/bidi/http_response.rbs +67 -8
- data/sig/puppeteer/bidi/locator.rbs +267 -0
- data/sig/puppeteer/bidi/page.rbs +170 -0
- data/sig/puppeteer/bidi.rbs +15 -3
- metadata +12 -1
data/README.md
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
A Ruby port of [Puppeteer](https://pptr.dev/) using the WebDriver BiDi protocol for Firefox automation.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
1
|
+
[](https://badge.fury.io/rb/puppeteer-bidi)
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### Why BiDi?
|
|
3
|
+
# Puppeteer::BiDi
|
|
10
4
|
|
|
11
|
-
|
|
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
|
-
|
|
7
|
+
## At a glance
|
|
16
8
|
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
- **puppeteer-bidi** (this gem): Uses BiDi protocol → Best for Firefox automation
|
|
16
|
+
### How it compares
|
|
21
17
|
|
|
22
|
-
|
|
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
|
|
40
|
+
gem "puppeteer-bidi"
|
|
42
41
|
```
|
|
43
42
|
|
|
44
43
|
## Prerequisites
|
|
45
44
|
|
|
46
|
-
- Ruby 3.2 or higher (required by
|
|
47
|
-
- Firefox
|
|
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
|
-
##
|
|
48
|
+
## Quickstart
|
|
50
49
|
|
|
51
|
-
### High-
|
|
50
|
+
### High-level page API (recommended)
|
|
52
51
|
|
|
53
52
|
```ruby
|
|
54
|
-
require
|
|
53
|
+
require "puppeteer/bidi"
|
|
55
54
|
|
|
56
|
-
# Launch Firefox with BiDi
|
|
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
|
-
#
|
|
62
|
-
page.
|
|
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
|
|
67
|
+
### Launch options
|
|
158
68
|
|
|
159
69
|
```ruby
|
|
160
70
|
Puppeteer::Bidi.launch(
|
|
161
|
-
headless: true,
|
|
162
|
-
executable_path:
|
|
163
|
-
user_data_dir:
|
|
164
|
-
args: [
|
|
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
|
-
###
|
|
78
|
+
### Connect to an existing browser
|
|
169
79
|
|
|
170
80
|
```ruby
|
|
171
|
-
|
|
81
|
+
browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
|
|
82
|
+
ws_endpoint = browser.ws_endpoint
|
|
83
|
+
browser.disconnect
|
|
172
84
|
|
|
173
|
-
Puppeteer::Bidi.
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
96
|
+
require "puppeteer/bidi"
|
|
214
97
|
|
|
215
|
-
# Launch browser and access connection
|
|
216
98
|
Puppeteer::Bidi.launch(headless: false) do |browser|
|
|
217
|
-
|
|
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
|
-
|
|
227
|
-
browsing_context
|
|
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
|
-
|
|
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).
|