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

Commit 4405489

Browse files
authored
Merge pull request #608 from grafana/fix/605-browsertype-launch-opts
Parse BrowserType.Launch options and return errors
2 parents 1f4392b + 9cd1e76 commit 4405489

File tree

5 files changed

+383
-165
lines changed

5 files changed

+383
-165
lines changed

common/browser_options.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package common
2+
3+
import (
4+
"context"
5+
"errors"
6+
"time"
7+
8+
"github.com/dop251/goja"
9+
10+
"github.com/grafana/xk6-browser/k6ext"
11+
)
12+
13+
// ProxyOptions allows configuring a proxy server.
14+
type ProxyOptions struct {
15+
Server string
16+
Bypass string
17+
Username string
18+
Password string
19+
}
20+
21+
// LaunchOptions stores browser launch options.
22+
type LaunchOptions struct {
23+
Args []string
24+
Debug bool
25+
Devtools bool
26+
Env map[string]string
27+
ExecutablePath string
28+
Headless bool
29+
IgnoreDefaultArgs []string
30+
LogCategoryFilter string
31+
Proxy ProxyOptions
32+
SlowMo time.Duration
33+
Timeout time.Duration
34+
}
35+
36+
// LaunchPersistentContextOptions stores browser launch options for persistent context.
37+
type LaunchPersistentContextOptions struct {
38+
LaunchOptions
39+
BrowserContextOptions
40+
}
41+
42+
// NewLaunchOptions returns a new LaunchOptions.
43+
func NewLaunchOptions() *LaunchOptions {
44+
launchOpts := LaunchOptions{
45+
Env: make(map[string]string),
46+
Headless: true,
47+
LogCategoryFilter: ".*",
48+
Timeout: DefaultTimeout,
49+
}
50+
return &launchOpts
51+
}
52+
53+
// Parse parses launch options from a JS object.
54+
func (l *LaunchOptions) Parse(ctx context.Context, opts goja.Value) error { //nolint:cyclop
55+
if !gojaValueExists(opts) {
56+
return errors.New("LaunchOptions does not exist in the runtime")
57+
}
58+
var (
59+
rt = k6ext.Runtime(ctx)
60+
o = opts.ToObject(rt)
61+
)
62+
for _, k := range o.Keys() {
63+
v := o.Get(k)
64+
if v.Export() == nil {
65+
continue // don't override the defaults on `null``
66+
}
67+
var err error
68+
switch k {
69+
case "args":
70+
err = exportOpt(rt, k, v, &l.Args)
71+
case "debug":
72+
l.Debug, err = parseBoolOpt(k, v)
73+
case "devtools":
74+
l.Devtools, err = parseBoolOpt(k, v)
75+
case "env":
76+
err = exportOpt(rt, k, v, &l.Env)
77+
case "executablePath":
78+
l.ExecutablePath, err = parseStrOpt(k, v)
79+
case "headless":
80+
l.Headless, err = parseBoolOpt(k, v)
81+
case "ignoreDefaultArgs":
82+
err = exportOpt(rt, k, v, &l.IgnoreDefaultArgs)
83+
case "logCategoryFilter":
84+
l.LogCategoryFilter, err = parseStrOpt(k, v)
85+
case "proxy":
86+
err = exportOpt(rt, k, v, &l.Proxy)
87+
case "slowMo":
88+
l.SlowMo, err = parseTimeOpt(k, v)
89+
case "timeout":
90+
l.Timeout, err = parseTimeOpt(k, v)
91+
}
92+
if err != nil {
93+
return err
94+
}
95+
}
96+
97+
return nil
98+
}

common/browser_options_test.go

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package common
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/grafana/xk6-browser/k6ext/k6test"
11+
)
12+
13+
func TestBrowserLaunchOptionsParse(t *testing.T) {
14+
t.Parallel()
15+
16+
for name, tt := range map[string]struct {
17+
opts map[string]any
18+
assert func(testing.TB, *LaunchOptions)
19+
err string
20+
}{
21+
"defaults": {
22+
opts: map[string]any{},
23+
assert: func(tb testing.TB, lo *LaunchOptions) {
24+
tb.Helper()
25+
assert.Equal(t, &LaunchOptions{
26+
Env: make(map[string]string),
27+
Headless: true,
28+
LogCategoryFilter: ".*",
29+
Timeout: DefaultTimeout,
30+
}, lo)
31+
},
32+
},
33+
"nulls": { // don't override the defaults on `null`
34+
opts: map[string]any{
35+
"env": nil,
36+
"headless": nil,
37+
"logCategoryFilter": nil,
38+
"timeout": nil,
39+
},
40+
assert: func(tb testing.TB, lo *LaunchOptions) {
41+
tb.Helper()
42+
assert.Equal(tb, &LaunchOptions{
43+
Env: make(map[string]string),
44+
Headless: true,
45+
LogCategoryFilter: ".*",
46+
Timeout: DefaultTimeout,
47+
}, lo)
48+
},
49+
},
50+
"args": {
51+
opts: map[string]any{
52+
"args": []any{"browser-arg1='value1", "browser-arg2=value2", "browser-flag"},
53+
},
54+
assert: func(tb testing.TB, lo *LaunchOptions) {
55+
tb.Helper()
56+
require.Len(tb, lo.Args, 3)
57+
assert.Equal(tb, "browser-arg1='value1", lo.Args[0])
58+
assert.Equal(tb, "browser-arg2=value2", lo.Args[1])
59+
assert.Equal(tb, "browser-flag", lo.Args[2])
60+
},
61+
},
62+
"args_err": {
63+
opts: map[string]any{
64+
"args": 1,
65+
},
66+
err: "args should be an array of strings",
67+
},
68+
"debug": {
69+
opts: map[string]any{"debug": true},
70+
assert: func(tb testing.TB, lo *LaunchOptions) {
71+
tb.Helper()
72+
assert.True(t, lo.Debug)
73+
},
74+
},
75+
"debug_err": {
76+
opts: map[string]any{"debug": "true"},
77+
err: "debug should be a boolean",
78+
},
79+
"devtools": {
80+
opts: map[string]any{
81+
"devtools": true,
82+
},
83+
assert: func(tb testing.TB, lo *LaunchOptions) {
84+
tb.Helper()
85+
assert.True(t, lo.Devtools)
86+
},
87+
},
88+
"devtools_err": {
89+
opts: map[string]any{"devtools": "true"},
90+
err: "devtools should be a boolean",
91+
},
92+
"env": {
93+
opts: map[string]any{
94+
"env": map[string]string{"key": "value"},
95+
},
96+
assert: func(tb testing.TB, lo *LaunchOptions) {
97+
tb.Helper()
98+
assert.Equal(t, map[string]string{"key": "value"}, lo.Env)
99+
},
100+
},
101+
"env_err": {
102+
opts: map[string]any{"env": 1},
103+
err: "env should be a map",
104+
},
105+
"executablePath": {
106+
opts: map[string]any{
107+
"executablePath": "cmd/somewhere",
108+
},
109+
assert: func(tb testing.TB, lo *LaunchOptions) {
110+
tb.Helper()
111+
assert.Equal(t, "cmd/somewhere", lo.ExecutablePath)
112+
},
113+
},
114+
"executablePath_err": {
115+
opts: map[string]any{"executablePath": 1},
116+
err: "executablePath should be a string",
117+
},
118+
"headless": {
119+
opts: map[string]any{
120+
"headless": false,
121+
},
122+
assert: func(tb testing.TB, lo *LaunchOptions) {
123+
tb.Helper()
124+
assert.False(t, lo.Headless)
125+
},
126+
},
127+
"headless_err": {
128+
opts: map[string]any{"headless": "true"},
129+
err: "headless should be a boolean",
130+
},
131+
"ignoreDefaultArgs": {
132+
opts: map[string]any{
133+
"ignoreDefaultArgs": []string{"--hide-scrollbars", "--hide-something"},
134+
},
135+
assert: func(tb testing.TB, lo *LaunchOptions) {
136+
tb.Helper()
137+
assert.Len(t, lo.IgnoreDefaultArgs, 2)
138+
assert.Equal(t, "--hide-scrollbars", lo.IgnoreDefaultArgs[0])
139+
assert.Equal(t, "--hide-something", lo.IgnoreDefaultArgs[1])
140+
},
141+
},
142+
"ignoreDefaultArgs_err": {
143+
opts: map[string]any{"ignoreDefaultArgs": "ABC"},
144+
err: "ignoreDefaultArgs should be an array of strings",
145+
},
146+
"logCategoryFilter": {
147+
opts: map[string]any{
148+
"logCategoryFilter": "**",
149+
},
150+
assert: func(tb testing.TB, lo *LaunchOptions) {
151+
tb.Helper()
152+
assert.Equal(t, "**", lo.LogCategoryFilter)
153+
},
154+
},
155+
"logCategoryFilter_err": {
156+
opts: map[string]any{"logCategoryFilter": 1},
157+
err: "logCategoryFilter should be a string",
158+
},
159+
"proxy": {
160+
opts: map[string]any{
161+
"proxy": ProxyOptions{
162+
Server: "serverVal",
163+
Bypass: "bypassVal",
164+
Username: "usernameVal",
165+
Password: "passwordVal",
166+
},
167+
},
168+
assert: func(tb testing.TB, lo *LaunchOptions) {
169+
tb.Helper()
170+
assert.Equal(t, ProxyOptions{
171+
Server: "serverVal",
172+
Bypass: "bypassVal",
173+
Username: "usernameVal",
174+
Password: "passwordVal",
175+
}, lo.Proxy)
176+
},
177+
},
178+
"proxy_err": {
179+
opts: map[string]any{"proxy": 1},
180+
err: "proxy should be an object",
181+
},
182+
"slowMo": {
183+
opts: map[string]any{
184+
"slowMo": "5s",
185+
},
186+
assert: func(tb testing.TB, lo *LaunchOptions) {
187+
tb.Helper()
188+
assert.Equal(t, 5*time.Second, lo.SlowMo)
189+
},
190+
},
191+
"slowMo_err": {
192+
opts: map[string]any{"slowMo": "ABC"},
193+
err: "slowMo should be a time duration value",
194+
},
195+
"timeout": {
196+
opts: map[string]any{
197+
"timeout": "10s",
198+
},
199+
assert: func(tb testing.TB, lo *LaunchOptions) {
200+
tb.Helper()
201+
assert.Equal(t, 10*time.Second, lo.Timeout)
202+
},
203+
},
204+
"timeout_err": {
205+
opts: map[string]any{"timeout": "ABC"},
206+
err: "timeout should be a time duration value",
207+
},
208+
} {
209+
tt := tt
210+
t.Run(name, func(t *testing.T) {
211+
t.Parallel()
212+
var (
213+
vu = k6test.NewVU(t)
214+
lo = NewLaunchOptions()
215+
)
216+
err := lo.Parse(vu.Context(), vu.ToGojaValue(tt.opts))
217+
if tt.err != "" {
218+
require.ErrorContains(t, err, tt.err)
219+
} else {
220+
require.NoError(t, err)
221+
tt.assert(t, lo)
222+
}
223+
})
224+
}
225+
}

0 commit comments

Comments
 (0)