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

Commit 918d5e0

Browse files
committed
Use Frame.evaluate with execution contexts
Provide an evaluateOptions type for clarifying the options when calling evaluate on an execution context. Also for ease of debugging.
1 parent 0bc440e commit 918d5e0

File tree

6 files changed

+171
-65
lines changed

6 files changed

+171
-65
lines changed

common/element_handle.go

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,12 @@ func (h *ElementHandle) checkHitTargetAt(apiCtx context.Context, p Position) (bo
238238
return injected.checkHitTargetAt(node, point);
239239
}
240240
`)
241+
opts := evaluateOptions{
242+
forceCallable: true,
243+
returnByValue: true,
244+
}
241245
result, err := h.execCtx.evaluate(
242-
apiCtx, true, true, pageFn, []goja.Value{
246+
apiCtx, opts, pageFn, []goja.Value{
243247
rt.ToValue(injected),
244248
rt.ToValue(h),
245249
rt.ToValue(p),
@@ -270,8 +274,12 @@ func (h *ElementHandle) checkElementState(apiCtx context.Context, state string)
270274
return injected.checkElementState(node, state);
271275
}
272276
`)
277+
opts := evaluateOptions{
278+
forceCallable: true,
279+
returnByValue: true,
280+
}
273281
result, err := h.execCtx.evaluate(
274-
apiCtx, true, true, pageFn, []goja.Value{
282+
apiCtx, opts, pageFn, []goja.Value{
275283
rt.ToValue(injected),
276284
rt.ToValue(h),
277285
rt.ToValue(state),
@@ -397,8 +405,12 @@ func (h *ElementHandle) dispatchEvent(apiCtx context.Context, typ string, eventI
397405
injected.dispatchEvent(node, type, eventInit);
398406
}
399407
`)
408+
opts := evaluateOptions{
409+
forceCallable: true,
410+
returnByValue: true,
411+
}
400412
_, err = h.execCtx.evaluate(
401-
apiCtx, true, true, pageFn, []goja.Value{
413+
apiCtx, opts, pageFn, []goja.Value{
402414
rt.ToValue(injected),
403415
rt.ToValue(h),
404416
rt.ToValue(typ),
@@ -421,8 +433,12 @@ func (h *ElementHandle) fill(apiCtx context.Context, value string) (interface{},
421433
return injected.fill(node, value);
422434
}
423435
`)
436+
opts := evaluateOptions{
437+
forceCallable: true,
438+
returnByValue: true,
439+
}
424440
result, err := h.execCtx.evaluate(
425-
apiCtx, true, true, pageFn, []goja.Value{
441+
apiCtx, opts, pageFn, []goja.Value{
426442
rt.ToValue(injected),
427443
rt.ToValue(h),
428444
rt.ToValue(value),
@@ -450,8 +466,12 @@ func (h *ElementHandle) focus(apiCtx context.Context, resetSelectionIfNotFocused
450466
return injected.focusNode(node, resetSelectionIfNotFocused);
451467
}
452468
`)
469+
opts := evaluateOptions{
470+
forceCallable: true,
471+
returnByValue: true,
472+
}
453473
result, err := h.execCtx.evaluate(
454-
apiCtx, true, true, pageFn, []goja.Value{
474+
apiCtx, opts, pageFn, []goja.Value{
455475
rt.ToValue(injected),
456476
rt.ToValue(h),
457477
rt.ToValue(resetSelectionIfNotFocused),
@@ -473,7 +493,11 @@ func (h *ElementHandle) getAttribute(apiCtx context.Context, name string) (inter
473493
return element.getAttribute('` + name + `');
474494
}`
475495
rt := k6common.GetRuntime(apiCtx)
476-
return h.execCtx.evaluate(apiCtx, true, true, rt.ToValue(js), rt.ToValue(h))
496+
opts := evaluateOptions{
497+
forceCallable: true,
498+
returnByValue: true,
499+
}
500+
return h.execCtx.evaluate(apiCtx, opts, rt.ToValue(js), rt.ToValue(h))
477501
}
478502

479503
func (h *ElementHandle) hover(apiCtx context.Context, p *Position) error {
@@ -485,15 +509,23 @@ func (h *ElementHandle) innerHTML(apiCtx context.Context) (interface{}, error) {
485509
js := `(element) => {
486510
return element.innerHTML;
487511
}`
488-
return h.execCtx.evaluate(apiCtx, true, true, rt.ToValue(js), rt.ToValue(h))
512+
opts := evaluateOptions{
513+
forceCallable: true,
514+
returnByValue: true,
515+
}
516+
return h.execCtx.evaluate(apiCtx, opts, rt.ToValue(js), rt.ToValue(h))
489517
}
490518

491519
func (h *ElementHandle) innerText(apiCtx context.Context) (interface{}, error) {
492520
rt := k6common.GetRuntime(apiCtx)
493521
js := `(element) => {
494522
return element.innerText;
495523
}`
496-
return h.execCtx.evaluate(apiCtx, true, true, rt.ToValue(js), rt.ToValue(h))
524+
opts := evaluateOptions{
525+
forceCallable: true,
526+
returnByValue: true,
527+
}
528+
return h.execCtx.evaluate(apiCtx, opts, rt.ToValue(js), rt.ToValue(h))
497529
}
498530

499531
func (h *ElementHandle) inputValue(apiCtx context.Context) (interface{}, error) {
@@ -504,7 +536,11 @@ func (h *ElementHandle) inputValue(apiCtx context.Context) (interface{}, error)
504536
}
505537
return element.value;
506538
}`
507-
return h.execCtx.evaluate(apiCtx, true, true, rt.ToValue(js), rt.ToValue(h))
539+
opts := evaluateOptions{
540+
forceCallable: true,
541+
returnByValue: true,
542+
}
543+
return h.execCtx.evaluate(apiCtx, opts, rt.ToValue(js), rt.ToValue(h))
508544
}
509545

510546
func (h *ElementHandle) isChecked(apiCtx context.Context, timeout time.Duration) (bool, error) {
@@ -543,8 +579,12 @@ func (h *ElementHandle) offsetPosition(apiCtx context.Context, offset *Position)
543579
return injected.getElementBorderWidth(node);
544580
}
545581
`)
582+
opts := evaluateOptions{
583+
forceCallable: true,
584+
returnByValue: true,
585+
}
546586
result, err := h.execCtx.evaluate(
547-
apiCtx, true, true, pageFn, []goja.Value{
587+
apiCtx, opts, pageFn, []goja.Value{
548588
rt.ToValue(injected),
549589
rt.ToValue(h),
550590
}...)
@@ -709,8 +749,12 @@ func (h *ElementHandle) selectOption(apiCtx context.Context, values goja.Value)
709749
return injected.selectOptions(node, values);
710750
}
711751
`)
752+
opts := evaluateOptions{
753+
forceCallable: true,
754+
returnByValue: false,
755+
}
712756
result, err := h.execCtx.evaluate(
713-
apiCtx, true, false, pageFn, []goja.Value{
757+
apiCtx, opts, pageFn, []goja.Value{
714758
rt.ToValue(injected),
715759
rt.ToValue(h),
716760
rt.ToValue(convValues),
@@ -738,8 +782,12 @@ func (h *ElementHandle) selectText(apiCtx context.Context) error {
738782
return injected.selectText(node);
739783
}
740784
`)
785+
opts := evaluateOptions{
786+
forceCallable: true,
787+
returnByValue: true,
788+
}
741789
result, err := h.execCtx.evaluate(
742-
apiCtx, true, true, pageFn, []goja.Value{
790+
apiCtx, opts, pageFn, []goja.Value{
743791
rt.ToValue(injected),
744792
rt.ToValue(h),
745793
}...)
@@ -789,7 +837,11 @@ func (h *ElementHandle) textContent(apiCtx context.Context) (interface{}, error)
789837
js := `(element) => {
790838
return element.textContent;
791839
}`
792-
return h.execCtx.evaluate(apiCtx, true, true, rt.ToValue(js), rt.ToValue(h))
840+
opts := evaluateOptions{
841+
forceCallable: true,
842+
returnByValue: true,
843+
}
844+
return h.execCtx.evaluate(apiCtx, opts, rt.ToValue(js), rt.ToValue(h))
793845
}
794846

795847
func (h *ElementHandle) typ(apiCtx context.Context, text string, opts *KeyboardOptions) error {
@@ -811,7 +863,11 @@ func (h *ElementHandle) waitAndScrollIntoViewIfNeeded(apiCtx context.Context, fo
811863
element.scrollIntoViewIfNeeded(true);
812864
return [window.scrollX, window.scrollY];
813865
}`)
814-
return h.execCtx.evaluate(apiCtx, true, true, pageFn, rt.ToValue(h))
866+
opts := evaluateOptions{
867+
forceCallable: true,
868+
returnByValue: true,
869+
}
870+
return h.execCtx.evaluate(apiCtx, opts, pageFn, rt.ToValue(h))
815871
}
816872
actFn := elementHandleActionFn(h, []string{"visible", "stable"}, fn, force, noWaitAfter, timeout)
817873
_, err := callApiWithTimeout(h.ctx, actFn, timeout)
@@ -832,8 +888,12 @@ func (h *ElementHandle) waitForElementState(apiCtx context.Context, states []str
832888
return injected.waitForElementStates(node, states, timeout);
833889
}
834890
`)
891+
opts := evaluateOptions{
892+
forceCallable: true,
893+
returnByValue: true,
894+
}
835895
result, err := h.execCtx.evaluate(
836-
apiCtx, true, true, pageFn, []goja.Value{
896+
apiCtx, opts, pageFn, []goja.Value{
837897
rt.ToValue(injected),
838898
rt.ToValue(h),
839899
rt.ToValue(states),
@@ -873,8 +933,12 @@ func (h *ElementHandle) waitForSelector(apiCtx context.Context, selector string,
873933
return injected.waitForSelector(selector, scope, strict, state, 'raf', timeout, ...args);
874934
}
875935
`)
936+
eopts := evaluateOptions{
937+
forceCallable: true,
938+
returnByValue: false,
939+
}
876940
result, err := h.execCtx.evaluate(
877-
apiCtx, true, false, pageFn, []goja.Value{
941+
apiCtx, eopts, pageFn, []goja.Value{
878942
rt.ToValue(injected),
879943
rt.ToValue(parsedSelector),
880944
rt.ToValue(h),
@@ -1166,7 +1230,11 @@ func (h *ElementHandle) OwnerFrame() api.Frame {
11661230
return injected.getDocumentElement(node);
11671231
}
11681232
`)
1169-
res, err := h.execCtx.evaluate(h.ctx, true, false, pageFn, []goja.Value{rt.ToValue(injected), rt.ToValue(h)}...)
1233+
opts := evaluateOptions{
1234+
forceCallable: true,
1235+
returnByValue: false,
1236+
}
1237+
res, err := h.execCtx.evaluate(h.ctx, opts, pageFn, []goja.Value{rt.ToValue(injected), rt.ToValue(h)}...)
11701238
if err != nil {
11711239
k6common.Throw(rt, fmt.Errorf("failed getting document element: %w", err))
11721240
}
@@ -1226,8 +1294,12 @@ func (h *ElementHandle) Query(selector string) api.ElementHandle {
12261294
return injected.querySelector(selector, scope || document, false);
12271295
}
12281296
`)
1297+
opts := evaluateOptions{
1298+
forceCallable: true,
1299+
returnByValue: false,
1300+
}
12291301
result, err := h.execCtx.evaluate(
1230-
h.ctx, true, false, pageFn, []goja.Value{
1302+
h.ctx, opts, pageFn, []goja.Value{
12311303
rt.ToValue(injected),
12321304
rt.ToValue(parsedSelector),
12331305
rt.ToValue(h),
@@ -1267,8 +1339,12 @@ func (h *ElementHandle) QueryAll(selector string) []api.ElementHandle {
12671339
return injected.querySelectorAll(selector, scope || document, false);
12681340
}
12691341
`)
1342+
opts := evaluateOptions{
1343+
forceCallable: true,
1344+
returnByValue: false,
1345+
}
12701346
result, err := h.execCtx.evaluate(
1271-
h.ctx, true, false, pageFn, []goja.Value{
1347+
h.ctx, opts, pageFn, []goja.Value{
12721348
rt.ToValue(injected),
12731349
rt.ToValue(parsedSelector),
12741350
rt.ToValue(h),

common/execution_context.go

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ const (
4343
utilityWorld = "utility"
4444
)
4545

46+
type evaluateOptions struct {
47+
forceCallable, returnByValue bool
48+
}
49+
50+
func (ea evaluateOptions) String() string {
51+
return fmt.Sprintf("forceCallable:%t returnByValue:%t", ea.forceCallable, ea.returnByValue)
52+
}
53+
4654
// ExecutionContext represents a JS execution context
4755
type ExecutionContext struct {
4856
ctx context.Context
@@ -150,23 +158,19 @@ func (e *ExecutionContext) adoptElementHandle(eh *ElementHandle) (*ElementHandle
150158

151159
// evaluate will evaluate provided callable within this execution context
152160
// and return by value or handle
153-
func (e *ExecutionContext) evaluate(
154-
apiCtx context.Context,
155-
forceCallable bool, returnByValue bool,
156-
pageFunc goja.Value, args ...goja.Value,
157-
) (res interface{}, err error) {
161+
func (e *ExecutionContext) evaluate(apiCtx context.Context, opts evaluateOptions, pageFunc goja.Value, args ...goja.Value) (res interface{}, err error) {
158162
e.logger.Debugf(
159163
"ExecutionContext:evaluate",
160-
"sid:%s stid:%s fid:%s ectxid:%d furl:%q forceCallable:%t returnByvalue:%t",
161-
e.sid, e.stid, e.fid, e.id, e.furl, forceCallable, returnByValue)
164+
"sid:%s stid:%s fid:%s ectxid:%d furl:%q %s",
165+
e.sid, e.stid, e.fid, e.id, e.furl, opts)
162166

163167
suffix := `//# sourceURL=` + evaluationScriptURL
164168

165169
var (
166-
isCallable = forceCallable
170+
isCallable = opts.forceCallable
167171
expression = pageFunc.ToString().String()
168172
)
169-
if !forceCallable {
173+
if !isCallable {
170174
_, isCallable = goja.AssertFunction(pageFunc)
171175
}
172176
if !isCallable {
@@ -181,7 +185,7 @@ func (e *ExecutionContext) evaluate(
181185
)
182186
action := runtime.Evaluate(expressionWithSourceURL).
183187
WithContextID(e.id).
184-
WithReturnByValue(returnByValue).
188+
WithReturnByValue(opts.returnByValue).
185189
WithAwaitPromise(true).
186190
WithUserGesture(true)
187191
if remoteObject, exceptionDetails, err = action.Do(cdp.WithExecutor(apiCtx, e.session)); err != nil {
@@ -193,7 +197,7 @@ func (e *ExecutionContext) evaluate(
193197
if remoteObject == nil {
194198
return
195199
}
196-
if returnByValue {
200+
if opts.returnByValue {
197201
res, err = valueFromRemoteObject(apiCtx, remoteObject)
198202
if err != nil {
199203
return nil, fmt.Errorf("cannot extract value from remote object (%s): %w", remoteObject.ObjectID, err)
@@ -220,7 +224,7 @@ func (e *ExecutionContext) evaluate(
220224
action := runtime.CallFunctionOn(expressionWithSourceURL).
221225
WithArguments(arguments).
222226
WithExecutionContextID(e.id).
223-
WithReturnByValue(returnByValue).
227+
WithReturnByValue(opts.returnByValue).
224228
WithAwaitPromise(true).
225229
WithUserGesture(true)
226230
if remoteObject, exceptionDetails, err = action.Do(cdp.WithExecutor(apiCtx, e.session)); err != nil {
@@ -232,7 +236,7 @@ func (e *ExecutionContext) evaluate(
232236
if remoteObject == nil {
233237
return
234238
}
235-
if returnByValue {
239+
if opts.returnByValue {
236240
res, err = valueFromRemoteObject(apiCtx, remoteObject)
237241
if err != nil {
238242
return nil, fmt.Errorf("cannot extract value from remote object (%s): %w", remoteObject.ObjectID, err)
@@ -263,7 +267,11 @@ func (e *ExecutionContext) getInjectedScript(apiCtx context.Context) (api.JSHand
263267
expressionWithSourceURL = expression + "\n" + suffix
264268
}
265269

266-
handle, err := e.evaluate(apiCtx, false, false, rt.ToValue(expressionWithSourceURL))
270+
opts := evaluateOptions{
271+
forceCallable: false,
272+
returnByValue: false,
273+
}
274+
handle, err := e.evaluate(apiCtx, opts, rt.ToValue(expressionWithSourceURL))
267275
if handle == nil || err != nil {
268276
return nil, fmt.Errorf("cannot get injected script (%q): %w", suffix, err)
269277
}
@@ -277,15 +285,23 @@ func (e *ExecutionContext) Evaluate(
277285
apiCtx context.Context,
278286
pageFunc goja.Value, args ...goja.Value,
279287
) (interface{}, error) {
280-
return e.evaluate(apiCtx, true, true, pageFunc, args...)
288+
opts := evaluateOptions{
289+
forceCallable: true,
290+
returnByValue: true,
291+
}
292+
return e.evaluate(apiCtx, opts, pageFunc, args...)
281293
}
282294

283295
// EvaluateHandle will evaluate provided page function within this execution context
284296
func (e *ExecutionContext) EvaluateHandle(
285297
apiCtx context.Context,
286298
pageFunc goja.Value, args ...goja.Value,
287299
) (api.JSHandle, error) {
288-
res, err := e.evaluate(apiCtx, true, false, pageFunc, args...)
300+
opts := evaluateOptions{
301+
forceCallable: true,
302+
returnByValue: false,
303+
}
304+
res, err := e.evaluate(apiCtx, opts, pageFunc, args...)
289305
if err != nil {
290306
return nil, err
291307
}

0 commit comments

Comments
 (0)