puppeteer-bidi 0.0.3.beta1 → 0.0.3.beta2
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/lib/puppeteer/bidi/browser.rb +15 -0
- data/lib/puppeteer/bidi/version.rb +1 -1
- data/sig/puppeteer/bidi/browser.rbs +3 -0
- metadata +1 -24
- data/CLAUDE/README.md +0 -158
- data/CLAUDE/async_programming.md +0 -158
- data/CLAUDE/click_implementation.md +0 -340
- data/CLAUDE/core_layer_gotchas.md +0 -136
- data/CLAUDE/error_handling.md +0 -232
- data/CLAUDE/expose_function_implementation.md +0 -271
- data/CLAUDE/file_chooser.md +0 -95
- data/CLAUDE/frame_architecture.md +0 -346
- data/CLAUDE/javascript_evaluation.md +0 -341
- data/CLAUDE/jshandle_implementation.md +0 -505
- data/CLAUDE/keyboard_implementation.md +0 -250
- data/CLAUDE/mouse_implementation.md +0 -140
- data/CLAUDE/navigation_waiting.md +0 -234
- data/CLAUDE/porting_puppeteer.md +0 -234
- data/CLAUDE/query_handler.md +0 -194
- data/CLAUDE/reactor_runner.md +0 -111
- data/CLAUDE/rspec_pending_vs_skip.md +0 -262
- data/CLAUDE/selector_evaluation.md +0 -198
- data/CLAUDE/test_server_routes.md +0 -263
- data/CLAUDE/testing_strategy.md +0 -236
- data/CLAUDE/two_layer_architecture.md +0 -180
- data/CLAUDE/wrapped_element_click.md +0 -247
- data/CLAUDE.md +0 -238
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
# RSpec: pending vs skip
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
RSpec provides two mechanisms for handling tests that cannot or should not run: `pending` and `skip`. Understanding when to use each is critical for documenting browser limitations and future work.
|
|
6
|
-
|
|
7
|
-
## Difference
|
|
8
|
-
|
|
9
|
-
### skip
|
|
10
|
-
|
|
11
|
-
**Completely skips the test** - does not run any code:
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
it 'should work' do
|
|
15
|
-
skip 'feature not implemented'
|
|
16
|
-
|
|
17
|
-
# This code NEVER runs
|
|
18
|
-
page.do_something
|
|
19
|
-
expect(result).to be_truthy
|
|
20
|
-
end
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Output**: Test marked as skipped, no execution, no error trace.
|
|
24
|
-
|
|
25
|
-
### pending
|
|
26
|
-
|
|
27
|
-
**Runs the test** and expects it to fail:
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
it 'should work' do
|
|
31
|
-
pending 'feature not implemented'
|
|
32
|
-
|
|
33
|
-
# This code RUNS and is expected to fail
|
|
34
|
-
page.do_something # Raises error
|
|
35
|
-
expect(result).to be_truthy
|
|
36
|
-
end
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**Output**: Test marked as pending with full error trace showing exactly what failed.
|
|
40
|
-
|
|
41
|
-
## When to Use Each
|
|
42
|
-
|
|
43
|
-
### Use `pending` for:
|
|
44
|
-
|
|
45
|
-
1. **Browser limitations** - Features not yet supported by Firefox BiDi
|
|
46
|
-
2. **Known failures** - Code exists but fails due to external issues
|
|
47
|
-
3. **Documentation** - Want to show error trace to document what's missing
|
|
48
|
-
|
|
49
|
-
### Use `skip` for:
|
|
50
|
-
|
|
51
|
-
1. **Unimplemented features** - Code doesn't exist yet
|
|
52
|
-
2. **Environment issues** - Test requires specific setup not available
|
|
53
|
-
3. **Temporary exclusion** - Test is broken and needs fixing
|
|
54
|
-
|
|
55
|
-
## Firefox BiDi Limitations
|
|
56
|
-
|
|
57
|
-
For features that exist in BiDi spec but not yet implemented in Firefox, use `pending`:
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
describe 'Page.setJavaScriptEnabled' do
|
|
61
|
-
it 'should work' do
|
|
62
|
-
# Pending: Firefox does not yet support emulation.setScriptingEnabled BiDi command
|
|
63
|
-
pending 'emulation.setScriptingEnabled not supported by Firefox yet'
|
|
64
|
-
|
|
65
|
-
with_test_state do |page:, **|
|
|
66
|
-
page.set_javascript_enabled(false)
|
|
67
|
-
expect(page.javascript_enabled?).to be false
|
|
68
|
-
|
|
69
|
-
page.goto('data:text/html, <script>var something = "forbidden"</script>')
|
|
70
|
-
|
|
71
|
-
error = nil
|
|
72
|
-
begin
|
|
73
|
-
page.evaluate('something')
|
|
74
|
-
rescue => e
|
|
75
|
-
error = e
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
expect(error).not_to be_nil
|
|
79
|
-
expect(error.message).to include('something is not defined')
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Why pending, not skip**:
|
|
86
|
-
- Code path exists (`page.set_javascript_enabled`)
|
|
87
|
-
- BiDi command exists in spec (`emulation.setScriptingEnabled`)
|
|
88
|
-
- Firefox just hasn't implemented it yet
|
|
89
|
-
- Running the test shows exactly what error Firefox returns
|
|
90
|
-
|
|
91
|
-
## Output Comparison
|
|
92
|
-
|
|
93
|
-
### With `skip`
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
Page
|
|
97
|
-
Page.setJavaScriptEnabled
|
|
98
|
-
should work (SKIPPED)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
No error information, no way to know what's missing.
|
|
102
|
-
|
|
103
|
-
### With `pending`
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
Page
|
|
107
|
-
Page.setJavaScriptEnabled
|
|
108
|
-
should work (PENDING: emulation.setScriptingEnabled not supported by Firefox yet)
|
|
109
|
-
|
|
110
|
-
Pending: (Failures listed here are expected and do not affect your suite's status)
|
|
111
|
-
|
|
112
|
-
1) Page Page.setJavaScriptEnabled should work
|
|
113
|
-
# emulation.setScriptingEnabled not supported by Firefox yet
|
|
114
|
-
Failure/Error: raise ProtocolError, "BiDi error (#{method}): #{result['error']['message']}"
|
|
115
|
-
|
|
116
|
-
Puppeteer::Bidi::Connection::ProtocolError:
|
|
117
|
-
BiDi error (emulation.setScriptingEnabled):
|
|
118
|
-
# ./lib/puppeteer/bidi/connection.rb:71:in 'send_command'
|
|
119
|
-
# ./lib/puppeteer/bidi/core/browsing_context.rb:331:in 'set_javascript_enabled'
|
|
120
|
-
# ./lib/puppeteer/bidi/page.rb:313:in 'set_javascript_enabled'
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Full error trace shows:
|
|
124
|
-
- Which BiDi command failed
|
|
125
|
-
- Error message from Firefox
|
|
126
|
-
- Complete stack trace
|
|
127
|
-
- Where in our code it failed
|
|
128
|
-
|
|
129
|
-
## Implementation Pattern
|
|
130
|
-
|
|
131
|
-
### Before (Incorrect - Using skip in before block)
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
134
|
-
describe 'Page.setJavaScriptEnabled' do
|
|
135
|
-
before do
|
|
136
|
-
skip 'emulation.setScriptingEnabled not supported by Firefox yet'
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
it 'should work' do
|
|
140
|
-
# Never runs
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
it 'setInterval should pause' do
|
|
144
|
-
# Never runs
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**Problems**:
|
|
150
|
-
- Tests don't run at all
|
|
151
|
-
- No error information
|
|
152
|
-
- Not clear which BiDi command is missing
|
|
153
|
-
|
|
154
|
-
### After (Correct - Using pending in individual tests)
|
|
155
|
-
|
|
156
|
-
```ruby
|
|
157
|
-
describe 'Page.setJavaScriptEnabled' do
|
|
158
|
-
it 'should work' do
|
|
159
|
-
pending 'emulation.setScriptingEnabled not supported by Firefox yet'
|
|
160
|
-
|
|
161
|
-
with_test_state do |page:, **|
|
|
162
|
-
# Test code runs and fails with proper error trace
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
it 'setInterval should pause' do
|
|
167
|
-
pending 'emulation.setScriptingEnabled not supported by Firefox yet'
|
|
168
|
-
|
|
169
|
-
with_test_state do |page:, **|
|
|
170
|
-
# Test code runs and fails with proper error trace
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Benefits**:
|
|
177
|
-
- Tests run and document exact failure
|
|
178
|
-
- Each test can have specific pending message
|
|
179
|
-
- Easy to identify when Firefox adds support (test will pass)
|
|
180
|
-
- Full error trace available for debugging
|
|
181
|
-
|
|
182
|
-
## Best Practices
|
|
183
|
-
|
|
184
|
-
### 1. Be Specific in Pending Messages
|
|
185
|
-
|
|
186
|
-
```ruby
|
|
187
|
-
# Good
|
|
188
|
-
pending 'emulation.setScriptingEnabled not supported by Firefox yet'
|
|
189
|
-
|
|
190
|
-
# Bad
|
|
191
|
-
pending 'not supported'
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### 2. Include BiDi Command Name
|
|
195
|
-
|
|
196
|
-
```ruby
|
|
197
|
-
# Good
|
|
198
|
-
pending 'browsingContext.setViewport not implemented'
|
|
199
|
-
|
|
200
|
-
# Bad
|
|
201
|
-
pending 'viewport not working'
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 3. Document When to Re-check
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
# Good
|
|
208
|
-
pending 'network.addIntercept requires Firefox 120+, current: 119'
|
|
209
|
-
|
|
210
|
-
# Bad
|
|
211
|
-
pending 'network interception broken'
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### 4. Remove Pending When Fixed
|
|
215
|
-
|
|
216
|
-
When Firefox adds support, the test will fail with:
|
|
217
|
-
```
|
|
218
|
-
Expected example to fail but it passed
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
This is your signal to remove the `pending` line!
|
|
222
|
-
|
|
223
|
-
## Firefox BiDi Limitations (Current)
|
|
224
|
-
|
|
225
|
-
As of this implementation, the following BiDi commands are not supported by Firefox:
|
|
226
|
-
|
|
227
|
-
1. `emulation.setScriptingEnabled` - Control JavaScript execution
|
|
228
|
-
- Tests: `spec/integration/page_spec.rb` (2 tests)
|
|
229
|
-
- Tests: `spec/integration/click_spec.rb` (1 test)
|
|
230
|
-
|
|
231
|
-
## Files Changed
|
|
232
|
-
|
|
233
|
-
- `spec/integration/click_spec.rb`: Changed `skip` to `pending` (line 71)
|
|
234
|
-
- `spec/integration/page_spec.rb`: Moved `skip` from before block to individual tests as `pending` (lines 19, 48)
|
|
235
|
-
|
|
236
|
-
## Test Results
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
bundle exec rspec spec/integration/
|
|
240
|
-
# 108 examples, 0 failures, 4 pending
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
All pending tests show proper error traces documenting Firefox limitations.
|
|
244
|
-
|
|
245
|
-
## Key Takeaways
|
|
246
|
-
|
|
247
|
-
1. **Use `pending` for browser limitations** - Shows what's missing with error trace
|
|
248
|
-
2. **Use `skip` for unimplemented features** - Our code doesn't exist yet
|
|
249
|
-
3. **Be specific in messages** - Include BiDi command name and reason
|
|
250
|
-
4. **Pending in test body, not before block** - Each test should be explicit
|
|
251
|
-
5. **Pending tests run code** - They document exact failure mode
|
|
252
|
-
6. **Remove pending when fixed** - Test will fail with "expected to fail but passed"
|
|
253
|
-
|
|
254
|
-
## References
|
|
255
|
-
|
|
256
|
-
- [RSpec Documentation: Pending and Skipped Examples](https://rspec.info/features/3-12/rspec-core/pending-and-skipped-examples/)
|
|
257
|
-
- [WebDriver BiDi Spec](https://w3c.github.io/webdriver-bidi/) - Check which commands are standardized
|
|
258
|
-
- [Firefox BiDi Implementation Status](https://wiki.mozilla.org/WebDriver/RemoteProtocol/WebDriver_BiDi) - Check Firefox support
|
|
259
|
-
|
|
260
|
-
## Commit Reference
|
|
261
|
-
|
|
262
|
-
See commit: "test: Use pending instead of skip for Firefox unsupported features"
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
# Selector Evaluation Methods Implementation
|
|
2
|
-
|
|
3
|
-
This document explains the implementation of `eval_on_selector` and `eval_on_selector_all` methods, including delegation patterns, handle lifecycle, and performance considerations.
|
|
4
|
-
|
|
5
|
-
### Overview
|
|
6
|
-
|
|
7
|
-
The `eval_on_selector` and `eval_on_selector_all` methods provide convenient shortcuts for querying elements and evaluating JavaScript functions on them, equivalent to Puppeteer's `$eval` and `$$eval`.
|
|
8
|
-
|
|
9
|
-
### API Design
|
|
10
|
-
|
|
11
|
-
#### Method Naming Convention
|
|
12
|
-
|
|
13
|
-
Ruby cannot use `$` in method names, so we use descriptive alternatives:
|
|
14
|
-
|
|
15
|
-
| Puppeteer | Ruby | Description |
|
|
16
|
-
|-----------|------|-------------|
|
|
17
|
-
| `$eval` | `eval_on_selector` | Evaluate on first matching element |
|
|
18
|
-
| `$$eval` | `eval_on_selector_all` | Evaluate on all matching elements |
|
|
19
|
-
|
|
20
|
-
#### Implementation Hierarchy
|
|
21
|
-
|
|
22
|
-
Following Puppeteer's delegation pattern:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
Page#eval_on_selector(_all)
|
|
26
|
-
↓ delegates to
|
|
27
|
-
Frame#eval_on_selector(_all)
|
|
28
|
-
↓ delegates to
|
|
29
|
-
ElementHandle#eval_on_selector(_all) (on document)
|
|
30
|
-
↓ implementation
|
|
31
|
-
1. query_selector(_all) - Find element(s)
|
|
32
|
-
2. Validate results
|
|
33
|
-
3. evaluate() - Execute function
|
|
34
|
-
4. dispose - Clean up handles
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Implementation Details
|
|
38
|
-
|
|
39
|
-
#### Page and Frame Methods
|
|
40
|
-
|
|
41
|
-
```ruby
|
|
42
|
-
# lib/puppeteer/bidi/page.rb
|
|
43
|
-
def eval_on_selector(selector, page_function, *args)
|
|
44
|
-
main_frame.eval_on_selector(selector, page_function, *args)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# lib/puppeteer/bidi/frame.rb
|
|
48
|
-
def eval_on_selector(selector, page_function, *args)
|
|
49
|
-
document.eval_on_selector(selector, page_function, *args)
|
|
50
|
-
end
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Design rationale**: Page and Frame act as thin wrappers, delegating to the document element handle.
|
|
54
|
-
|
|
55
|
-
#### ElementHandle#eval_on_selector
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
def eval_on_selector(selector, page_function, *args)
|
|
59
|
-
assert_not_disposed
|
|
60
|
-
|
|
61
|
-
element_handle = query_selector(selector)
|
|
62
|
-
raise SelectorNotFoundError, selector unless element_handle
|
|
63
|
-
|
|
64
|
-
begin
|
|
65
|
-
element_handle.evaluate(page_function, *args)
|
|
66
|
-
ensure
|
|
67
|
-
element_handle.dispose
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Key points**:
|
|
73
|
-
- Throws `SelectorNotFoundError` if no element found (matches Puppeteer behavior)
|
|
74
|
-
- Uses `begin/ensure` to guarantee handle disposal
|
|
75
|
-
- Searches within element's subtree (not page-wide)
|
|
76
|
-
|
|
77
|
-
#### ElementHandle#eval_on_selector_all
|
|
78
|
-
|
|
79
|
-
```ruby
|
|
80
|
-
def eval_on_selector_all(selector, page_function, *args)
|
|
81
|
-
assert_not_disposed
|
|
82
|
-
|
|
83
|
-
element_handles = query_selector_all(selector)
|
|
84
|
-
|
|
85
|
-
begin
|
|
86
|
-
# Create array handle in browser context
|
|
87
|
-
array_handle = @realm.call_function(
|
|
88
|
-
'(...elements) => elements',
|
|
89
|
-
false,
|
|
90
|
-
arguments: element_handles.map(&:remote_value)
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
array_js_handle = JSHandle.from(array_handle['result'], @realm)
|
|
94
|
-
|
|
95
|
-
begin
|
|
96
|
-
array_js_handle.evaluate(page_function, *args)
|
|
97
|
-
ensure
|
|
98
|
-
array_js_handle.dispose
|
|
99
|
-
end
|
|
100
|
-
ensure
|
|
101
|
-
element_handles.each(&:dispose)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**Key points**:
|
|
107
|
-
- Returns result for empty array without error (differs from `eval_on_selector`)
|
|
108
|
-
- Creates array handle using spread operator trick: `(...elements) => elements`
|
|
109
|
-
- Nested `ensure` blocks for proper resource cleanup
|
|
110
|
-
- Disposes both individual element handles and array handle
|
|
111
|
-
|
|
112
|
-
### Error Handling Differences
|
|
113
|
-
|
|
114
|
-
| Method | Behavior when no elements found |
|
|
115
|
-
|--------|--------------------------------|
|
|
116
|
-
| `eval_on_selector` | Throws `SelectorNotFoundError` |
|
|
117
|
-
| `eval_on_selector_all` | Returns evaluation result (e.g., `0` for `divs => divs.length`) |
|
|
118
|
-
|
|
119
|
-
This matches Puppeteer's behavior:
|
|
120
|
-
- `$eval`: Must find exactly one element
|
|
121
|
-
- `$$eval`: Works with zero or more elements
|
|
122
|
-
|
|
123
|
-
### Usage Examples
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
# Basic usage
|
|
127
|
-
page.set_content('<section id="test">Hello</section>')
|
|
128
|
-
id = page.eval_on_selector('section', 'e => e.id')
|
|
129
|
-
# => "test"
|
|
130
|
-
|
|
131
|
-
# With arguments
|
|
132
|
-
text = page.eval_on_selector('section', '(e, suffix) => e.textContent + suffix', '!')
|
|
133
|
-
# => "Hello!"
|
|
134
|
-
|
|
135
|
-
# ElementHandle arguments
|
|
136
|
-
div = page.query_selector('div')
|
|
137
|
-
result = page.eval_on_selector('section', '(e, div) => e.textContent + div.textContent', div)
|
|
138
|
-
|
|
139
|
-
# eval_on_selector_all with multiple elements
|
|
140
|
-
page.set_content('<div>A</div><div>B</div><div>C</div>')
|
|
141
|
-
count = page.eval_on_selector_all('div', 'divs => divs.length')
|
|
142
|
-
# => 3
|
|
143
|
-
|
|
144
|
-
# Subtree search with ElementHandle
|
|
145
|
-
tweet = page.query_selector('.tweet')
|
|
146
|
-
likes = tweet.eval_on_selector('.like', 'node => node.innerText')
|
|
147
|
-
# Only searches within .tweet element
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Test Coverage
|
|
151
|
-
|
|
152
|
-
**Total**: 13 integration tests
|
|
153
|
-
|
|
154
|
-
**Page.eval_on_selector** (4 tests):
|
|
155
|
-
- Basic functionality (property access)
|
|
156
|
-
- Argument passing
|
|
157
|
-
- ElementHandle arguments
|
|
158
|
-
- Error on missing selector
|
|
159
|
-
|
|
160
|
-
**ElementHandle.eval_on_selector** (3 tests):
|
|
161
|
-
- Basic functionality
|
|
162
|
-
- Subtree isolation
|
|
163
|
-
- Error on missing selector
|
|
164
|
-
|
|
165
|
-
**Page.eval_on_selector_all** (4 tests):
|
|
166
|
-
- Basic functionality (array length)
|
|
167
|
-
- Extra arguments
|
|
168
|
-
- ElementHandle arguments
|
|
169
|
-
- Large element count (1001 elements)
|
|
170
|
-
|
|
171
|
-
**ElementHandle.eval_on_selector_all** (2 tests):
|
|
172
|
-
- Subtree retrieval
|
|
173
|
-
- Empty result handling
|
|
174
|
-
|
|
175
|
-
### Performance Considerations
|
|
176
|
-
|
|
177
|
-
#### Handle Lifecycle
|
|
178
|
-
|
|
179
|
-
- **eval_on_selector**: Creates 1 temporary handle per call
|
|
180
|
-
- **eval_on_selector_all**: Creates N+1 handles (N elements + 1 array)
|
|
181
|
-
- All handles automatically disposed after evaluation
|
|
182
|
-
|
|
183
|
-
#### Large Element Sets
|
|
184
|
-
|
|
185
|
-
Tested with 1001 elements without issues. The implementation efficiently:
|
|
186
|
-
1. Queries all elements at once
|
|
187
|
-
2. Creates single array handle
|
|
188
|
-
3. Evaluates function in single round-trip
|
|
189
|
-
4. Disposes all handles in parallel
|
|
190
|
-
|
|
191
|
-
### Reference Implementation
|
|
192
|
-
|
|
193
|
-
Based on Puppeteer's implementation:
|
|
194
|
-
- [Page.$eval](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/api/Page.ts)
|
|
195
|
-
- [Frame.$eval](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/api/Frame.ts)
|
|
196
|
-
- [ElementHandle.$eval](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/api/ElementHandle.ts)
|
|
197
|
-
- [Test specs](https://github.com/puppeteer/puppeteer/blob/main/test/src/queryselector.spec.ts)
|
|
198
|
-
|