Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 38ed12e

Browse files
authored
Merge pull request #149 from grafana/fix/53-interface-conversion-error
Fix interface conversion in document
2 parents 46b7848 + fd30521 commit 38ed12e

File tree

3 files changed

+150
-8
lines changed

3 files changed

+150
-8
lines changed

common/execution_context.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,8 @@ func (e *ExecutionContext) EvaluateHandle(apiCtx context.Context, pageFunc goja.
217217
func (e *ExecutionContext) Frame() *Frame {
218218
return e.frame
219219
}
220+
221+
// ID returns the CDP runtime ID of this execution context.
222+
func (e *ExecutionContext) ID() runtime.ExecutionContextID {
223+
return e.id
224+
}

common/frame.go

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ type Frame struct {
117117

118118
documentHandle *ElementHandle
119119

120-
mainExecutionContext *ExecutionContext
121-
utilityExecutionContext *ExecutionContext
120+
mainExecutionContext frameExecutionContext
121+
utilityExecutionContext frameExecutionContext
122122
mainExecutionContextCh chan bool
123123
utilityExecutionContextCh chan bool
124124
mainExecutionContextHasWaited int32
@@ -311,11 +311,17 @@ func (f *Frame) document() (*ElementHandle, error) {
311311
if f.documentHandle != nil {
312312
return f.documentHandle, nil
313313
}
314+
314315
f.waitForExecutionContext("main")
316+
315317
result, err := f.mainExecutionContext.evaluate(f.ctx, false, false, rt.ToValue("document"), nil)
316318
if err != nil {
317-
return nil, err
319+
return nil, fmt.Errorf("frame document: cannot evaluate in main execution context: %w", err)
320+
}
321+
if result == nil {
322+
return nil, errors.New("frame document: evaluate result is nil in main execution context")
318323
}
324+
319325
f.documentHandle = result.(*ElementHandle)
320326
return f.documentHandle, err
321327
}
@@ -350,10 +356,10 @@ func (f *Frame) navigated(name string, url string, loaderID string) {
350356
}
351357

352358
func (f *Frame) nullContext(execCtxID runtime.ExecutionContextID) {
353-
if f.mainExecutionContext != nil && f.mainExecutionContext.id == execCtxID {
359+
if f.mainExecutionContext != nil && f.mainExecutionContext.ID() == execCtxID {
354360
f.mainExecutionContext = nil
355361
f.documentHandle = nil
356-
} else if f.utilityExecutionContext != nil && f.utilityExecutionContext.id == execCtxID {
362+
} else if f.utilityExecutionContext != nil && f.utilityExecutionContext.ID() == execCtxID {
357363
f.utilityExecutionContext = nil
358364
}
359365
}
@@ -412,7 +418,7 @@ func (f *Frame) requestByID(reqID network.RequestID) *Request {
412418
return frameSession.networkManager.requestFromID(reqID)
413419
}
414420

415-
func (f *Frame) setContext(world string, execCtx *ExecutionContext) {
421+
func (f *Frame) setContext(world string, execCtx frameExecutionContext) {
416422
if world == "main" {
417423
f.mainExecutionContext = execCtx
418424
if len(f.mainExecutionContextCh) == 0 {
@@ -507,13 +513,13 @@ func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptio
507513

508514
func (f *Frame) AddScriptTag(opts goja.Value) {
509515
rt := k6common.GetRuntime(f.ctx)
510-
k6common.Throw(rt, errors.New("Frame.AddScriptTag() has not been implemented yet!"))
516+
k6common.Throw(rt, errors.New("Frame.AddScriptTag() has not been implemented yet"))
511517
applySlowMo(f.ctx)
512518
}
513519

514520
func (f *Frame) AddStyleTag(opts goja.Value) {
515521
rt := k6common.GetRuntime(f.ctx)
516-
k6common.Throw(rt, errors.New("Frame.AddStyleTag() has not been implemented yet!"))
522+
k6common.Throw(rt, errors.New("Frame.AddStyleTag() has not been implemented yet"))
517523
applySlowMo(f.ctx)
518524
}
519525

@@ -1244,3 +1250,46 @@ func (f *Frame) WaitForTimeout(timeout int64) {
12441250
case <-time.After(time.Duration(timeout) * time.Millisecond):
12451251
}
12461252
}
1253+
1254+
// frameExecutionContext represents a JS execution context that belongs to Frame.
1255+
type frameExecutionContext interface {
1256+
// adoptBackendNodeId adopts specified backend node into this execution
1257+
// context from another execution context.
1258+
adoptBackendNodeId(backendNodeID cdp.BackendNodeID) (*ElementHandle, error)
1259+
1260+
// adoptElementHandle adopts the specified element handle into this
1261+
// execution context from another execution context.
1262+
adoptElementHandle(elementHandle *ElementHandle) (*ElementHandle, error)
1263+
1264+
// evaluate will evaluate provided callable within this execution
1265+
// context and return by value or handle.
1266+
evaluate(
1267+
apiCtx context.Context,
1268+
forceCallable bool, returnByValue bool,
1269+
pageFunc goja.Value, args ...goja.Value,
1270+
) (res interface{}, err error)
1271+
1272+
// getInjectedScript returns a JS handle to the injected script of helper
1273+
// functions.
1274+
getInjectedScript(apiCtx context.Context) (api.JSHandle, error)
1275+
1276+
// Evaluate will evaluate provided page function within this execution
1277+
// context.
1278+
Evaluate(
1279+
apiCtx context.Context,
1280+
pageFunc goja.Value, args ...goja.Value,
1281+
) (interface{}, error)
1282+
1283+
// EvaluateHandle will evaluate provided page function within this
1284+
// execution context.
1285+
EvaluateHandle(
1286+
apiCtx context.Context,
1287+
pageFunc goja.Value, args ...goja.Value,
1288+
) (api.JSHandle, error)
1289+
1290+
// Frame returns the frame that this execution context belongs to.
1291+
Frame() *Frame
1292+
1293+
// id returns the CDP runtime ID of this execution context.
1294+
ID() runtime.ExecutionContextID
1295+
}

common/frame_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
*
3+
* xk6-browser - a browser automation extension for k6
4+
* Copyright (C) 2021 Load Impact
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*
19+
*/
20+
21+
package common
22+
23+
import (
24+
"context"
25+
"testing"
26+
"time"
27+
28+
"github.com/chromedp/cdproto/cdp"
29+
"github.com/dop251/goja"
30+
"github.com/stretchr/testify/require"
31+
)
32+
33+
// Test calling Frame.document does not panic with a nil document.
34+
// See: issue #53 for details.
35+
func TestFrameNilDocument(t *testing.T) {
36+
t.Parallel()
37+
38+
ctx := context.Background()
39+
fm := NewFrameManager(ctx, nil, nil, nil, nil)
40+
frame := NewFrame(ctx, fm, nil, cdp.FrameID("42"))
41+
42+
// frame should not panic with a nil document
43+
stub := &executionContextTestStub{
44+
evaluateFn: func(apiCtx context.Context, forceCallable bool, returnByValue bool, pageFunc goja.Value, args ...goja.Value) (res interface{}, err error) {
45+
// return nil to test for panic
46+
return nil, nil
47+
},
48+
}
49+
50+
// document() waits for the main execution context
51+
ok := make(chan struct{}, 1)
52+
go func() {
53+
frame.setContext("main", stub)
54+
ok <- struct{}{}
55+
}()
56+
select {
57+
case <-ok:
58+
case <-time.After(time.Second):
59+
require.FailNow(t, "cannot set the main execution context, frame.setContext timed out")
60+
}
61+
62+
require.NotPanics(t, func() {
63+
_, err := frame.document()
64+
require.Error(t, err)
65+
})
66+
67+
// frame gets the document from the evaluate call
68+
want := &ElementHandle{}
69+
stub.evaluateFn = func(apiCtx context.Context, forceCallable bool, returnByValue bool, pageFunc goja.Value, args ...goja.Value) (res interface{}, err error) {
70+
return want, nil
71+
}
72+
got, err := frame.document()
73+
require.NoError(t, err)
74+
require.Equal(t, want, got)
75+
76+
// frame sets documentHandle in the document method
77+
got = frame.documentHandle
78+
require.Equal(t, want, got)
79+
}
80+
81+
type executionContextTestStub struct {
82+
ExecutionContext
83+
evaluateFn func(apiCtx context.Context, forceCallable bool, returnByValue bool, pageFunc goja.Value, args ...goja.Value) (res interface{}, err error)
84+
}
85+
86+
func (e executionContextTestStub) evaluate(apiCtx context.Context, forceCallable bool, returnByValue bool, pageFunc goja.Value, args ...goja.Value) (res interface{}, err error) {
87+
return e.evaluateFn(apiCtx, forceCallable, returnByValue, pageFunc, args...)
88+
}

0 commit comments

Comments
 (0)