puppeteer-bidi 0.0.1.beta9 → 0.0.1.beta10
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/CLAUDE/javascript_evaluation.md +1 -1
- data/CLAUDE/testing_strategy.md +1 -1
- data/README.md +149 -155
- data/lib/puppeteer/bidi/version.rb +1 -1
- data/lib/puppeteer/bidi.rb +36 -27
- data/sig/puppeteer/bidi.rbs +13 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a52c1dcb0d3a791f2754c968a543411c98051380796455da1f0e24404923aa8
|
|
4
|
+
data.tar.gz: cd9c3880fe2f8ab49201c681efd39a95a77b477e6445cd620e3cdb3e7a7014d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 516d9e6b86a99902e20db1fd43d7d8521fcf8ce6850d72cf253cc6ae5af97f5bd591e2138f0c733d2d96f89f606e45f3d8b6f015ca4730e7b8f75b306dcbd0df
|
|
7
|
+
data.tar.gz: a2314ab02834c9ded0abdf5ba85bbae5dcf2252c9dbeaaf81c9f2d6c332d16fc291a6b18d6e36bc470d0585f960fb4cac8aa090467329fa816b47e693df7b024
|
|
@@ -179,7 +179,7 @@ end
|
|
|
179
179
|
# In spec_helper.rb
|
|
180
180
|
config.before(:suite) do
|
|
181
181
|
if RSpec.configuration.files_to_run.any? { |f| f.include?('spec/integration') }
|
|
182
|
-
$shared_browser = Puppeteer::Bidi.
|
|
182
|
+
$shared_browser = Puppeteer::Bidi.launch_browser_instance(headless: headless_mode?)
|
|
183
183
|
$shared_test_server = TestServer::Server.new
|
|
184
184
|
$shared_test_server.start
|
|
185
185
|
end
|
data/CLAUDE/testing_strategy.md
CHANGED
|
@@ -74,7 +74,7 @@ end
|
|
|
74
74
|
# In spec_helper.rb
|
|
75
75
|
config.before(:suite) do
|
|
76
76
|
if RSpec.configuration.files_to_run.any? { |f| f.include?('spec/integration') }
|
|
77
|
-
$shared_browser = Puppeteer::Bidi.
|
|
77
|
+
$shared_browser = Puppeteer::Bidi.launch_browser_instance(headless: headless_mode?)
|
|
78
78
|
$shared_test_server = TestServer::Server.new
|
|
79
79
|
$shared_test_server.start
|
|
80
80
|
end
|
data/README.md
CHANGED
|
@@ -54,84 +54,81 @@ gem 'puppeteer-bidi'
|
|
|
54
54
|
require 'puppeteer/bidi'
|
|
55
55
|
|
|
56
56
|
# Launch Firefox with BiDi protocol
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
page.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
page.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
page.
|
|
122
|
-
|
|
57
|
+
Puppeteer::Bidi.launch(headless: false) do |browser|
|
|
58
|
+
# Create a new page
|
|
59
|
+
page = browser.new_page
|
|
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
|
|
123
131
|
end
|
|
124
|
-
|
|
125
|
-
# User input simulation
|
|
126
|
-
page.click('button#submit')
|
|
127
|
-
page.type('input[name="email"]', 'user@example.com', delay: 100)
|
|
128
|
-
page.focus('textarea')
|
|
129
|
-
|
|
130
|
-
# Close the page
|
|
131
|
-
page.close
|
|
132
|
-
|
|
133
|
-
# Close the browser
|
|
134
|
-
browser.close
|
|
135
132
|
```
|
|
136
133
|
|
|
137
134
|
### Low-Level BiDi API
|
|
@@ -140,30 +137,27 @@ browser.close
|
|
|
140
137
|
require 'puppeteer/bidi'
|
|
141
138
|
|
|
142
139
|
# Launch Firefox with BiDi protocol
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# Close the browser
|
|
160
|
-
browser.close
|
|
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)
|
|
154
|
+
end
|
|
161
155
|
```
|
|
162
156
|
|
|
163
157
|
### Launch Options
|
|
164
158
|
|
|
165
159
|
```ruby
|
|
166
|
-
|
|
160
|
+
Puppeteer::Bidi.launch(
|
|
167
161
|
headless: true, # Run in headless mode (default: true)
|
|
168
162
|
executable_path: '/path/to/firefox', # Path to Firefox executable (optional)
|
|
169
163
|
user_data_dir: '/path/to/profile', # User data directory (optional)
|
|
@@ -176,26 +170,26 @@ browser = Puppeteer::Bidi.launch(
|
|
|
176
170
|
```ruby
|
|
177
171
|
require 'puppeteer/bidi'
|
|
178
172
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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')
|
|
194
192
|
end
|
|
195
|
-
|
|
196
|
-
# Create context and navigate
|
|
197
|
-
result = browser.new_context(type: 'tab')
|
|
198
|
-
browser.navigate(context: result['context'], url: 'https://example.com')
|
|
199
193
|
```
|
|
200
194
|
|
|
201
195
|
### Connecting to Existing Browser
|
|
@@ -219,47 +213,47 @@ The Core layer provides a structured API over BiDi protocol:
|
|
|
219
213
|
require 'puppeteer/bidi'
|
|
220
214
|
|
|
221
215
|
# Launch browser and access connection
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
216
|
+
Puppeteer::Bidi.launch(headless: false) do |browser|
|
|
217
|
+
# Create Core layer objects
|
|
218
|
+
session_info = { 'sessionId' => 'default-session', 'capabilities' => {} }
|
|
219
|
+
session = Puppeteer::Bidi::Core::Session.new(browser.connection, session_info)
|
|
220
|
+
core_browser = Puppeteer::Bidi::Core::Browser.from(session)
|
|
221
|
+
session.browser = core_browser
|
|
222
|
+
|
|
223
|
+
# Get default user context
|
|
224
|
+
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
|
|
239
256
|
end
|
|
240
|
-
|
|
241
|
-
browsing_context.subscribe(['browsingContext.load'])
|
|
242
|
-
|
|
243
|
-
# Navigate
|
|
244
|
-
browsing_context.navigate('https://example.com', wait: 'complete')
|
|
245
|
-
|
|
246
|
-
# Evaluate JavaScript
|
|
247
|
-
result = browsing_context.default_realm.evaluate('document.title', true)
|
|
248
|
-
puts "Title: #{result['value']}"
|
|
249
|
-
|
|
250
|
-
# Take screenshot
|
|
251
|
-
image_data = browsing_context.capture_screenshot(format: 'png')
|
|
252
|
-
|
|
253
|
-
# Error handling with custom exceptions
|
|
254
|
-
begin
|
|
255
|
-
browsing_context.navigate('https://example.com')
|
|
256
|
-
rescue Puppeteer::Bidi::Core::BrowsingContextClosedError => e
|
|
257
|
-
puts "Context was closed: #{e.reason}"
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
# Clean up
|
|
261
|
-
browsing_context.close
|
|
262
|
-
core_browser.close
|
|
263
257
|
```
|
|
264
258
|
|
|
265
259
|
For more details on the Core layer, see `lib/puppeteer/bidi/core/README.md`.
|
data/lib/puppeteer/bidi.rb
CHANGED
|
@@ -33,42 +33,51 @@ require "puppeteer/bidi/browser"
|
|
|
33
33
|
module Puppeteer
|
|
34
34
|
module Bidi
|
|
35
35
|
# Launch a new browser instance
|
|
36
|
-
#
|
|
37
36
|
# @rbs executable_path: String? -- Path to browser executable
|
|
38
37
|
# @rbs user_data_dir: String? -- Path to user data directory
|
|
39
|
-
# @rbs headless: bool
|
|
38
|
+
# @rbs headless: bool -- Run browser in headless mode
|
|
40
39
|
# @rbs args: Array[String]? -- Additional browser arguments
|
|
41
40
|
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
#: (executable_path: String? , user_data_dir: String? , headless: bool , args: Array[String]? , timeout: Numeric?) { (Browser) -> untyped } -> untyped
|
|
41
|
+
# @rbs &block: (Browser) -> untyped -- Block to execute with the browser instance
|
|
42
|
+
# @rbs return: untyped
|
|
45
43
|
def self.launch(executable_path: nil, user_data_dir: nil, headless: true, args: nil, timeout: nil, &block)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
unless block
|
|
45
|
+
raise ArgumentError, 'Block is required for launch_with_sync'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Sync do
|
|
49
|
+
begin
|
|
50
|
+
browser = launch_browser_instance(
|
|
51
|
+
executable_path: executable_path,
|
|
52
|
+
user_data_dir: user_data_dir,
|
|
53
|
+
headless: headless,
|
|
54
|
+
args: args,
|
|
55
|
+
timeout: timeout
|
|
56
|
+
)
|
|
57
|
+
block.call(browser)
|
|
58
|
+
ensure
|
|
59
|
+
browser&.close
|
|
60
60
|
end
|
|
61
|
-
else
|
|
62
|
-
Browser.launch(
|
|
63
|
-
executable_path: executable_path,
|
|
64
|
-
user_data_dir: user_data_dir,
|
|
65
|
-
headless: headless,
|
|
66
|
-
args: args,
|
|
67
|
-
timeout: timeout
|
|
68
|
-
)
|
|
69
61
|
end
|
|
70
62
|
end
|
|
71
63
|
|
|
64
|
+
# Launch a new browser instance
|
|
65
|
+
# @rbs executable_path: String? -- Path to browser executable
|
|
66
|
+
# @rbs user_data_dir: String? -- Path to user data directory
|
|
67
|
+
# @rbs headless: bool -- Run browser in headless mode
|
|
68
|
+
# @rbs args: Array[String]? -- Additional browser arguments
|
|
69
|
+
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
70
|
+
# @rbs return: Browser -- Browser instance (if no block given)
|
|
71
|
+
def self.launch_browser_instance(executable_path: nil, user_data_dir: nil, headless: true, args: nil, timeout: nil)
|
|
72
|
+
Browser.launch(
|
|
73
|
+
executable_path: executable_path,
|
|
74
|
+
user_data_dir: user_data_dir,
|
|
75
|
+
headless: headless,
|
|
76
|
+
args: args,
|
|
77
|
+
timeout: timeout
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
72
81
|
# Connect to an existing browser instance
|
|
73
82
|
# @rbs ws_endpoint: String -- WebSocket endpoint URL
|
|
74
83
|
# @rbs return: Browser -- Browser instance
|
data/sig/puppeteer/bidi.rbs
CHANGED
|
@@ -3,17 +3,23 @@
|
|
|
3
3
|
module Puppeteer
|
|
4
4
|
module Bidi
|
|
5
5
|
# Launch a new browser instance
|
|
6
|
-
#
|
|
7
6
|
# @rbs executable_path: String? -- Path to browser executable
|
|
8
7
|
# @rbs user_data_dir: String? -- Path to user data directory
|
|
9
|
-
# @rbs headless: bool
|
|
8
|
+
# @rbs headless: bool -- Run browser in headless mode
|
|
10
9
|
# @rbs args: Array[String]? -- Additional browser arguments
|
|
11
10
|
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
# @rbs &block: (Browser) -> untyped -- Block to execute with the browser instance
|
|
12
|
+
# @rbs return: untyped
|
|
13
|
+
def self.launch: (?executable_path: String?, ?user_data_dir: String?, ?headless: bool, ?args: Array[String]?, ?timeout: Numeric?) { (Browser) -> untyped } -> untyped
|
|
14
|
+
|
|
15
|
+
# Launch a new browser instance
|
|
16
|
+
# @rbs executable_path: String? -- Path to browser executable
|
|
17
|
+
# @rbs user_data_dir: String? -- Path to user data directory
|
|
18
|
+
# @rbs headless: bool -- Run browser in headless mode
|
|
19
|
+
# @rbs args: Array[String]? -- Additional browser arguments
|
|
20
|
+
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
21
|
+
# @rbs return: Browser -- Browser instance (if no block given)
|
|
22
|
+
def self.launch_browser_instance: (?executable_path: String?, ?user_data_dir: String?, ?headless: bool, ?args: Array[String]?, ?timeout: Numeric?) -> Browser
|
|
17
23
|
|
|
18
24
|
# Connect to an existing browser instance
|
|
19
25
|
# @rbs ws_endpoint: String -- WebSocket endpoint URL
|