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

Commit 165614c

Browse files
author
Ivan Mirić
committed
Move RemoteObject helpers to standalone file
Resolves #130 (comment)
1 parent 80181ea commit 165614c

File tree

4 files changed

+353
-292
lines changed

4 files changed

+353
-292
lines changed

common/helpers.go

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ import (
2828
"math"
2929
"os"
3030
"reflect"
31-
"strconv"
3231
"strings"
3332
"time"
3433

3534
cdpruntime "github.com/chromedp/cdproto/runtime"
3635
"github.com/dop251/goja"
37-
"github.com/sirupsen/logrus"
3836
k6common "go.k6.io/k6/js/common"
3937
)
4038

@@ -169,85 +167,6 @@ func errorFromDOMError(domErr string) error {
169167
return fmt.Errorf(domErr)
170168
}
171169

172-
func parseRemoteObjectPreview(op *cdpruntime.ObjectPreview, logger *logrus.Entry) (map[string]interface{}, error) {
173-
obj := make(map[string]interface{})
174-
if op.Overflow {
175-
logger.Warn("object will be parsed partially")
176-
}
177-
178-
for _, p := range op.Properties {
179-
val, err := parseRemoteObjectValue(p.Type, p.Value, p.ValuePreview, logger)
180-
if err != nil {
181-
logger.WithError(err).Errorf("failed parsing object property '%s'", p.Name)
182-
continue
183-
}
184-
obj[p.Name] = val
185-
}
186-
187-
return obj, nil
188-
}
189-
190-
func parseRemoteObjectValue(
191-
t cdpruntime.Type, val string, op *cdpruntime.ObjectPreview, logger *logrus.Entry,
192-
) (interface{}, error) {
193-
switch t {
194-
case cdpruntime.TypeAccessor:
195-
return "accessor", nil
196-
case cdpruntime.TypeBigint:
197-
n, err := strconv.ParseInt(strings.Replace(val, "n", "", -1), 10, 64)
198-
if err != nil {
199-
return nil, BigIntParseError{err}
200-
}
201-
return n, nil
202-
case cdpruntime.TypeFunction:
203-
return "function()", nil
204-
case cdpruntime.TypeString:
205-
if !strings.HasPrefix(val, `"`) {
206-
return val, nil
207-
}
208-
case cdpruntime.TypeSymbol:
209-
return val, nil
210-
case cdpruntime.TypeObject:
211-
if op != nil {
212-
return parseRemoteObjectPreview(op, logger)
213-
}
214-
if val == "Object" {
215-
return val, nil
216-
}
217-
case cdpruntime.TypeUndefined:
218-
return "undefined", nil
219-
}
220-
221-
var v interface{}
222-
err := json.Unmarshal([]byte(val), &v)
223-
if err != nil {
224-
return nil, err
225-
}
226-
227-
return v, nil
228-
}
229-
230-
func parseRemoteObject(obj *cdpruntime.RemoteObject, logger *logrus.Entry) (val interface{}, err error) {
231-
if obj.UnserializableValue == "" {
232-
val, err = parseRemoteObjectValue(obj.Type, string(obj.Value), obj.Preview, logger)
233-
} else {
234-
switch obj.UnserializableValue.String() {
235-
case "-0": // To handle +0 divided by negative number
236-
return math.Float64frombits(0 | (1 << 63)), nil
237-
case "NaN":
238-
return math.NaN(), nil
239-
case "Infinity":
240-
return math.Inf(0), nil
241-
case "-Infinity":
242-
return math.Inf(-1), nil
243-
default:
244-
return nil, UnserializableValueError{obj.UnserializableValue}
245-
}
246-
}
247-
248-
return
249-
}
250-
251170
func stringSliceContains(s []string, e string) bool {
252171
for _, a := range s {
253172
if a == e {
@@ -257,17 +176,6 @@ func stringSliceContains(s []string, e string) bool {
257176
return false
258177
}
259178

260-
func valueFromRemoteObject(
261-
ctx context.Context, remoteObject *cdpruntime.RemoteObject, logger *logrus.Entry,
262-
) (goja.Value, error) {
263-
rt := k6common.GetRuntime(ctx)
264-
val, err := parseRemoteObject(remoteObject, logger)
265-
if val == "undefined" {
266-
return goja.Undefined(), err
267-
}
268-
return rt.ToValue(val), err
269-
}
270-
271179
func createWaitForEventHandler(
272180
ctx context.Context,
273181
emitter EventEmitter, events []string,

common/helpers_test.go

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
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+
"encoding/json"
26+
"fmt"
27+
"math"
28+
"testing"
29+
30+
"github.com/chromedp/cdproto/cdp"
31+
"github.com/chromedp/cdproto/runtime"
32+
"github.com/dop251/goja"
33+
"github.com/sirupsen/logrus"
34+
"github.com/stretchr/testify/require"
35+
)
36+
37+
func newExecCtx() (*ExecutionContext, context.Context, *goja.Runtime) {
38+
ctx := context.Background()
39+
logger := NewLogger(ctx, logrus.New(), false, nil)
40+
execCtx := NewExecutionContext(ctx, nil, nil, runtime.ExecutionContextID(123456789), logger)
41+
42+
return execCtx, ctx, goja.New()
43+
}
44+
45+
func TestConvertArgument(t *testing.T) {
46+
t.Parallel()
47+
48+
t.Run("int64", func(t *testing.T) {
49+
execCtx, ctx, rt := newExecCtx()
50+
var value int64 = 777
51+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
52+
53+
require.NotNil(t, arg)
54+
result, _ := json.Marshal(value)
55+
require.Equal(t, result, []byte(arg.Value))
56+
require.Empty(t, arg.UnserializableValue)
57+
require.Empty(t, arg.ObjectID)
58+
})
59+
60+
t.Run("int64 maxint", func(t *testing.T) {
61+
execCtx, ctx, rt := newExecCtx()
62+
63+
var value int64 = math.MaxInt32 + 1
64+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
65+
66+
require.NotNil(t, arg)
67+
require.Equal(t, fmt.Sprintf("%dn", value), string(arg.UnserializableValue))
68+
require.Empty(t, arg.Value)
69+
require.Empty(t, arg.ObjectID)
70+
})
71+
72+
t.Run("float64", func(t *testing.T) {
73+
execCtx, ctx, rt := newExecCtx()
74+
75+
var value float64 = 777.0
76+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
77+
78+
require.NotNil(t, arg)
79+
result, _ := json.Marshal(value)
80+
require.Equal(t, result, []byte(arg.Value))
81+
require.Empty(t, arg.UnserializableValue)
82+
require.Empty(t, arg.ObjectID)
83+
})
84+
85+
t.Run("float64 unserializable values", func(t *testing.T) {
86+
execCtx, ctx, rt := newExecCtx()
87+
88+
unserializableValues := []struct {
89+
value float64
90+
expected string
91+
}{
92+
{
93+
value: math.Float64frombits(0 | (1 << 63)),
94+
expected: "-0",
95+
},
96+
{
97+
value: math.Inf(0),
98+
expected: "Infinity",
99+
},
100+
{
101+
value: math.Inf(-1),
102+
expected: "-Infinity",
103+
},
104+
{
105+
value: math.NaN(),
106+
expected: "NaN",
107+
},
108+
}
109+
110+
for _, v := range unserializableValues {
111+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(v.value))
112+
require.NotNil(t, arg)
113+
require.Equal(t, v.expected, string(arg.UnserializableValue))
114+
require.Empty(t, arg.Value)
115+
require.Empty(t, arg.ObjectID)
116+
}
117+
})
118+
119+
t.Run("bool", func(t *testing.T) {
120+
execCtx, ctx, rt := newExecCtx()
121+
122+
var value bool = true
123+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
124+
125+
require.NotNil(t, arg)
126+
result, _ := json.Marshal(value)
127+
require.Equal(t, result, []byte(arg.Value))
128+
require.Empty(t, arg.UnserializableValue)
129+
require.Empty(t, arg.ObjectID)
130+
})
131+
132+
t.Run("string", func(t *testing.T) {
133+
execCtx, ctx, rt := newExecCtx()
134+
var value string = "hello world"
135+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
136+
137+
require.NotNil(t, arg)
138+
result, _ := json.Marshal(value)
139+
require.Equal(t, result, []byte(arg.Value))
140+
require.Empty(t, arg.UnserializableValue)
141+
require.Empty(t, arg.ObjectID)
142+
})
143+
144+
t.Run("*BaseJSHandle", func(t *testing.T) {
145+
execCtx, ctx, rt := newExecCtx()
146+
timeoutSetings := NewTimeoutSettings(nil)
147+
frameManager := NewFrameManager(ctx, nil, nil, timeoutSetings, NewLogger(ctx, NullLogger(), false, nil))
148+
frame := NewFrame(ctx, frameManager, nil, cdp.FrameID("frame_id_0123456789"))
149+
remoteObjValue := "hellow world"
150+
result, _ := json.Marshal(remoteObjValue)
151+
remoteObject := &runtime.RemoteObject{
152+
Type: "string",
153+
Value: result,
154+
}
155+
156+
value := NewJSHandle(ctx, nil, execCtx, frame, remoteObject, execCtx.logger)
157+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
158+
159+
require.NotNil(t, arg)
160+
require.Equal(t, result, []byte(arg.Value))
161+
require.Empty(t, arg.UnserializableValue)
162+
require.Empty(t, arg.ObjectID)
163+
})
164+
165+
t.Run("*BaseJSHandle wrong context", func(t *testing.T) {
166+
execCtx, ctx, rt := newExecCtx()
167+
timeoutSetings := NewTimeoutSettings(nil)
168+
frameManager := NewFrameManager(ctx, nil, nil, timeoutSetings, NewLogger(ctx, NullLogger(), false, nil))
169+
frame := NewFrame(ctx, frameManager, nil, cdp.FrameID("frame_id_0123456789"))
170+
remoteObjectID := runtime.RemoteObjectID("object_id_0123456789")
171+
remoteObject := &runtime.RemoteObject{
172+
Type: "object",
173+
Subtype: "node",
174+
ObjectID: remoteObjectID,
175+
}
176+
execCtx2 := NewExecutionContext(ctx, nil, nil, runtime.ExecutionContextID(123456789), execCtx.logger)
177+
178+
value := NewJSHandle(ctx, nil, execCtx2, frame, remoteObject, execCtx.logger)
179+
arg, err := convertArgument(ctx, execCtx, rt.ToValue(value))
180+
181+
require.Nil(t, arg)
182+
require.ErrorIs(t, ErrWrongExecutionContext, err)
183+
})
184+
185+
t.Run("*BaseJSHandle is disposed", func(t *testing.T) {
186+
execCtx, ctx, rt := newExecCtx()
187+
timeoutSetings := NewTimeoutSettings(nil)
188+
frameManager := NewFrameManager(ctx, nil, nil, timeoutSetings, NewLogger(ctx, NullLogger(), false, nil))
189+
frame := NewFrame(ctx, frameManager, nil, cdp.FrameID("frame_id_0123456789"))
190+
remoteObjectID := runtime.RemoteObjectID("object_id_0123456789")
191+
remoteObject := &runtime.RemoteObject{
192+
Type: "object",
193+
Subtype: "node",
194+
ObjectID: remoteObjectID,
195+
}
196+
197+
value := NewJSHandle(ctx, nil, execCtx, frame, remoteObject, execCtx.logger)
198+
value.(*BaseJSHandle).disposed = true
199+
arg, err := convertArgument(ctx, execCtx, rt.ToValue(value))
200+
201+
require.Nil(t, arg)
202+
require.ErrorIs(t, ErrJSHandleDisposed, err)
203+
})
204+
205+
t.Run("*BaseJSHandle as *ElementHandle", func(t *testing.T) {
206+
execCtx, ctx, rt := newExecCtx()
207+
timeoutSetings := NewTimeoutSettings(nil)
208+
frameManager := NewFrameManager(ctx, nil, nil, timeoutSetings, NewLogger(ctx, NullLogger(), false, nil))
209+
frame := NewFrame(ctx, frameManager, nil, cdp.FrameID("frame_id_0123456789"))
210+
remoteObjectID := runtime.RemoteObjectID("object_id_0123456789")
211+
remoteObject := &runtime.RemoteObject{
212+
Type: "object",
213+
Subtype: "node",
214+
ObjectID: remoteObjectID,
215+
}
216+
217+
value := NewJSHandle(ctx, nil, execCtx, frame, remoteObject, execCtx.logger)
218+
arg, _ := convertArgument(ctx, execCtx, rt.ToValue(value))
219+
220+
require.NotNil(t, arg)
221+
require.Equal(t, remoteObjectID, arg.ObjectID)
222+
require.Empty(t, arg.Value)
223+
require.Empty(t, arg.UnserializableValue)
224+
})
225+
}

0 commit comments

Comments
 (0)