From b6e94cc64a5ada7d48bde14ca92ef0a873149f6f Mon Sep 17 00:00:00 2001 From: ka3de Date: Wed, 3 May 2023 10:17:01 +0200 Subject: [PATCH 01/22] Rename LaunchOptions to BrowserOptions LaunchOptions struct serves also for browser options when using connect method, therefore it makes sense to rename it to a more generic naming that defines its purpose. Co-authored-by: ankur22 --- chromium/browser_type.go | 40 ++++++++++----------- chromium/browser_type_test.go | 24 ++++++------- common/browser.go | 16 ++++----- common/browser_context.go | 2 +- common/browser_context_test.go | 2 +- common/browser_options.go | 55 +++++++++++++++-------------- common/browser_options_test.go | 40 ++++++++++----------- common/browser_test.go | 4 +-- common/context.go | 17 +++++---- common/frame_session.go | 2 +- common/hooks.go | 2 +- tests/browser_test.go | 2 +- tests/frame_test.go | 2 +- tests/launch_options_slowmo_test.go | 2 +- tests/test_browser.go | 32 ++++++++--------- 15 files changed, 124 insertions(+), 118 deletions(-) diff --git a/chromium/browser_type.go b/chromium/browser_type.go index 5582e50dd..31b4b6b83 100644 --- a/chromium/browser_type.go +++ b/chromium/browser_type.go @@ -60,7 +60,7 @@ func NewBrowserType(vu k6modules.VU) api.BrowserType { func (b *BrowserType) init( opts goja.Value, isRemoteBrowser bool, -) (context.Context, *common.LaunchOptions, *log.Logger, error) { +) (context.Context, *common.BrowserOptions, *log.Logger, error) { ctx := b.initContext() logger, err := makeLogger(ctx) @@ -68,26 +68,26 @@ func (b *BrowserType) init( return nil, nil, nil, fmt.Errorf("error setting up logger: %w", err) } - var launchOpts *common.LaunchOptions + var browserOpts *common.BrowserOptions if isRemoteBrowser { - launchOpts = common.NewRemoteBrowserLaunchOptions() + browserOpts = common.NewRemoteBrowserOptions() } else { - launchOpts = common.NewLaunchOptions() + browserOpts = common.NewLocalBrowserOptions() } - if err = launchOpts.Parse(ctx, logger, opts); err != nil { - return nil, nil, nil, fmt.Errorf("error parsing launch options: %w", err) + if err = browserOpts.Parse(ctx, logger, opts); err != nil { + return nil, nil, nil, fmt.Errorf("error parsing browser options: %w", err) } - ctx = common.WithLaunchOptions(ctx, launchOpts) + ctx = common.WithBrowserOptions(ctx, browserOpts) - if err := logger.SetCategoryFilter(launchOpts.LogCategoryFilter); err != nil { + if err := logger.SetCategoryFilter(browserOpts.LogCategoryFilter); err != nil { return nil, nil, nil, fmt.Errorf("error setting category filter: %w", err) } - if launchOpts.Debug { + if browserOpts.Debug { _ = logger.SetLevel("debug") } - return ctx, launchOpts, logger, nil + return ctx, browserOpts, logger, nil } func (b *BrowserType) initContext() context.Context { @@ -100,16 +100,16 @@ func (b *BrowserType) initContext() context.Context { // Connect attaches k6 browser to an existing browser instance. func (b *BrowserType) Connect(wsEndpoint string, opts goja.Value) api.Browser { - ctx, launchOpts, logger, err := b.init(opts, true) + ctx, browserOpts, logger, err := b.init(opts, true) if err != nil { k6ext.Panic(ctx, "initializing browser type: %w", err) } - bp, err := b.connect(ctx, wsEndpoint, launchOpts, logger) + bp, err := b.connect(ctx, wsEndpoint, browserOpts, logger) if err != nil { err = &k6ext.UserFriendlyError{ Err: err, - Timeout: launchOpts.Timeout, + Timeout: browserOpts.Timeout, } k6ext.Panic(ctx, "%w", err) } @@ -118,7 +118,7 @@ func (b *BrowserType) Connect(wsEndpoint string, opts goja.Value) api.Browser { } func (b *BrowserType) connect( - ctx context.Context, wsURL string, opts *common.LaunchOptions, logger *log.Logger, + ctx context.Context, wsURL string, opts *common.BrowserOptions, logger *log.Logger, ) (*common.Browser, error) { browserProc, err := b.link(ctx, wsURL, logger) if browserProc == nil { @@ -155,16 +155,16 @@ func (b *BrowserType) link( // Launch allocates a new Chrome browser process and returns a new api.Browser value, // which can be used for controlling the Chrome browser. func (b *BrowserType) Launch(opts goja.Value) (_ api.Browser, browserProcessID int) { - ctx, launchOpts, logger, err := b.init(opts, false) + ctx, browserOpts, logger, err := b.init(opts, false) if err != nil { k6ext.Panic(ctx, "initializing browser type: %w", err) } - bp, pid, err := b.launch(ctx, launchOpts, logger) + bp, pid, err := b.launch(ctx, browserOpts, logger) if err != nil { err = &k6ext.UserFriendlyError{ Err: err, - Timeout: launchOpts.Timeout, + Timeout: browserOpts.Timeout, } k6ext.Panic(ctx, "%w", err) } @@ -173,7 +173,7 @@ func (b *BrowserType) Launch(opts goja.Value) (_ api.Browser, browserProcessID i } func (b *BrowserType) launch( - ctx context.Context, opts *common.LaunchOptions, logger *log.Logger, + ctx context.Context, opts *common.BrowserOptions, logger *log.Logger, ) (_ *common.Browser, pid int, _ error) { flags, err := prepareFlags(opts, &(b.vu.State()).Options) if err != nil { @@ -231,7 +231,7 @@ func (b *BrowserType) Name() string { // allocate starts a new Chromium browser process and returns it. func (b *BrowserType) allocate( - ctx context.Context, opts *common.LaunchOptions, + ctx context.Context, opts *common.BrowserOptions, flags map[string]any, dataDir *storage.Dir, logger *log.Logger, ) (_ *common.BrowserProcess, rerr error) { @@ -322,7 +322,7 @@ func parseArgs(flags map[string]any) ([]string, error) { return args, nil } -func prepareFlags(lopts *common.LaunchOptions, k6opts *k6lib.Options) (map[string]any, error) { +func prepareFlags(lopts *common.BrowserOptions, k6opts *k6lib.Options) (map[string]any, error) { // After Puppeteer's and Playwright's default behavior. f := map[string]any{ "disable-background-networking": true, diff --git a/chromium/browser_type_test.go b/chromium/browser_type_test.go index 296790e03..47cf31a4c 100644 --- a/chromium/browser_type_test.go +++ b/chromium/browser_type_test.go @@ -28,36 +28,36 @@ func TestBrowserTypePrepareFlags(t *testing.T) { testCases := []struct { flag string - changeOpts *common.LaunchOptions + changeOpts *common.BrowserOptions changeK6Opts *k6lib.Options expInitVal, expChangedVal any post func(t *testing.T, flags map[string]any) }{ { flag: "hide-scrollbars", - changeOpts: &common.LaunchOptions{IgnoreDefaultArgs: []string{"hide-scrollbars"}, Headless: true}, + changeOpts: &common.BrowserOptions{IgnoreDefaultArgs: []string{"hide-scrollbars"}, Headless: true}, }, { flag: "hide-scrollbars", - changeOpts: &common.LaunchOptions{Headless: true}, + changeOpts: &common.BrowserOptions{Headless: true}, expChangedVal: true, }, { flag: "browser-arg", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{"browser-arg=value"}}, + changeOpts: &common.BrowserOptions{Args: []string{"browser-arg=value"}}, expChangedVal: "value", }, { flag: "browser-arg-flag", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{"browser-arg-flag"}}, + changeOpts: &common.BrowserOptions{Args: []string{"browser-arg-flag"}}, expChangedVal: "", }, { flag: "browser-arg-trim-double-quote", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{ + changeOpts: &common.BrowserOptions{Args: []string{ ` browser-arg-trim-double-quote = "value " `, }}, expChangedVal: "value ", @@ -65,7 +65,7 @@ func TestBrowserTypePrepareFlags(t *testing.T) { { flag: "browser-arg-trim-single-quote", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{ + changeOpts: &common.BrowserOptions{Args: []string{ ` browser-arg-trim-single-quote=' value '`, }}, expChangedVal: " value ", @@ -73,7 +73,7 @@ func TestBrowserTypePrepareFlags(t *testing.T) { { flag: "browser-args", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{ + changeOpts: &common.BrowserOptions{Args: []string{ "browser-arg1='value1", "browser-arg2=''value2''", "browser-flag", }}, post: func(t *testing.T, flags map[string]any) { @@ -87,7 +87,7 @@ func TestBrowserTypePrepareFlags(t *testing.T) { { flag: "host-resolver-rules", expInitVal: nil, - changeOpts: &common.LaunchOptions{Args: []string{ + changeOpts: &common.BrowserOptions{Args: []string{ `host-resolver-rules="MAP * www.example.com, EXCLUDE *.youtube.*"`, }}, changeK6Opts: &k6lib.Options{ @@ -99,14 +99,14 @@ func TestBrowserTypePrepareFlags(t *testing.T) { { flag: "host-resolver-rules", expInitVal: nil, - changeOpts: &common.LaunchOptions{}, + changeOpts: &common.BrowserOptions{}, changeK6Opts: &k6lib.Options{}, expChangedVal: nil, }, { flag: "headless", expInitVal: false, - changeOpts: &common.LaunchOptions{Headless: true}, + changeOpts: &common.BrowserOptions{Headless: true}, expChangedVal: true, post: func(t *testing.T, flags map[string]any) { t.Helper() @@ -124,7 +124,7 @@ func TestBrowserTypePrepareFlags(t *testing.T) { t.Run(tc.flag, func(t *testing.T) { t.Parallel() - flags, err := prepareFlags(&common.LaunchOptions{}, nil) + flags, err := prepareFlags(&common.BrowserOptions{}, nil) require.NoError(t, err, "failed to prepare flags") if tc.expInitVal != nil { diff --git a/common/browser.go b/common/browser.go index 3c1d53d7a..f1b7348ba 100644 --- a/common/browser.go +++ b/common/browser.go @@ -44,7 +44,7 @@ type Browser struct { state int64 browserProc *BrowserProcess - launchOpts *LaunchOptions + browserOpts *BrowserOptions // Connection to the browser to talk CDP protocol. // A *Connection is saved to this field, see: connect(). @@ -78,10 +78,10 @@ func NewBrowser( ctx context.Context, cancel context.CancelFunc, browserProc *BrowserProcess, - launchOpts *LaunchOptions, + browserOpts *BrowserOptions, logger *log.Logger, ) (*Browser, error) { - b := newBrowser(ctx, cancel, browserProc, launchOpts, logger) + b := newBrowser(ctx, cancel, browserProc, browserOpts, logger) if err := b.connect(); err != nil { return nil, err } @@ -93,7 +93,7 @@ func newBrowser( ctx context.Context, cancelFn context.CancelFunc, browserProc *BrowserProcess, - launchOpts *LaunchOptions, + browserOpts *BrowserOptions, logger *log.Logger, ) *Browser { return &Browser{ @@ -102,7 +102,7 @@ func newBrowser( cancelFn: cancelFn, state: int64(BrowserStateOpen), browserProc: browserProc, - launchOpts: launchOpts, + browserOpts: browserOpts, contexts: make(map[cdp.BrowserContextID]*BrowserContext), pages: make(map[target.ID]*Page), sessionIDtoTargetID: make(map[target.SessionID]target.ID), @@ -384,7 +384,7 @@ func (b *Browser) newPageInContext(id cdp.BrowserContextID) (*Page, error) { return nil, fmt.Errorf("missing browser context: %s", id) } - ctx, cancel := context.WithTimeout(b.ctx, b.launchOpts.Timeout) + ctx, cancel := context.WithTimeout(b.ctx, b.browserOpts.Timeout) defer cancel() // buffer of one is for sending the target ID whether an event handler @@ -425,7 +425,7 @@ func (b *Browser) newPageInContext(id cdp.BrowserContextID) (*Page, error) { case <-ctx.Done(): err = &k6ext.UserFriendlyError{ Err: ctx.Err(), - Timeout: b.launchOpts.Timeout, + Timeout: b.browserOpts.Timeout, } b.logger.Debugf("Browser:newPageInContext:<-ctx.Done", "tid:%v bctxid:%v err:%v", tid, id, err) } @@ -462,7 +462,7 @@ func (b *Browser) Close() { // If the browser is not being executed remotely, send the Browser.close CDP // command, which triggers the browser process to exit. - if !b.launchOpts.isRemoteBrowser { + if !b.browserOpts.isRemoteBrowser { var closeErr *websocket.CloseError err := cdpbrowser.Close().Do(cdp.WithExecutor(b.ctx, b.conn)) if err != nil && !errors.As(err, &closeErr) { diff --git a/common/browser_context.go b/common/browser_context.go index b98e8a6da..82a4540b8 100644 --- a/common/browser_context.go +++ b/common/browser_context.go @@ -368,7 +368,7 @@ func (b *BrowserContext) WaitForEvent(event string, optsOrPredicate goja.Value) isCallable bool predicateFn goja.Callable // TODO: Find out whether * time.Second is necessary. - timeout = b.browser.launchOpts.Timeout * time.Second //nolint:durationcheck + timeout = b.browser.browserOpts.Timeout * time.Second //nolint:durationcheck ) if gojaValueExists(optsOrPredicate) { switch optsOrPredicate.ExportType() { diff --git a/common/browser_context_test.go b/common/browser_context_test.go index 6cf1386af..be52dccd9 100644 --- a/common/browser_context_test.go +++ b/common/browser_context_test.go @@ -17,7 +17,7 @@ func TestNewBrowserContext(t *testing.T) { t.Run("add_web_vital_js_scripts_to_context", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) logger := log.NewNullLogger() - b := newBrowser(ctx, cancel, nil, NewLaunchOptions(), logger) + b := newBrowser(ctx, cancel, nil, NewLocalBrowserOptions(), logger) vu := k6test.NewVU(t) ctx = k6ext.WithVU(ctx, vu) diff --git a/common/browser_options.go b/common/browser_options.go index f1c71fecf..b2a3a8a47 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -21,8 +21,8 @@ const ( optTimeout = "timeout" ) -// LaunchOptions stores browser launch options. -type LaunchOptions struct { +// BrowserOptions stores browser options. +type BrowserOptions struct { Args []string Debug bool ExecutablePath string @@ -37,23 +37,24 @@ type LaunchOptions struct { // LaunchPersistentContextOptions stores browser launch options for persistent context. type LaunchPersistentContextOptions struct { - LaunchOptions + BrowserOptions BrowserContextOptions } -// NewLaunchOptions returns a new LaunchOptions. -func NewLaunchOptions() *LaunchOptions { - return &LaunchOptions{ +// NewLocalBrowserOptions returns a new BrowserOptions +// for a browser launched in the local machine. +func NewLocalBrowserOptions() *BrowserOptions { + return &BrowserOptions{ Headless: true, LogCategoryFilter: ".*", Timeout: DefaultTimeout, } } -// NewRemoteBrowserLaunchOptions returns a new LaunchOptions +// NewRemoteBrowserOptions returns a new BrowserOptions // for a browser running in a remote machine. -func NewRemoteBrowserLaunchOptions() *LaunchOptions { - return &LaunchOptions{ +func NewRemoteBrowserOptions() *BrowserOptions { + return &BrowserOptions{ Headless: true, LogCategoryFilter: ".*", Timeout: DefaultTimeout, @@ -61,8 +62,8 @@ func NewRemoteBrowserLaunchOptions() *LaunchOptions { } } -// Parse parses launch options from a JS object. -func (l *LaunchOptions) Parse(ctx context.Context, logger *log.Logger, opts goja.Value) error { //nolint:cyclop +// Parse parses browser options from a JS object. +func (bo *BrowserOptions) Parse(ctx context.Context, logger *log.Logger, opts goja.Value) error { //nolint:cyclop // when opts is nil, we just return the default options without error. if !gojaValueExists(opts) { return nil @@ -71,41 +72,41 @@ func (l *LaunchOptions) Parse(ctx context.Context, logger *log.Logger, opts goja rt = k6ext.Runtime(ctx) o = opts.ToObject(rt) defaults = map[string]any{ - optHeadless: l.Headless, - optLogCategoryFilter: l.LogCategoryFilter, - optTimeout: l.Timeout, + optHeadless: bo.Headless, + optLogCategoryFilter: bo.LogCategoryFilter, + optTimeout: bo.Timeout, } ) for _, k := range o.Keys() { - if l.shouldIgnoreIfBrowserIsRemote(k) { - logger.Warnf("LaunchOptions", "setting %s option is disallowed when browser is remote", k) + if bo.shouldIgnoreIfBrowserIsRemote(k) { + logger.Warnf("BrowserOptions", "setting %s option is disallowed when browser is remote", k) continue } v := o.Get(k) if v.Export() == nil { if dv, ok := defaults[k]; ok { - logger.Warnf("LaunchOptions", "%s was null and set to its default: %v", k, dv) + logger.Warnf("BrowserOptions", "%s was null and set to its default: %v", k, dv) } continue } var err error switch k { case optArgs: - err = exportOpt(rt, k, v, &l.Args) + err = exportOpt(rt, k, v, &bo.Args) case optDebug: - l.Debug, err = parseBoolOpt(k, v) + bo.Debug, err = parseBoolOpt(k, v) case optExecutablePath: - l.ExecutablePath, err = parseStrOpt(k, v) + bo.ExecutablePath, err = parseStrOpt(k, v) case optHeadless: - l.Headless, err = parseBoolOpt(k, v) + bo.Headless, err = parseBoolOpt(k, v) case optIgnoreDefaultArgs: - err = exportOpt(rt, k, v, &l.IgnoreDefaultArgs) + err = exportOpt(rt, k, v, &bo.IgnoreDefaultArgs) case optLogCategoryFilter: - l.LogCategoryFilter, err = parseStrOpt(k, v) + bo.LogCategoryFilter, err = parseStrOpt(k, v) case optSlowMo: - l.SlowMo, err = parseTimeOpt(k, v) + bo.SlowMo, err = parseTimeOpt(k, v) case optTimeout: - l.Timeout, err = parseTimeOpt(k, v) + bo.Timeout, err = parseTimeOpt(k, v) } if err != nil { return err @@ -115,8 +116,8 @@ func (l *LaunchOptions) Parse(ctx context.Context, logger *log.Logger, opts goja return nil } -func (l *LaunchOptions) shouldIgnoreIfBrowserIsRemote(opt string) bool { - if !l.isRemoteBrowser { +func (bo *BrowserOptions) shouldIgnoreIfBrowserIsRemote(opt string) bool { + if !bo.isRemoteBrowser { return false } diff --git a/common/browser_options_test.go b/common/browser_options_test.go index ffac6dcab..5e9fa9e9f 100644 --- a/common/browser_options_test.go +++ b/common/browser_options_test.go @@ -11,10 +11,10 @@ import ( "github.com/grafana/xk6-browser/log" ) -func TestBrowserLaunchOptionsParse(t *testing.T) { +func TestBrowserOptionsParse(t *testing.T) { t.Parallel() - defaultOptions := &LaunchOptions{ + defaultOptions := &BrowserOptions{ Headless: true, LogCategoryFilter: ".*", Timeout: DefaultTimeout, @@ -22,20 +22,20 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { for name, tt := range map[string]struct { opts map[string]any - assert func(testing.TB, *LaunchOptions) + assert func(testing.TB, *BrowserOptions) err string isRemoteBrowser bool }{ "defaults": { opts: map[string]any{}, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, defaultOptions, lo) }, }, "defaults_nil": { // providing nil option returns default options opts: nil, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, defaultOptions, lo) }, @@ -54,9 +54,9 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { "slowMo": time.Second, "timeout": time.Second, }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() - assert.Equal(t, &LaunchOptions{ + assert.Equal(t, &BrowserOptions{ // disallowed: Headless: true, // allowed: @@ -75,9 +75,9 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { "logCategoryFilter": nil, "timeout": nil, }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() - assert.Equal(tb, &LaunchOptions{ + assert.Equal(tb, &BrowserOptions{ Headless: true, LogCategoryFilter: ".*", Timeout: DefaultTimeout, @@ -88,7 +88,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "args": []any{"browser-arg1='value1", "browser-arg2=value2", "browser-flag"}, }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() require.Len(tb, lo.Args, 3) assert.Equal(tb, "browser-arg1='value1", lo.Args[0]) @@ -104,7 +104,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { }, "debug": { opts: map[string]any{"debug": true}, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.True(t, lo.Debug) }, @@ -117,7 +117,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "executablePath": "cmd/somewhere", }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, "cmd/somewhere", lo.ExecutablePath) }, @@ -130,7 +130,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "headless": false, }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.False(t, lo.Headless) }, @@ -143,7 +143,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "ignoreDefaultArgs": []string{"--hide-scrollbars", "--hide-something"}, }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Len(t, lo.IgnoreDefaultArgs, 2) assert.Equal(t, "--hide-scrollbars", lo.IgnoreDefaultArgs[0]) @@ -158,7 +158,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "logCategoryFilter": "**", }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, "**", lo.LogCategoryFilter) }, @@ -171,7 +171,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "slowMo": "5s", }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, 5*time.Second, lo.SlowMo) }, @@ -184,7 +184,7 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { opts: map[string]any{ "timeout": "10s", }, - assert: func(tb testing.TB, lo *LaunchOptions) { + assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, 10*time.Second, lo.Timeout) }, @@ -199,13 +199,13 @@ func TestBrowserLaunchOptionsParse(t *testing.T) { t.Parallel() var ( vu = k6test.NewVU(t) - lo *LaunchOptions + lo *BrowserOptions ) if tt.isRemoteBrowser { - lo = NewRemoteBrowserLaunchOptions() + lo = NewRemoteBrowserOptions() } else { - lo = NewLaunchOptions() + lo = NewLocalBrowserOptions() } err := lo.Parse(vu.Context(), log.NewNullLogger(), vu.ToGojaValue(tt.opts)) diff --git a/common/browser_test.go b/common/browser_test.go index 3a54803d1..c01539ade 100644 --- a/common/browser_test.go +++ b/common/browser_test.go @@ -26,7 +26,7 @@ func TestBrowserNewPageInContext(t *testing.T) { newTestCase := func(id cdp.BrowserContextID) *testCase { ctx, cancel := context.WithCancel(context.Background()) logger := log.NewNullLogger() - b := newBrowser(ctx, cancel, nil, NewLaunchOptions(), logger) + b := newBrowser(ctx, cancel, nil, NewLocalBrowserOptions(), logger) // set a new browser context in the browser with `id`, so that newPageInContext can find it. var err error vu := k6test.NewVU(t) @@ -129,7 +129,7 @@ func TestBrowserNewPageInContext(t *testing.T) { // set a lower timeout for catching the timeout error. const timeout = 100 * time.Millisecond // set the timeout for the browser value. - tc.b.launchOpts.Timeout = timeout + tc.b.browserOpts.Timeout = timeout tc.b.conn = fakeConn{ execute: func(context.Context, string, easyjson.Marshaler, easyjson.Unmarshaler) error { // executor takes more time than the timeout. diff --git a/common/context.go b/common/context.go index 8efe8689c..56f8d643d 100644 --- a/common/context.go +++ b/common/context.go @@ -7,7 +7,7 @@ import ( type ctxKey int const ( - ctxKeyLaunchOptions ctxKey = iota + ctxKeyBrowserOptions ctxKey = iota ctxKeyHooks ctxKeyIterationID ) @@ -35,16 +35,21 @@ func GetIterationID(ctx context.Context) string { return s } -func WithLaunchOptions(ctx context.Context, opts *LaunchOptions) context.Context { - return context.WithValue(ctx, ctxKeyLaunchOptions, opts) +// WithBrowserOptions adds the browser options to the context. +func WithBrowserOptions(ctx context.Context, opts *BrowserOptions) context.Context { + return context.WithValue(ctx, ctxKeyBrowserOptions, opts) } -func GetLaunchOptions(ctx context.Context) *LaunchOptions { - v := ctx.Value(ctxKeyLaunchOptions) +// GetBrowserOptions returns the browser options attached to the context. +func GetBrowserOptions(ctx context.Context) *BrowserOptions { + v := ctx.Value(ctxKeyBrowserOptions) if v == nil { return nil } - return v.(*LaunchOptions) + if bo, ok := v.(*BrowserOptions); ok { + return bo + } + return nil } // contextWithDoneChan returns a new context that is canceled either diff --git a/common/frame_session.go b/common/frame_session.go index 969b56ab6..9932ee22c 100644 --- a/common/frame_session.go +++ b/common/frame_session.go @@ -1076,7 +1076,7 @@ func (fs *FrameSession) updateViewport() error { // add an inset to viewport depending on the operating system. // this won't add an inset if we're running in headless mode. viewport.calculateInset( - fs.page.browserCtx.browser.launchOpts.Headless, + fs.page.browserCtx.browser.browserOpts.Headless, runtime.GOOS, ) action2 := browser.SetWindowBounds(fs.windowID, &browser.Bounds{ diff --git a/common/hooks.go b/common/hooks.go index abfa22005..dfa2340c2 100644 --- a/common/hooks.go +++ b/common/hooks.go @@ -30,7 +30,7 @@ func applySlowMo(ctx context.Context) { } func defaultSlowMo(ctx context.Context) { - sm := GetLaunchOptions(ctx).SlowMo + sm := GetBrowserOptions(ctx).SlowMo if sm <= 0 { return } diff --git a/tests/browser_test.go b/tests/browser_test.go index e1df565a9..a85c88e58 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -146,7 +146,7 @@ func TestBrowserCrashErr(t *testing.T) { t.Parallel() assertExceptionContains(t, goja.New(), func() { - lopts := defaultLaunchOpts() + lopts := defaultBrowserOpts() lopts.Args = []any{"remote-debugging-port=99999"} newTestBrowser(t, lopts) diff --git a/tests/frame_test.go b/tests/frame_test.go index 4dfc4f8b9..4fab0cff9 100644 --- a/tests/frame_test.go +++ b/tests/frame_test.go @@ -84,7 +84,7 @@ func TestFrameNoPanicWithEmbeddedIFrame(t *testing.T) { t.Parallel() - opts := defaultLaunchOpts() + opts := defaultBrowserOpts() opts.Headless = false tb := newTestBrowser(t, withFileServer(), opts) p := tb.NewPage(nil) diff --git a/tests/launch_options_slowmo_test.go b/tests/launch_options_slowmo_test.go index 1f7a9c457..d297a1cb4 100644 --- a/tests/launch_options_slowmo_test.go +++ b/tests/launch_options_slowmo_test.go @@ -12,7 +12,7 @@ import ( "github.com/grafana/xk6-browser/common" ) -func TestLaunchOptionsSlowMo(t *testing.T) { +func TestBrowserOptionsSlowMo(t *testing.T) { t.Parallel() if testing.Short() { diff --git a/tests/test_browser.go b/tests/test_browser.go index 68824378b..478dd02ed 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -45,14 +45,14 @@ type testBrowser struct { // It automatically closes it when `t` returns. // // opts provides a way to customize the newTestBrowser. -// see: withLaunchOptions for an example. +// see: withBrowserOptions for an example. func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tb.Helper() // set default options and then customize them var ( ctx context.Context - launchOpts = defaultLaunchOpts() + browserOpts = defaultBrowserOpts() enableHTTPMultiBin = false enableFileServer = false enableLogCache = false @@ -61,8 +61,8 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { ) for _, opt := range opts { switch opt := opt.(type) { - case withLaunchOptions: - launchOpts = opt + case withBrowserOptions: + browserOpts = opt case httpServerOption: enableHTTPMultiBin = true case fileServerOption: @@ -118,7 +118,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { state.Transport = testServer.HTTPTransport } - b, pid := bt.Launch(rt.ToValue(launchOpts)) + b, pid := bt.Launch(rt.ToValue(browserOpts)) cb, ok := b.(*common.Browser) if !ok { tb.Fatalf("testBrowser: unexpected browser %T", b) @@ -136,7 +136,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tbr := &testBrowser{ t: tb, - ctx: bt.Ctx, // This context has the additional wrapping of common.WithLaunchOptions + ctx: bt.Ctx, // This context has the additional wrapping of common.WithBrowserOptions http: testServer, vu: vu, logCache: lc, @@ -382,9 +382,9 @@ func (t testPromise) then(resolve any, reject ...any) testPromise { return t.tb.promise(p) } -// launchOptions provides a way to customize browser type -// launch options in tests. -type launchOptions struct { +// browserOptions provides a way to customize browser +// options in tests. +type browserOptions struct { Args []any `js:"args"` Debug bool `js:"debug"` Headless bool `js:"headless"` @@ -392,26 +392,26 @@ type launchOptions struct { Timeout string `js:"timeout"` } -// withLaunchOptions is a helper for increasing readability -// in tests while customizing the browser type launch options. +// withBrowserOptions is a helper for increasing readability +// in tests while customizing the browser options. // // example: // -// b := TestBrowser(t, withLaunchOptions{ +// b := TestBrowser(t, withBrowserOptions{ // SlowMo: "100s", // Timeout: "30s", // }) -type withLaunchOptions = launchOptions +type withBrowserOptions = browserOptions -// defaultLaunchOptions returns defaults for browser type launch options. +// defaultBrowserOpts returns defaults for browser options. // TestBrowser uses this for launching a browser type by default. -func defaultLaunchOpts() launchOptions { +func defaultBrowserOpts() browserOptions { headless := true if v, found := os.LookupEnv("XK6_BROWSER_TEST_HEADLESS"); found { headless, _ = strconv.ParseBool(v) } - return launchOptions{ + return browserOptions{ Headless: headless, SlowMo: "0s", Timeout: "30s", From 5b9f5678babe2136aab3c74f303310bb3f87b72c Mon Sep 17 00:00:00 2001 From: ka3de Date: Thu, 18 May 2023 12:03:08 +0200 Subject: [PATCH 02/22] Remove unused LaunchPersistentContextOptions struct --- common/browser_options.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/common/browser_options.go b/common/browser_options.go index b2a3a8a47..b7077ad04 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -35,12 +35,6 @@ type BrowserOptions struct { isRemoteBrowser bool // some options will be ignored if browser is in a remote machine } -// LaunchPersistentContextOptions stores browser launch options for persistent context. -type LaunchPersistentContextOptions struct { - BrowserOptions - BrowserContextOptions -} - // NewLocalBrowserOptions returns a new BrowserOptions // for a browser launched in the local machine. func NewLocalBrowserOptions() *BrowserOptions { From a320ea151a12b4310f75ea8443583eb549a9a39e Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:10:04 +0200 Subject: [PATCH 03/22] Upgrade k6 version --- go.mod | 7 ++++--- go.sum | 31 ++++++++++++++----------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index af742a2c2..4cfaab7de 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.19 require ( github.com/chromedp/cdproto v0.0.0-20221023212508-67ada9507fb2 - github.com/dop251/goja v0.0.0-20230216180835-5937a312edda + github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 github.com/gorilla/websocket v1.5.0 github.com/mailru/easyjson v0.7.7 github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 - go.k6.io/k6 v0.43.2-0.20230404074422-e40265226b89 + go.k6.io/k6 v0.44.2-0.20230509145227-d951eace0c16 golang.org/x/net v0.7.0 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde gopkg.in/guregu/null.v3 v3.3.0 @@ -28,6 +28,7 @@ require ( github.com/fatih/color v1.14.1 // indirect github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.16.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -35,7 +36,7 @@ require ( github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.18.1 // indirect + github.com/onsi/gomega v1.20.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect github.com/spf13/afero v1.1.2 // indirect diff --git a/go.sum b/go.sum index bd165b701..2e6b2c532 100644 --- a/go.sum +++ b/go.sum @@ -12,9 +12,9 @@ github.com/chromedp/cdproto v0.0.0-20221023212508-67ada9507fb2 h1:xESwMZNYkDnZf9 github.com/chromedp/cdproto v0.0.0-20221023212508-67ada9507fb2/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -23,8 +23,8 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230216180835-5937a312edda h1:yWEvdMtib3RbPysHDTNf/c3gerF5r+iMcmhlAeE6hEk= -github.com/dop251/goja v0.0.0-20230216180835-5937a312edda/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= +github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 h1:xkbJGxVnk5sM8/LXeTKaBOfAZrI+iqvIPyH8oK1c6CQ= +github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= @@ -51,11 +51,12 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= @@ -87,15 +88,12 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= @@ -124,8 +122,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.k6.io/k6 v0.43.2-0.20230404074422-e40265226b89 h1:aJSJixr/yPdHn7/bWP5sh1xrZMPJOjEYDgRNDS1sMbs= -go.k6.io/k6 v0.43.2-0.20230404074422-e40265226b89/go.mod h1:Azozhj76R5Fa1pPatdrTgl7+cL5JHBTIqp4aWroBMw4= +go.k6.io/k6 v0.44.2-0.20230509145227-d951eace0c16 h1:zBLEYAz7XCiiprlTv/Pk+pwgTuIbZf31Wej2yZ1yYXg= +go.k6.io/k6 v0.44.2-0.20230509145227-d951eace0c16/go.mod h1:4D2BnugW3gBWyI+yuKp18f+rFPkZIWBpxNe6Xe6MvGE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -140,7 +138,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= @@ -157,14 +154,13 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -178,6 +174,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= From 14561912b6f1f31145ee4d4cb1e95815dc0c8b7d Mon Sep 17 00:00:00 2001 From: ka3de Date: Mon, 8 May 2023 15:54:50 +0200 Subject: [PATCH 04/22] Move IsRemoteBrowser to new env package A new env package is a better fit for the current implementation of IsRemoteBrowser than the k6ext package. In this new env package we can define a lookupper function type to be used across the rest of the codebase as a reference for environment variables lookup functions. --- browser/mapping.go | 3 ++- env/doc.go | 2 ++ {k6ext => env}/env.go | 2 +- {k6ext => env}/env_test.go | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 env/doc.go rename {k6ext => env}/env.go (98%) rename {k6ext => env}/env_test.go (99%) diff --git a/browser/mapping.go b/browser/mapping.go index fe8012c6e..8d79bb441 100644 --- a/browser/mapping.go +++ b/browser/mapping.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/xk6-browser/api" "github.com/grafana/xk6-browser/chromium" + "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/k6error" "github.com/grafana/xk6-browser/k6ext" @@ -36,7 +37,7 @@ func mapBrowserToGoja(vu moduleVU) *goja.Object { obj = rt.NewObject() // TODO: Use k6 LookupEnv instead of OS package methods. // See https://github.com/grafana/xk6-browser/issues/822. - wsURL, isRemoteBrowser = k6ext.IsRemoteBrowser(os.LookupEnv) + wsURL, isRemoteBrowser = env.IsRemoteBrowser(os.LookupEnv) browserType = chromium.NewBrowserType(vu) ) for k, v := range mapBrowserType(vu, browserType, wsURL, isRemoteBrowser) { diff --git a/env/doc.go b/env/doc.go new file mode 100644 index 000000000..4f2054bb0 --- /dev/null +++ b/env/doc.go @@ -0,0 +1,2 @@ +// Package env provides methods to interact with environment setup. +package env diff --git a/k6ext/env.go b/env/env.go similarity index 98% rename from k6ext/env.go rename to env/env.go index cf0fd468b..7fbe4659a 100644 --- a/k6ext/env.go +++ b/env/env.go @@ -1,4 +1,4 @@ -package k6ext +package env import ( "crypto/rand" diff --git a/k6ext/env_test.go b/env/env_test.go similarity index 99% rename from k6ext/env_test.go rename to env/env_test.go index 624c42210..96100f9d6 100644 --- a/k6ext/env_test.go +++ b/env/env_test.go @@ -1,4 +1,4 @@ -package k6ext +package env import ( "testing" From 2a953fc4fb13d7e181f304f4fce58a133fb5bff9 Mon Sep 17 00:00:00 2001 From: ka3de Date: Mon, 8 May 2023 16:00:47 +0200 Subject: [PATCH 05/22] Export env.LookupFunc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to have a common environment variable lookupper function definition across the codebase. Co-authored-by: İnanç Gümüş --- env/env.go | 5 +++-- env/env_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/env/env.go b/env/env.go index 7fbe4659a..7a134ba59 100644 --- a/env/env.go +++ b/env/env.go @@ -6,7 +6,8 @@ import ( "strings" ) -type envLookupper func(key string) (string, bool) +// LookupFunc defines a function to look up a key from the environment. +type LookupFunc func(key string) (string, bool) // IsRemoteBrowser returns true and the corresponding CDP // WS URL if this one is set through the K6_BROWSER_WS_URL @@ -15,7 +16,7 @@ type envLookupper func(key string) (string, bool) // URLs, this method returns a randomly chosen URL from the list // so connections are done in a round-robin fashion for all the // entries in the list. -func IsRemoteBrowser(envLookup envLookupper) (wsURL string, isRemote bool) { +func IsRemoteBrowser(envLookup LookupFunc) (wsURL string, isRemote bool) { wsURL, isRemote = envLookup("K6_BROWSER_WS_URL") if !isRemote { return "", false diff --git a/env/env_test.go b/env/env_test.go index 96100f9d6..13ad541aa 100644 --- a/env/env_test.go +++ b/env/env_test.go @@ -11,7 +11,7 @@ func TestIsRemoteBrowser(t *testing.T) { testCases := []struct { name string - envLookup envLookupper + envLookup LookupFunc expIsRemote bool expValidWSURLs []string }{ From 33a765cfcfa2420ee7379eec3498d5466c5f597a Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:03:10 +0200 Subject: [PATCH 06/22] Add k6ext.GetScenarioOpts This function allows to retrieve the scenario options associated with the given VU context. It is required in order to validate the browser options defined at the scenario.options.browser level. --- k6ext/context.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/k6ext/context.go b/k6ext/context.go index 9c1de7698..f5f27193e 100644 --- a/k6ext/context.go +++ b/k6ext/context.go @@ -4,6 +4,7 @@ import ( "context" k6modules "go.k6.io/k6/js/modules" + k6lib "go.k6.io/k6/lib" "github.com/dop251/goja" ) @@ -51,3 +52,16 @@ func GetCustomMetrics(ctx context.Context) *CustomMetrics { func Runtime(ctx context.Context) *goja.Runtime { return GetVU(ctx).Runtime() } + +// GetScenarioOpts returns the browser options and environment variables associated +// with the given context. +func GetScenarioOpts(ctx context.Context, vu k6modules.VU) map[string]any { + ss := k6lib.GetScenarioState(ctx) + if ss == nil { + return nil + } + if so := vu.State().Options.Scenarios[ss.Name].GetScenarioOptions(); so != nil { + return so.Browser + } + return nil +} From 359028b55b8a24c2a33599cfef3a03b6053c5be9 Mon Sep 17 00:00:00 2001 From: ka3de Date: Mon, 8 May 2023 14:42:22 +0200 Subject: [PATCH 07/22] Refactor test browser args option Change its type from []any to []string to match the actual browser options struct. There is no need for it to be a slice of 'any'. Making it a string slice will also ease the set up as environment variable moving browser options to be parsed from ENV. --- tests/browser_test.go | 2 +- tests/test_browser.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/browser_test.go b/tests/browser_test.go index a85c88e58..4636fd1b7 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -147,7 +147,7 @@ func TestBrowserCrashErr(t *testing.T) { assertExceptionContains(t, goja.New(), func() { lopts := defaultBrowserOpts() - lopts.Args = []any{"remote-debugging-port=99999"} + lopts.Args = []string{"remote-debugging-port=99999"} newTestBrowser(t, lopts) }, "launching browser: Invalid devtools server port") diff --git a/tests/test_browser.go b/tests/test_browser.go index 478dd02ed..8854787ca 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -385,11 +385,11 @@ func (t testPromise) then(resolve any, reject ...any) testPromise { // browserOptions provides a way to customize browser // options in tests. type browserOptions struct { - Args []any `js:"args"` - Debug bool `js:"debug"` - Headless bool `js:"headless"` - SlowMo string `js:"slowMo"` - Timeout string `js:"timeout"` + Args []string `js:"args"` + Debug bool `js:"debug"` + Headless bool `js:"headless"` + SlowMo string `js:"slowMo"` + Timeout string `js:"timeout"` } // withBrowserOptions is a helper for increasing readability From bdee6e2a19b4f7dc90392f69d7a3304c20627903 Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:15:59 +0200 Subject: [PATCH 08/22] Move options parsing methods to browser_options.go These methods are only used in the browser options parsing method, therefore, by now, it makes more sense to keep them in the same file instead than in a separate generic options implementation. --- common/browser_options.go | 52 +++++++++++++++++++++++++++++++++ common/options.go | 60 --------------------------------------- 2 files changed, 52 insertions(+), 60 deletions(-) delete mode 100644 common/options.go diff --git a/common/browser_options.go b/common/browser_options.go index b7077ad04..e8477b2d0 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -2,9 +2,12 @@ package common import ( "context" + "fmt" + "reflect" "time" "github.com/dop251/goja" + "go.k6.io/k6/lib/types" "github.com/grafana/xk6-browser/k6ext" "github.com/grafana/xk6-browser/log" @@ -125,3 +128,52 @@ func (bo *BrowserOptions) shouldIgnoreIfBrowserIsRemote(opt string) bool { return ignore } + +func parseBoolOpt(key string, val goja.Value) (b bool, err error) { + if val.ExportType().Kind() != reflect.Bool { + return false, fmt.Errorf("%s should be a boolean", key) + } + b, _ = val.Export().(bool) + return b, nil +} + +func parseStrOpt(key string, val goja.Value) (s string, err error) { + if val.ExportType().Kind() != reflect.String { + return "", fmt.Errorf("%s should be a string", key) + } + return val.String(), nil +} + +func parseTimeOpt(key string, val goja.Value) (t time.Duration, err error) { + if t, err = types.GetDurationValue(val.String()); err != nil { + return time.Duration(0), fmt.Errorf("%s should be a time duration value: %w", key, err) + } + return +} + +// exportOpt exports src to dst and dynamically returns an error +// depending on the type if an error occurs. Panics if dst is not +// a pointer and not points to a map, struct, or slice. +func exportOpt[T any](rt *goja.Runtime, key string, src goja.Value, dst T) error { + typ := reflect.TypeOf(dst) + if typ.Kind() != reflect.Pointer { + panic("dst should be a pointer") + } + kind := typ.Elem().Kind() + s, ok := map[reflect.Kind]string{ + reflect.Map: "a map", + reflect.Struct: "an object", + reflect.Slice: "an array of", + }[kind] + if !ok { + panic("dst should be one of: map, struct, slice") + } + if err := rt.ExportTo(src, dst); err != nil { + if kind == reflect.Slice { + s += fmt.Sprintf(" %ss", typ.Elem().Elem()) + } + return fmt.Errorf("%s should be %s: %w", key, s, err) + } + + return nil +} diff --git a/common/options.go b/common/options.go deleted file mode 100644 index 9d4e5399c..000000000 --- a/common/options.go +++ /dev/null @@ -1,60 +0,0 @@ -package common - -import ( - "fmt" - "reflect" - "time" - - "github.com/dop251/goja" - - "go.k6.io/k6/lib/types" -) - -func parseBoolOpt(key string, val goja.Value) (b bool, err error) { - if val.ExportType().Kind() != reflect.Bool { - return false, fmt.Errorf("%s should be a boolean", key) - } - b, _ = val.Export().(bool) - return b, nil -} - -func parseStrOpt(key string, val goja.Value) (s string, err error) { - if val.ExportType().Kind() != reflect.String { - return "", fmt.Errorf("%s should be a string", key) - } - return val.String(), nil -} - -func parseTimeOpt(key string, val goja.Value) (t time.Duration, err error) { - if t, err = types.GetDurationValue(val.String()); err != nil { - return time.Duration(0), fmt.Errorf("%s should be a time duration value: %w", key, err) - } - return -} - -// exportOpt exports src to dst and dynamically returns an error -// depending on the type if an error occurs. Panics if dst is not -// a pointer and not points to a map, struct, or slice. -func exportOpt[T any](rt *goja.Runtime, key string, src goja.Value, dst T) error { - typ := reflect.TypeOf(dst) - if typ.Kind() != reflect.Pointer { - panic("dst should be a pointer") - } - kind := typ.Elem().Kind() - s, ok := map[reflect.Kind]string{ - reflect.Map: "a map", - reflect.Struct: "an object", - reflect.Slice: "an array of", - }[kind] - if !ok { - panic("dst should be one of: map, struct, slice") - } - if err := rt.ExportTo(src, dst); err != nil { - if kind == reflect.Slice { - s += fmt.Sprintf(" %ss", typ.Elem().Elem()) - } - return fmt.Errorf("%s should be %s: %w", key, s, err) - } - - return nil -} From e8e815740567e22fd5b6bc8d7b87c10720806915 Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:33:17 +0200 Subject: [PATCH 09/22] Do not expose browser opts through Goja Avoid reading browser options defined at the JS layer for BrowserType.Launch and BrowserType.Connect methods. These options will be parsed from scenario options and environment variables. --- api/browser_type.go | 4 ++-- browser/mapping.go | 6 +++--- chromium/browser_type.go | 12 ++++++------ tests/browser_test.go | 4 ++-- tests/browser_type_test.go | 2 +- tests/test_browser.go | 3 +-- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/api/browser_type.go b/api/browser_type.go index 07f047c3d..e10afcfad 100644 --- a/api/browser_type.go +++ b/api/browser_type.go @@ -6,9 +6,9 @@ import ( // BrowserType is the public interface of a CDP browser client. type BrowserType interface { - Connect(wsEndpoint string, opts goja.Value) Browser + Connect(wsEndpoint string) Browser ExecutablePath() string - Launch(opts goja.Value) (_ Browser, browserProcessID int) + Launch() (_ Browser, browserProcessID int) LaunchPersistentContext(userDataDir string, opts goja.Value) Browser Name() string } diff --git a/browser/mapping.go b/browser/mapping.go index 8d79bb441..5b3cff318 100644 --- a/browser/mapping.go +++ b/browser/mapping.go @@ -715,7 +715,7 @@ func mapBrowserType(vu moduleVU, bt api.BrowserType, wsURL string, isRemoteBrows rt := vu.Runtime() return mapping{ "connect": func(wsEndpoint string, opts goja.Value) *goja.Object { - b := bt.Connect(wsEndpoint, opts) + b := bt.Connect(wsEndpoint) m := mapBrowser(vu, b) return rt.ToValue(m).ToObject(rt) }, @@ -727,11 +727,11 @@ func mapBrowserType(vu moduleVU, bt api.BrowserType, wsURL string, isRemoteBrows // to connect and avoid storing the browser pid // as we have no access to it. if isRemoteBrowser { - m := mapBrowser(vu, bt.Connect(wsURL, opts)) + m := mapBrowser(vu, bt.Connect(wsURL)) return rt.ToValue(m).ToObject(rt) } - b, pid := bt.Launch(opts) + b, pid := bt.Launch() // store the pid so we can kill it later on panic. vu.registerPid(pid) m := mapBrowser(vu, b) diff --git a/chromium/browser_type.go b/chromium/browser_type.go index 31b4b6b83..a85e7ff1c 100644 --- a/chromium/browser_type.go +++ b/chromium/browser_type.go @@ -59,7 +59,7 @@ func NewBrowserType(vu k6modules.VU) api.BrowserType { } func (b *BrowserType) init( - opts goja.Value, isRemoteBrowser bool, + isRemoteBrowser bool, ) (context.Context, *common.BrowserOptions, *log.Logger, error) { ctx := b.initContext() @@ -75,7 +75,7 @@ func (b *BrowserType) init( browserOpts = common.NewLocalBrowserOptions() } - if err = browserOpts.Parse(ctx, logger, opts); err != nil { + if err = browserOpts.Parse(ctx, logger, nil); err != nil { return nil, nil, nil, fmt.Errorf("error parsing browser options: %w", err) } ctx = common.WithBrowserOptions(ctx, browserOpts) @@ -99,8 +99,8 @@ func (b *BrowserType) initContext() context.Context { } // Connect attaches k6 browser to an existing browser instance. -func (b *BrowserType) Connect(wsEndpoint string, opts goja.Value) api.Browser { - ctx, browserOpts, logger, err := b.init(opts, true) +func (b *BrowserType) Connect(wsEndpoint string) api.Browser { + ctx, browserOpts, logger, err := b.init(true) if err != nil { k6ext.Panic(ctx, "initializing browser type: %w", err) } @@ -154,8 +154,8 @@ func (b *BrowserType) link( // Launch allocates a new Chrome browser process and returns a new api.Browser value, // which can be used for controlling the Chrome browser. -func (b *BrowserType) Launch(opts goja.Value) (_ api.Browser, browserProcessID int) { - ctx, browserOpts, logger, err := b.init(opts, false) +func (b *BrowserType) Launch() (_ api.Browser, browserProcessID int) { + ctx, browserOpts, logger, err := b.init(false) if err != nil { k6ext.Panic(ctx, "initializing browser type: %w", err) } diff --git a/tests/browser_test.go b/tests/browser_test.go index 4636fd1b7..62551a389 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -231,13 +231,13 @@ func TestMultiConnectToSingleBrowser(t *testing.T) { tb := newTestBrowser(t, withSkipClose()) defer tb.Close() - b1 := tb.browserType.Connect(tb.wsURL, nil) + b1 := tb.browserType.Connect(tb.wsURL) bctx1, err := b1.NewContext(nil) require.NoError(t, err) p1, err := bctx1.NewPage() require.NoError(t, err, "failed to create page #1") - b2 := tb.browserType.Connect(tb.wsURL, nil) + b2 := tb.browserType.Connect(tb.wsURL) bctx2, err := b2.NewContext(nil) require.NoError(t, err) diff --git a/tests/browser_type_test.go b/tests/browser_type_test.go index 9de3ce312..1d492a0c3 100644 --- a/tests/browser_type_test.go +++ b/tests/browser_type_test.go @@ -18,7 +18,7 @@ func TestBrowserTypeConnect(t *testing.T) { bt := chromium.NewBrowserType(vu) vu.MoveToVUContext() - b := bt.Connect(tb.wsURL, nil) + b := bt.Connect(tb.wsURL) b.NewPage(nil) } diff --git a/tests/test_browser.go b/tests/test_browser.go index 8854787ca..1da8f8d54 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -105,7 +105,6 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { var ( testServer *k6httpmultibin.HTTPMultiBin state = vu.StateField - rt = vu.RuntimeField lc *logCache ) @@ -118,7 +117,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { state.Transport = testServer.HTTPTransport } - b, pid := bt.Launch(rt.ToValue(browserOpts)) + b, pid := bt.Launch() cb, ok := b.(*common.Browser) if !ok { tb.Fatalf("testBrowser: unexpected browser %T", b) From 798224164c30d9fe0d231abb4c3bb1266688717f Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:49:44 +0200 Subject: [PATCH 10/22] Read browser options from scenario options This commit changes the way browser options are currently parsed in order to read them browser options field defined at the scenario level and also from environment variables. --- chromium/browser_type.go | 3 +- common/browser_options.go | 131 +++++++++++++++--------- common/browser_options_test.go | 180 ++++++++++++++++++++++----------- 3 files changed, 206 insertions(+), 108 deletions(-) diff --git a/chromium/browser_type.go b/chromium/browser_type.go index a85e7ff1c..47c92ce4e 100644 --- a/chromium/browser_type.go +++ b/chromium/browser_type.go @@ -75,7 +75,8 @@ func (b *BrowserType) init( browserOpts = common.NewLocalBrowserOptions() } - if err = browserOpts.Parse(ctx, logger, nil); err != nil { + opts := k6ext.GetScenarioOpts(b.vu.Context(), b.vu) + if err = browserOpts.Parse(ctx, logger, opts, os.LookupEnv); err != nil { return nil, nil, nil, fmt.Errorf("error parsing browser options: %w", err) } ctx = common.WithBrowserOptions(ctx, browserOpts) diff --git a/common/browser_options.go b/common/browser_options.go index e8477b2d0..c7027159a 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -2,26 +2,36 @@ package common import ( "context" + "errors" "fmt" "reflect" + "strconv" + "strings" "time" "github.com/dop251/goja" - "go.k6.io/k6/lib/types" - "github.com/grafana/xk6-browser/k6ext" + "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/log" + + "go.k6.io/k6/lib/types" ) const ( - optArgs = "args" - optDebug = "debug" - optExecutablePath = "executablePath" - optHeadless = "headless" - optIgnoreDefaultArgs = "ignoreDefaultArgs" - optLogCategoryFilter = "logCategoryFilter" - optSlowMo = "slowMo" - optTimeout = "timeout" + // Script variables. + + optType = "type" + + // ENV variables. + + optArgs = "K6_BROWSER_ARGS" + optDebug = "K6_BROWSER_DEBUG" + optExecutablePath = "K6_BROWSER_EXECUTABLE_PATH" + optHeadless = "K6_BROWSER_HEADLESS" + optIgnoreDefaultArgs = "K6_BROWSER_IGNORE_DEFAULT_ARGS" + optLogCategoryFilter = "K6_BROWSER_LOG_CATEGORY_FILTER" + optSlowMo = "K6_BROWSER_SLOWMO" + optTimeout = "K6_BROWSER_TIMEOUT" ) // BrowserOptions stores browser options. @@ -60,50 +70,60 @@ func NewRemoteBrowserOptions() *BrowserOptions { } // Parse parses browser options from a JS object. -func (bo *BrowserOptions) Parse(ctx context.Context, logger *log.Logger, opts goja.Value) error { //nolint:cyclop - // when opts is nil, we just return the default options without error. - if !gojaValueExists(opts) { - return nil +func (bo *BrowserOptions) Parse( //nolint:cyclop + ctx context.Context, logger *log.Logger, opts map[string]any, envLookup env.LookupFunc, +) error { + // Parse opts + bt, ok := opts[optType] + // Only 'chromium' is supported by now, so return error + // if type option is not set, or if it's set and its value + // is different than 'chromium' + if !ok { + return errors.New("browser type option must be set") } - var ( - rt = k6ext.Runtime(ctx) - o = opts.ToObject(rt) - defaults = map[string]any{ - optHeadless: bo.Headless, - optLogCategoryFilter: bo.LogCategoryFilter, - optTimeout: bo.Timeout, - } - ) - for _, k := range o.Keys() { - if bo.shouldIgnoreIfBrowserIsRemote(k) { - logger.Warnf("BrowserOptions", "setting %s option is disallowed when browser is remote", k) + if bt != "chromium" { + return fmt.Errorf("unsupported browser type: %s", bt) + } + + // Parse env + envOpts := [...]string{ + optArgs, + optDebug, + optExecutablePath, + optHeadless, + optIgnoreDefaultArgs, + optLogCategoryFilter, + optSlowMo, + optTimeout, + } + + for _, e := range envOpts { + ev, ok := envLookup(e) + if !ok || ev == "" { continue } - v := o.Get(k) - if v.Export() == nil { - if dv, ok := defaults[k]; ok { - logger.Warnf("BrowserOptions", "%s was null and set to its default: %v", k, dv) - } + if bo.shouldIgnoreIfBrowserIsRemote(e) { + logger.Warnf("BrowserOptions", "setting %s option is disallowed when browser is remote", e) continue } var err error - switch k { + switch e { case optArgs: - err = exportOpt(rt, k, v, &bo.Args) + bo.Args = parseListOpt(ev) case optDebug: - bo.Debug, err = parseBoolOpt(k, v) + bo.Debug, err = parseBoolOpt(e, ev) case optExecutablePath: - bo.ExecutablePath, err = parseStrOpt(k, v) + bo.ExecutablePath = ev case optHeadless: - bo.Headless, err = parseBoolOpt(k, v) + bo.Headless, err = parseBoolOpt(e, ev) case optIgnoreDefaultArgs: - err = exportOpt(rt, k, v, &bo.IgnoreDefaultArgs) + bo.IgnoreDefaultArgs = parseListOpt(ev) case optLogCategoryFilter: - bo.LogCategoryFilter, err = parseStrOpt(k, v) + bo.LogCategoryFilter = ev case optSlowMo: - bo.SlowMo, err = parseTimeOpt(k, v) + bo.SlowMo, err = parseTimeOpt(e, ev) case optTimeout: - bo.Timeout, err = parseTimeOpt(k, v) + bo.Timeout, err = parseTimeOpt(e, ev) } if err != nil { return err @@ -129,11 +149,12 @@ func (bo *BrowserOptions) shouldIgnoreIfBrowserIsRemote(opt string) bool { return ignore } -func parseBoolOpt(key string, val goja.Value) (b bool, err error) { - if val.ExportType().Kind() != reflect.Bool { - return false, fmt.Errorf("%s should be a boolean", key) +func parseBoolOpt(k, v string) (bool, error) { + b, err := strconv.ParseBool(v) + if err != nil { + return false, fmt.Errorf("%s should be a boolean", k) } - b, _ = val.Export().(bool) + return b, nil } @@ -144,11 +165,25 @@ func parseStrOpt(key string, val goja.Value) (s string, err error) { return val.String(), nil } -func parseTimeOpt(key string, val goja.Value) (t time.Duration, err error) { - if t, err = types.GetDurationValue(val.String()); err != nil { - return time.Duration(0), fmt.Errorf("%s should be a time duration value: %w", key, err) +func parseTimeOpt(k, v string) (time.Duration, error) { + t, err := types.GetDurationValue(v) + if err != nil { + return time.Duration(0), fmt.Errorf("%s should be a time duration value: %w", k, err) } - return + + return t, nil +} + +func parseListOpt(v string) []string { + elems := strings.Split(v, ",") + // If last element is a void string, + // because value contained an ending comma, + // remove it + if elems[len(elems)-1] == "" { + elems = elems[:len(elems)-1] + } + + return elems } // exportOpt exports src to dst and dynamically returns an error diff --git a/common/browser_options_test.go b/common/browser_options_test.go index 5e9fa9e9f..727cdcc7f 100644 --- a/common/browser_options_test.go +++ b/common/browser_options_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/k6ext/k6test" "github.com/grafana/xk6-browser/log" ) @@ -20,39 +21,63 @@ func TestBrowserOptionsParse(t *testing.T) { Timeout: DefaultTimeout, } + noopEnvLookuper := func(string) (string, bool) { + return "", false + } + for name, tt := range map[string]struct { opts map[string]any + envLookupper env.LookupFunc assert func(testing.TB, *BrowserOptions) err string isRemoteBrowser bool }{ "defaults": { - opts: map[string]any{}, + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: noopEnvLookuper, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, defaultOptions, lo) }, }, "defaults_nil": { // providing nil option returns default options - opts: nil, + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: noopEnvLookuper, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, defaultOptions, lo) }, }, "defaults_remote_browser": { - isRemoteBrowser: true, opts: map[string]any{ + "type": "chromium", + }, + isRemoteBrowser: true, + envLookupper: func(k string) (string, bool) { + switch k { // disallow changing the following opts - "args": []string{"any"}, - "executablePath": "something else", - "headless": false, - "ignoreDefaultArgs": []string{"any"}, + case optArgs: + return "any", true + case optExecutablePath: + return "something else", true + case optHeadless: + return "false", true + case optIgnoreDefaultArgs: + return "any", true // allow changing the following opts - "debug": true, - "logCategoryFilter": "...", - "slowMo": time.Second, - "timeout": time.Second, + case optDebug: + return "true", true + case optLogCategoryFilter: + return "...", true + case optTimeout: + return "1s", true + default: + return "", false + } }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -62,7 +87,6 @@ func TestBrowserOptionsParse(t *testing.T) { // allowed: Debug: true, LogCategoryFilter: "...", - SlowMo: time.Second, Timeout: time.Second, isRemoteBrowser: true, @@ -71,9 +95,10 @@ func TestBrowserOptionsParse(t *testing.T) { }, "nulls": { // don't override the defaults on `null` opts: map[string]any{ - "headless": nil, - "logCategoryFilter": nil, - "timeout": nil, + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + return "", true }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -86,7 +111,13 @@ func TestBrowserOptionsParse(t *testing.T) { }, "args": { opts: map[string]any{ - "args": []any{"browser-arg1='value1", "browser-arg2=value2", "browser-flag"}, + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optArgs { + return "browser-arg1='value1,browser-arg2=value2,browser-flag", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -96,39 +127,57 @@ func TestBrowserOptionsParse(t *testing.T) { assert.Equal(tb, "browser-flag", lo.Args[2]) }, }, - "args_err": { + "debug": { opts: map[string]any{ - "args": 1, + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optDebug { + return "true", true + } + return "", false }, - err: "args should be an array of strings", - }, - "debug": { - opts: map[string]any{"debug": true}, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.True(t, lo.Debug) }, }, "debug_err": { - opts: map[string]any{"debug": "true"}, - err: "debug should be a boolean", + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optDebug { + return "non-boolean", true + } + return "", false + }, + err: "K6_BROWSER_DEBUG should be a boolean", }, "executablePath": { opts: map[string]any{ - "executablePath": "cmd/somewhere", + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optExecutablePath { + return "cmd/somewhere", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() assert.Equal(t, "cmd/somewhere", lo.ExecutablePath) }, }, - "executablePath_err": { - opts: map[string]any{"executablePath": 1}, - err: "executablePath should be a string", - }, "headless": { opts: map[string]any{ - "headless": false, + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optHeadless { + return "false", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -136,12 +185,26 @@ func TestBrowserOptionsParse(t *testing.T) { }, }, "headless_err": { - opts: map[string]any{"headless": "true"}, - err: "headless should be a boolean", + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optHeadless { + return "non-boolean", true + } + return "", false + }, + err: "K6_BROWSER_HEADLESS should be a boolean", }, "ignoreDefaultArgs": { opts: map[string]any{ - "ignoreDefaultArgs": []string{"--hide-scrollbars", "--hide-something"}, + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optIgnoreDefaultArgs { + return "--hide-scrollbars,--hide-something", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -150,39 +213,30 @@ func TestBrowserOptionsParse(t *testing.T) { assert.Equal(t, "--hide-something", lo.IgnoreDefaultArgs[1]) }, }, - "ignoreDefaultArgs_err": { - opts: map[string]any{"ignoreDefaultArgs": "ABC"}, - err: "ignoreDefaultArgs should be an array of strings", - }, "logCategoryFilter": { opts: map[string]any{ - "logCategoryFilter": "**", + "type": "chromium", }, - assert: func(tb testing.TB, lo *BrowserOptions) { - tb.Helper() - assert.Equal(t, "**", lo.LogCategoryFilter) - }, - }, - "logCategoryFilter_err": { - opts: map[string]any{"logCategoryFilter": 1}, - err: "logCategoryFilter should be a string", - }, - "slowMo": { - opts: map[string]any{ - "slowMo": "5s", + envLookupper: func(k string) (string, bool) { + if k == optLogCategoryFilter { + return "**", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() - assert.Equal(t, 5*time.Second, lo.SlowMo) + assert.Equal(t, "**", lo.LogCategoryFilter) }, }, - "slowMo_err": { - opts: map[string]any{"slowMo": "ABC"}, - err: "slowMo should be a time duration value", - }, "timeout": { opts: map[string]any{ - "timeout": "10s", + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optTimeout { + return "10s", true + } + return "", false }, assert: func(tb testing.TB, lo *BrowserOptions) { tb.Helper() @@ -190,8 +244,16 @@ func TestBrowserOptionsParse(t *testing.T) { }, }, "timeout_err": { - opts: map[string]any{"timeout": "ABC"}, - err: "timeout should be a time duration value", + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: func(k string) (string, bool) { + if k == optTimeout { + return "ABC", true + } + return "", false + }, + err: "K6_BROWSER_TIMEOUT should be a time duration value", }, } { tt := tt @@ -208,7 +270,7 @@ func TestBrowserOptionsParse(t *testing.T) { lo = NewLocalBrowserOptions() } - err := lo.Parse(vu.Context(), log.NewNullLogger(), vu.ToGojaValue(tt.opts)) + err := lo.Parse(vu.Context(), log.NewNullLogger(), tt.opts, tt.envLookupper) if tt.err != "" { require.ErrorContains(t, err, tt.err) } else { From 9a1ddbf45eba77fed68bb9cc318fe3815f85a067 Mon Sep 17 00:00:00 2001 From: ka3de Date: Tue, 9 May 2023 08:41:06 +0200 Subject: [PATCH 11/22] Add unit test for browser type option The unit test should verify that any browser type set with a value different than 'chromium', currently the only supported browser type, returns an error. No browser type set also returns an error as this field is now mandatory. --- common/browser_options_test.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/common/browser_options_test.go b/common/browser_options_test.go index 727cdcc7f..951983916 100644 --- a/common/browser_options_test.go +++ b/common/browser_options_test.go @@ -12,7 +12,7 @@ import ( "github.com/grafana/xk6-browser/log" ) -func TestBrowserOptionsParse(t *testing.T) { +func TestBrowserOptionsParse(t *testing.T) { //nolint:gocognit t.Parallel() defaultOptions := &BrowserOptions{ @@ -255,6 +255,27 @@ func TestBrowserOptionsParse(t *testing.T) { }, err: "K6_BROWSER_TIMEOUT should be a time duration value", }, + "browser_type": { + opts: map[string]any{ + "type": "chromium", + }, + envLookupper: noopEnvLookuper, + assert: func(tb testing.TB, lo *BrowserOptions) { + tb.Helper() + // Noop, just expect no error + }, + }, + "browser_type_err": { + opts: map[string]any{ + "type": "mybrowsertype", + }, + envLookupper: noopEnvLookuper, + err: "unsupported browser type: mybrowsertype", + }, + "browser_type_unset_err": { + envLookupper: noopEnvLookuper, + err: "browser type option must be set", + }, } { tt := tt t.Run(name, func(t *testing.T) { From ab5d5a7772583454fa5979af4e3ee04724619ec9 Mon Sep 17 00:00:00 2001 From: ka3de Date: Sat, 6 May 2023 16:52:28 +0200 Subject: [PATCH 12/22] Remove unused options parsing functions --- common/browser_options.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/common/browser_options.go b/common/browser_options.go index c7027159a..9261456c5 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -4,13 +4,10 @@ import ( "context" "errors" "fmt" - "reflect" "strconv" "strings" "time" - "github.com/dop251/goja" - "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/log" @@ -158,13 +155,6 @@ func parseBoolOpt(k, v string) (bool, error) { return b, nil } -func parseStrOpt(key string, val goja.Value) (s string, err error) { - if val.ExportType().Kind() != reflect.String { - return "", fmt.Errorf("%s should be a string", key) - } - return val.String(), nil -} - func parseTimeOpt(k, v string) (time.Duration, error) { t, err := types.GetDurationValue(v) if err != nil { @@ -185,30 +175,3 @@ func parseListOpt(v string) []string { return elems } - -// exportOpt exports src to dst and dynamically returns an error -// depending on the type if an error occurs. Panics if dst is not -// a pointer and not points to a map, struct, or slice. -func exportOpt[T any](rt *goja.Runtime, key string, src goja.Value, dst T) error { - typ := reflect.TypeOf(dst) - if typ.Kind() != reflect.Pointer { - panic("dst should be a pointer") - } - kind := typ.Elem().Kind() - s, ok := map[reflect.Kind]string{ - reflect.Map: "a map", - reflect.Struct: "an object", - reflect.Slice: "an array of", - }[kind] - if !ok { - panic("dst should be one of: map, struct, slice") - } - if err := rt.ExportTo(src, dst); err != nil { - if kind == reflect.Slice { - s += fmt.Sprintf(" %ss", typ.Elem().Elem()) - } - return fmt.Errorf("%s should be %s: %w", key, s, err) - } - - return nil -} From c0360c97af906062cd34aa91ddc0dfe3ccb75778 Mon Sep 17 00:00:00 2001 From: ka3de Date: Mon, 8 May 2023 16:45:15 +0200 Subject: [PATCH 13/22] Add envLookupper to BrowserType This allows to define a default env.Lookupper function at the BrowserType level, which will be the one passed into browser options parsing function, and that can be overwritten from tests (e.g.: test browser) in order to be able to run tests that depend on environment browser options in parallel. --- chromium/browser_type.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/chromium/browser_type.go b/chromium/browser_type.go index 47c92ce4e..85acb9f06 100644 --- a/chromium/browser_type.go +++ b/chromium/browser_type.go @@ -15,6 +15,7 @@ import ( "github.com/grafana/xk6-browser/api" "github.com/grafana/xk6-browser/common" + "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/k6ext" "github.com/grafana/xk6-browser/log" "github.com/grafana/xk6-browser/storage" @@ -34,12 +35,13 @@ var _ api.BrowserType = &BrowserType{} type BrowserType struct { // FIXME: This is only exported because testBrowser needs it. Contexts // shouldn't be stored on structs if we can avoid it. - Ctx context.Context - vu k6modules.VU - hooks *common.Hooks - k6Metrics *k6ext.CustomMetrics - execPath string // path to the Chromium executable - randSrc *rand.Rand + Ctx context.Context + vu k6modules.VU + hooks *common.Hooks + k6Metrics *k6ext.CustomMetrics + execPath string // path to the Chromium executable + randSrc *rand.Rand + envLookupper env.LookupFunc } // NewBrowserType registers our custom k6 metrics, creates method mappings on @@ -49,10 +51,11 @@ func NewBrowserType(vu k6modules.VU) api.BrowserType { // otherwise it will return nil. k6m := k6ext.RegisterCustomMetrics(vu.InitEnv().Registry) b := BrowserType{ - vu: vu, - hooks: common.NewHooks(), - k6Metrics: k6m, - randSrc: rand.New(rand.NewSource(time.Now().UnixNano())), //nolint: gosec + vu: vu, + hooks: common.NewHooks(), + k6Metrics: k6m, + randSrc: rand.New(rand.NewSource(time.Now().UnixNano())), //nolint: gosec + envLookupper: os.LookupEnv, } return &b @@ -76,7 +79,7 @@ func (b *BrowserType) init( } opts := k6ext.GetScenarioOpts(b.vu.Context(), b.vu) - if err = browserOpts.Parse(ctx, logger, opts, os.LookupEnv); err != nil { + if err = browserOpts.Parse(ctx, logger, opts, b.envLookupper); err != nil { return nil, nil, nil, fmt.Errorf("error parsing browser options: %w", err) } ctx = common.WithBrowserOptions(ctx, browserOpts) @@ -296,6 +299,11 @@ func (b *BrowserType) ExecutablePath() (execPath string) { return "" } +// SetEnvLookupper sets the environment variable lookupper function. +func (b *BrowserType) SetEnvLookupper(envLookupper env.LookupFunc) { + b.envLookupper = envLookupper +} + // parseArgs parses command-line arguments and returns them. func parseArgs(flags map[string]any) ([]string, error) { // Build command line args list From f1c93090bafce0ab1265ab56e8ad57067a71c4a1 Mon Sep 17 00:00:00 2001 From: ka3de Date: Mon, 8 May 2023 16:59:54 +0200 Subject: [PATCH 14/22] Support browser options in testbrowser --- tests/test_browser.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_browser.go b/tests/test_browser.go index 1da8f8d54..17249ee90 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -6,12 +6,14 @@ import ( "net/http" "os" "strconv" + "strings" "testing" "time" "github.com/grafana/xk6-browser/api" "github.com/grafana/xk6-browser/chromium" "github.com/grafana/xk6-browser/common" + "github.com/grafana/xk6-browser/env" "github.com/grafana/xk6-browser/k6ext" "github.com/grafana/xk6-browser/k6ext/k6test" @@ -117,6 +119,8 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { state.Transport = testServer.HTTPTransport } + bt.SetEnvLookupper(setupEnvLookupper(tb, browserOpts)) + b, pid := bt.Launch() cb, ok := b.(*common.Browser) if !ok { @@ -494,3 +498,30 @@ func setupHTTPTestModuleInstance(tb testing.TB, samples chan k6metrics.SampleCon return vu } + +func setupEnvLookupper(tb testing.TB, opts browserOptions) env.LookupFunc { + tb.Helper() + + return func(key string) (string, bool) { + switch key { + case "K6_BROWSER_ARGS": + if len(opts.Args) != 0 { + return strings.Join(opts.Args, ","), true + } + case "K6_BROWSER_DEBUG": + return strconv.FormatBool(opts.Debug), true + case "K6_BROWSER_HEADLESS": + return strconv.FormatBool(opts.Headless), true + case "K6_BROWSER_SLOWMO": + if opts.SlowMo != "" { + return opts.SlowMo, true + } + case "K6_BROWSER_TIMEOUT": + if opts.Timeout != "" { + return opts.Timeout, true + } + } + + return "", false + } +} From b7619d72210b06fca159f273bf1d3bb336d3a210 Mon Sep 17 00:00:00 2001 From: ka3de Date: Wed, 10 May 2023 09:11:45 +0200 Subject: [PATCH 15/22] Add test scenario config to mock VU Because browser type option is now a mandatory field to be able to run k6 browser tests, we have to modify our mock VU implementation in order to add this field. This is done through the definition of a default scenario and a mock executor implementation for testing purposes. --- k6ext/k6test/executor.go | 34 ++++++++++++++++++++++++++++++++++ k6ext/k6test/vu.go | 21 +++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 k6ext/k6test/executor.go diff --git a/k6ext/k6test/executor.go b/k6ext/k6test/executor.go new file mode 100644 index 000000000..b1e332e4f --- /dev/null +++ b/k6ext/k6test/executor.go @@ -0,0 +1,34 @@ +package k6test + +import ( + "github.com/sirupsen/logrus" + + k6lib "go.k6.io/k6/lib" + k6executor "go.k6.io/k6/lib/executor" +) + +// TestExecutor is a k6lib.ExecutorConfig implementation +// for testing purposes. +type TestExecutor struct { + k6executor.BaseConfig +} + +// GetDescription returns a mock Executor description. +func (te *TestExecutor) GetDescription(*k6lib.ExecutionTuple) string { + return "TestExecutor" +} + +// GetExecutionRequirements is a dummy implementation that just returns nil. +func (te *TestExecutor) GetExecutionRequirements(*k6lib.ExecutionTuple) []k6lib.ExecutionStep { + return nil +} + +// NewExecutor is a dummy implementation that just returns nil. +func (te *TestExecutor) NewExecutor(*k6lib.ExecutionState, *logrus.Entry) (k6lib.Executor, error) { + return nil, nil +} + +// HasWork is a dummy implementation that returns true. +func (te *TestExecutor) HasWork(*k6lib.ExecutionTuple) bool { + return true +} diff --git a/k6ext/k6test/vu.go b/k6ext/k6test/vu.go index abb340c18..58b0248be 100644 --- a/k6ext/k6test/vu.go +++ b/k6ext/k6test/vu.go @@ -3,17 +3,18 @@ package k6test import ( "testing" + "github.com/dop251/goja" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v3" + "github.com/grafana/xk6-browser/k6ext" k6eventloop "go.k6.io/k6/js/eventloop" k6modulestest "go.k6.io/k6/js/modulestest" k6lib "go.k6.io/k6/lib" + k6executor "go.k6.io/k6/lib/executor" k6testutils "go.k6.io/k6/lib/testutils" k6metrics "go.k6.io/k6/metrics" - - "github.com/dop251/goja" - "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) // VU is a k6 VU instance. @@ -81,6 +82,17 @@ func NewVU(tb testing.TB, opts ...any) *VU { Batch: null.IntFrom(20), BatchPerHost: null.IntFrom(20), // HTTPDebug: null.StringFrom("full"), + Scenarios: k6lib.ScenarioConfigs{ + "default": &TestExecutor{ + BaseConfig: k6executor.BaseConfig{ + Options: &k6lib.ScenarioOptions{ + Browser: map[string]any{ + "type": "chromium", + }, + }, + }, + }, + }, }, Logger: k6testutils.NewLogger(tb), Group: root, @@ -91,6 +103,7 @@ func NewVU(tb testing.TB, opts ...any) *VU { } ctx := k6ext.WithVU(testRT.VU.CtxField, testRT.VU) + ctx = k6lib.WithScenarioState(ctx, &k6lib.ScenarioState{Name: "default"}) testRT.VU.CtxField = ctx return &VU{VU: testRT.VU, Loop: testRT.EventLoop, toBeState: state, samples: samples} From dae4788edabd78e0f55ebff277f83f33a8fdf50d Mon Sep 17 00:00:00 2001 From: ka3de Date: Wed, 10 May 2023 10:15:14 +0200 Subject: [PATCH 16/22] Fix testbrowser custom ctx does not wrap VU state Test browser had support for withContext option, which allowed to pass a custom context to test browser, which would be the one set as VU context. Usually this context was used in order to control from the test the cancellation of the test browser. The problem is that this context passed from the test implementation, was "divergent" from the test runtime context set up when calling setupHTTPTestModuleInstance() from test browser, which sets up all the VU related mock data, including the mandatory browser type field inside scenario options. Because this option is not mandatory, tests that were using a custom context were failing due to scenario options not being present in this context. This has been fixed by not allowing custom contexts to be set for test browser, and instead expose this component's context and context cancelling function so they can be handled from tests implementation. This commit also fixes the tests affected by this issue. --- tests/browser_test.go | 10 ++++------ tests/page_test.go | 9 ++++----- tests/test_browser.go | 32 ++++++++++++++++---------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/tests/browser_test.go b/tests/browser_test.go index 62551a389..8459f49d4 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -1,7 +1,6 @@ package tests import ( - "context" "os" "path/filepath" "regexp" @@ -104,16 +103,15 @@ func TestBrowserOn(t *testing.T) { t.Parallel() var ( - ctx, cancel = context.WithCancel(context.Background()) - b = newTestBrowser(t, ctx) - rt = b.vu.Runtime() - log []string + b = newTestBrowser(t) + rt = b.vu.Runtime() + log []string ) require.NoError(t, rt.Set("b", b.Browser)) require.NoError(t, rt.Set("log", func(s string) { log = append(log, s) })) - time.AfterFunc(100*time.Millisecond, cancel) + time.AfterFunc(100*time.Millisecond, b.Cancel) _, err := b.runJavaScript(script, "disconnected") assert.ErrorContains(t, err, "browser.on promise rejected: context canceled") }) diff --git a/tests/page_test.go b/tests/page_test.go index f097e0e0a..75508cfca 100644 --- a/tests/page_test.go +++ b/tests/page_test.go @@ -2,7 +2,6 @@ package tests import ( "bytes" - "context" _ "embed" "encoding/json" "fmt" @@ -645,10 +644,10 @@ func TestPageWaitForLoadState(t *testing.T) { // See: The issue #187 for details. func TestPageWaitForNavigationErrOnCtxDone(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - p := newTestBrowser(t, ctx).NewPage(nil) - go cancel() - <-ctx.Done() + b := newTestBrowser(t) + p := b.NewPage(nil) + go b.Cancel() + <-b.Context().Done() _, err := p.WaitForNavigation(nil) require.ErrorContains(t, err, "canceled") } diff --git a/tests/test_browser.go b/tests/test_browser.go index 17249ee90..93b3c3ec3 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -41,6 +41,8 @@ type testBrowser struct { browserType api.BrowserType api.Browser + + cancel context.CancelFunc } // newTestBrowser configures and launches a new chrome browser. @@ -53,7 +55,6 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { // set default options and then customize them var ( - ctx context.Context browserOpts = defaultBrowserOpts() enableHTTPMultiBin = false enableFileServer = false @@ -72,8 +73,6 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { enableHTTPMultiBin = true case logCacheOption: enableLogCache = true - case withContext: - ctx = opt case skipCloseOption: skipClose = true case withSamplesListener: @@ -83,15 +82,9 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { vu := setupHTTPTestModuleInstance(tb, samples) - if ctx == nil { - dummyCtx, cancel := context.WithCancel(vu.Context()) - tb.Cleanup(cancel) - vu.CtxField = dummyCtx - } else { - // Attach the mock VU to the passed context - ctx = k6ext.WithVU(ctx, vu) - vu.CtxField = ctx - } + dummyCtx, cancel := context.WithCancel(vu.Context()) + tb.Cleanup(cancel) + vu.CtxField = dummyCtx registry := k6metrics.NewRegistry() k6m := k6ext.RegisterCustomMetrics(registry) @@ -147,6 +140,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { browserType: bt, pid: pid, wsURL: cb.WsURL(), + cancel: cancel, } if enableFileServer { tbr = tbr.withFileServer() @@ -215,6 +209,16 @@ func (b *testBrowser) staticURL(path string) string { return b.URL("/" + testBrowserStaticDir + "/" + path) } +// Context returns the testBrowser context. +func (b *testBrowser) Context() context.Context { + return b.ctx +} + +// Cancel cancels the testBrowser context. +func (b *testBrowser) Cancel() { + b.cancel() +} + // attachFrame attaches the frame to the page and returns it. func (b *testBrowser) attachFrame(page api.Page, frameID string, url string) api.Frame { b.t.Helper() @@ -450,10 +454,6 @@ func withFileServer() fileServerOption { return struct{}{} } -// withContext is used to detect whether to use a custom context in the test -// browser. -type withContext = context.Context - // logCacheOption is used to detect whether to enable the log cache. type logCacheOption struct{} From b8123699be2d066cefdd01a7b040da728a459b8d Mon Sep 17 00:00:00 2001 From: ka3de Date: Tue, 9 May 2023 08:32:26 +0200 Subject: [PATCH 17/22] Do not expose browser slowMo option Based on https://github.com/grafana/xk6-browser/issues/857, a decision was made to not expose slowMo browser option by now, and try to move it to the browser context level instead, which will provide more flexibility. --- common/browser_options.go | 10 ++++------ tests/test_browser.go | 6 ------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/common/browser_options.go b/common/browser_options.go index 9261456c5..3f45fda1d 100644 --- a/common/browser_options.go +++ b/common/browser_options.go @@ -27,7 +27,6 @@ const ( optHeadless = "K6_BROWSER_HEADLESS" optIgnoreDefaultArgs = "K6_BROWSER_IGNORE_DEFAULT_ARGS" optLogCategoryFilter = "K6_BROWSER_LOG_CATEGORY_FILTER" - optSlowMo = "K6_BROWSER_SLOWMO" optTimeout = "K6_BROWSER_TIMEOUT" ) @@ -39,8 +38,10 @@ type BrowserOptions struct { Headless bool IgnoreDefaultArgs []string LogCategoryFilter string - SlowMo time.Duration - Timeout time.Duration + // TODO: Do not expose slowMo option by now. + // See https://github.com/grafana/xk6-browser/issues/857. + SlowMo time.Duration + Timeout time.Duration isRemoteBrowser bool // some options will be ignored if browser is in a remote machine } @@ -90,7 +91,6 @@ func (bo *BrowserOptions) Parse( //nolint:cyclop optHeadless, optIgnoreDefaultArgs, optLogCategoryFilter, - optSlowMo, optTimeout, } @@ -117,8 +117,6 @@ func (bo *BrowserOptions) Parse( //nolint:cyclop bo.IgnoreDefaultArgs = parseListOpt(ev) case optLogCategoryFilter: bo.LogCategoryFilter = ev - case optSlowMo: - bo.SlowMo, err = parseTimeOpt(e, ev) case optTimeout: bo.Timeout, err = parseTimeOpt(e, ev) } diff --git a/tests/test_browser.go b/tests/test_browser.go index 93b3c3ec3..67127f7bd 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -395,7 +395,6 @@ type browserOptions struct { Args []string `js:"args"` Debug bool `js:"debug"` Headless bool `js:"headless"` - SlowMo string `js:"slowMo"` Timeout string `js:"timeout"` } @@ -420,7 +419,6 @@ func defaultBrowserOpts() browserOptions { return browserOptions{ Headless: headless, - SlowMo: "0s", Timeout: "30s", } } @@ -512,10 +510,6 @@ func setupEnvLookupper(tb testing.TB, opts browserOptions) env.LookupFunc { return strconv.FormatBool(opts.Debug), true case "K6_BROWSER_HEADLESS": return strconv.FormatBool(opts.Headless), true - case "K6_BROWSER_SLOWMO": - if opts.SlowMo != "" { - return opts.SlowMo, true - } case "K6_BROWSER_TIMEOUT": if opts.Timeout != "" { return opts.Timeout, true From 185bd1819bac23aacd992320748030c421ee0bda Mon Sep 17 00:00:00 2001 From: ka3de Date: Tue, 9 May 2023 14:27:36 +0200 Subject: [PATCH 18/22] Update examples Modify JS examples removing browser options that now should be set as ENV variables and setting browser type option inside scenario options, which is now a mandatory field for any browser test. Also browser_args.js example is removed as it does not provide any value beyond showing how to set a browser arg in the previous implementation; and evaluate.js is modified in order to showcase 'evaluate' method functionality without requiring the usage of 'ignoreBrowserArgs' option. --- examples/browser_args.js | 27 ------------------------- examples/browser_on.js | 14 ++++++++++--- examples/colorscheme.js | 14 ++++++++++--- examples/device_emulation.js | 14 ++++++++++--- examples/elementstate.js | 14 ++++++++++--- examples/evaluate.js | 37 ++++++++++++++++++----------------- examples/fillform.js | 14 ++++++++++--- examples/getattribute.js | 14 ++++++++++--- examples/grant_permission.js | 14 ++++++++++--- examples/hosts.js | 14 ++++++++++--- examples/keyboard.js | 17 +++++++++++++--- examples/launch_naked.js | 13 +++++++++++- examples/locator.js | 14 ++++++++++--- examples/locator_pom.js | 14 ++++++++++--- examples/mouse.js | 17 +++++++++++++--- examples/multiple-scenario.js | 18 +++++++++++------ examples/querying.js | 14 ++++++++++--- examples/screenshot.js | 14 ++++++++++--- examples/touchscreen.js | 17 +++++++++++++--- examples/waitforfunction.js | 14 ++++++++++--- 20 files changed, 228 insertions(+), 100 deletions(-) delete mode 100644 examples/browser_args.js diff --git a/examples/browser_args.js b/examples/browser_args.js deleted file mode 100644 index 1442308d7..000000000 --- a/examples/browser_args.js +++ /dev/null @@ -1,27 +0,0 @@ -import { check } from 'k6'; -import { chromium } from 'k6/x/browser'; - -export const options = { - thresholds: { - checks: ["rate==1.0"] - } -} - -export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - args: ['host-resolver-rules=MAP test.k6.io 127.0.0.1'], - }); - const context = browser.newContext(); - const page = context.newPage(); - - try { - const res = await page.goto('http://test.k6.io/', { waitUntil: 'load' }); - check(res, { - 'null response': r => r === null, - }); - } finally { - page.close(); - browser.close(); - } -} diff --git a/examples/browser_on.js b/examples/browser_on.js index b77522a46..bd37bd5db 100644 --- a/examples/browser_on.js +++ b/examples/browser_on.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); check(browser, { 'should be connected after launch': browser.isConnected(), diff --git a/examples/colorscheme.js b/examples/colorscheme.js index deb659f82..2990c261a 100644 --- a/examples/colorscheme.js +++ b/examples/colorscheme.js @@ -2,6 +2,16 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } @@ -10,9 +20,7 @@ export const options = { export default async function() { const preferredColorScheme = 'dark'; - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext({ // valid values are "light", "dark" or "no-preference" diff --git a/examples/device_emulation.js b/examples/device_emulation.js index 67656a62c..6a0c616f6 100644 --- a/examples/device_emulation.js +++ b/examples/device_emulation.js @@ -2,15 +2,23 @@ import { check, sleep } from 'k6'; import { chromium, devices } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const device = devices['iPhone X']; // The spread operator is currently unsupported by k6's Babel, so use diff --git a/examples/elementstate.js b/examples/elementstate.js index 458101bdf..e77fad4a7 100644 --- a/examples/elementstate.js +++ b/examples/elementstate.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/evaluate.js b/examples/evaluate.js index 31508b6c0..76a771904 100644 --- a/examples/evaluate.js +++ b/examples/evaluate.js @@ -2,35 +2,36 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - ignoreDefaultArgs: ['--hide-scrollbars'] - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); try { await page.goto('https://test.k6.io/', { waitUntil: 'load' }); - const dimensions = page.evaluate(() => { - const obj = { - width: document.documentElement.clientWidth, - height: document.documentElement.clientHeight, - deviceScaleFactor: window.devicePixelRatio - }; - console.log(obj); // tests #120 - return obj; - }); - - check(dimensions, { - 'width': d => d.width === 1265, - 'height': d => d.height === 720, - 'scale': d => d.deviceScaleFactor === 1, + + const result = page.evaluate(([x, y]) => { + return Promise.resolve(x * y); + }, [5, 5]); + console.log(result); // tests #120 + + check(result, { + 'result is 25': (result) => result == 25, }); } finally { page.close(); diff --git a/examples/fillform.js b/examples/fillform.js index fa79c5b4e..e5b8d8005 100644 --- a/examples/fillform.js +++ b/examples/fillform.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/getattribute.js b/examples/getattribute.js index 2a5c392e6..004e21a1c 100644 --- a/examples/getattribute.js +++ b/examples/getattribute.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/grant_permission.js b/examples/grant_permission.js index a3dcd1786..889dcdab5 100644 --- a/examples/grant_permission.js +++ b/examples/grant_permission.js @@ -1,15 +1,23 @@ import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); // grant camera and microphone permissions to the // new browser context. diff --git a/examples/hosts.js b/examples/hosts.js index 22f9b8bf5..f33e8fa00 100644 --- a/examples/hosts.js +++ b/examples/hosts.js @@ -2,6 +2,16 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, hosts: { 'test.k6.io': '127.0.0.254' }, thresholds: { checks: ["rate==1.0"] @@ -9,9 +19,7 @@ export const options = { }; export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/keyboard.js b/examples/keyboard.js index 4b4b5eee1..a3def27e3 100644 --- a/examples/keyboard.js +++ b/examples/keyboard.js @@ -1,9 +1,20 @@ import { chromium } from 'k6/x/browser'; +export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + } +} + export default async function () { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const page = browser.newPage(); await page.goto('https://test.k6.io/my_messages.php', { waitUntil: 'networkidle' }); diff --git a/examples/launch_naked.js b/examples/launch_naked.js index 101e74e50..b169bac6d 100644 --- a/examples/launch_naked.js +++ b/examples/launch_naked.js @@ -1,7 +1,18 @@ import exec from 'k6/execution'; import { chromium } from 'k6/x/browser'; -export const options = {} +export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + } +} export default function() { try { diff --git a/examples/locator.js b/examples/locator.js index 99d90acea..ed3a248cb 100644 --- a/examples/locator.js +++ b/examples/locator.js @@ -1,15 +1,23 @@ import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/locator_pom.js b/examples/locator_pom.js index f0447dc3f..636e83be6 100644 --- a/examples/locator_pom.js +++ b/examples/locator_pom.js @@ -1,6 +1,16 @@ import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } @@ -47,9 +57,7 @@ export class Bet { } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/mouse.js b/examples/mouse.js index 57f002f32..579f9fd95 100644 --- a/examples/mouse.js +++ b/examples/mouse.js @@ -1,9 +1,20 @@ import { chromium } from 'k6/x/browser'; +export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + } +} + export default async function () { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const page = browser.newPage(); await page.goto('https://test.k6.io/', { waitUntil: 'networkidle' }); diff --git a/examples/multiple-scenario.js b/examples/multiple-scenario.js index 69d6dfbaf..d118e72b6 100644 --- a/examples/multiple-scenario.js +++ b/examples/multiple-scenario.js @@ -7,6 +7,11 @@ export const options = { exec: 'messages', vus: 2, duration: '2s', + options: { + browser: { + type: 'chromium', + }, + }, }, news: { executor: 'per-vu-iterations', @@ -14,6 +19,11 @@ export const options = { vus: 2, iterations: 4, maxDuration: '5s', + options: { + browser: { + type: 'chromium', + }, + }, }, }, thresholds: { @@ -23,9 +33,7 @@ export const options = { } export async function messages() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const page = browser.newPage(); try { @@ -37,9 +45,7 @@ export async function messages() { } export async function news() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const page = browser.newPage(); try { diff --git a/examples/querying.js b/examples/querying.js index 26aac9cca..061d9eee4 100644 --- a/examples/querying.js +++ b/examples/querying.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/screenshot.js b/examples/screenshot.js index 360315864..8dfc07091 100644 --- a/examples/screenshot.js +++ b/examples/screenshot.js @@ -1,15 +1,23 @@ import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); diff --git a/examples/touchscreen.js b/examples/touchscreen.js index 414067df3..64bebf15a 100644 --- a/examples/touchscreen.js +++ b/examples/touchscreen.js @@ -1,9 +1,20 @@ import { chromium } from 'k6/x/browser'; +export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + } +} + export default async function () { - const browser = chromium.launch({ - headless: __ENV.XK6_HEADLESS ? true : false, - }); + const browser = chromium.launch(); const page = browser.newPage(); await page.goto('https://test.k6.io/', { waitUntil: 'networkidle' }); diff --git a/examples/waitforfunction.js b/examples/waitforfunction.js index 299e6fe37..993fd1644 100644 --- a/examples/waitforfunction.js +++ b/examples/waitforfunction.js @@ -2,15 +2,23 @@ import { check } from 'k6'; import { chromium } from 'k6/x/browser'; export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium', + }, + }, + }, + }, thresholds: { checks: ["rate==1.0"] } } export default async function() { - const browser = chromium.launch({ - headless: true, - }); + const browser = chromium.launch(); const context = browser.newContext(); const page = context.newPage(); From 64407fc9b9e07a8f17950c93f73d78bf32bc9c2a Mon Sep 17 00:00:00 2001 From: ka3de Date: Tue, 9 May 2023 09:48:26 +0200 Subject: [PATCH 19/22] Add tests package godoc --- tests/doc.go | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/doc.go diff --git a/tests/doc.go b/tests/doc.go new file mode 100644 index 000000000..75d6372de --- /dev/null +++ b/tests/doc.go @@ -0,0 +1,2 @@ +// Package tests contains integration tests. +package tests From e827f38018c36f1dc81c950fcd16b8e65461f2e5 Mon Sep 17 00:00:00 2001 From: ka3de Date: Tue, 9 May 2023 14:25:31 +0200 Subject: [PATCH 20/22] Add common package godoc --- common/doc.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 common/doc.go diff --git a/common/doc.go b/common/doc.go new file mode 100644 index 000000000..ed51123e2 --- /dev/null +++ b/common/doc.go @@ -0,0 +1,3 @@ +// Package common contains the implementation of API elements that do not +// depend on the browser type. +package common From 25346e202a7b0d60886a0b37a92290c9b3ab4695 Mon Sep 17 00:00:00 2001 From: ka3de Date: Wed, 10 May 2023 09:15:52 +0200 Subject: [PATCH 21/22] Add k6test package godoc --- k6ext/k6test/doc.go | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 k6ext/k6test/doc.go diff --git a/k6ext/k6test/doc.go b/k6ext/k6test/doc.go new file mode 100644 index 000000000..125b2c77d --- /dev/null +++ b/k6ext/k6test/doc.go @@ -0,0 +1,2 @@ +// Package k6test provides mock implementations of k6 elements for testing purposes. +package k6test From a6b5b6010919fb9bc36abdac99e2cf56785719f3 Mon Sep 17 00:00:00 2001 From: ka3de Date: Wed, 10 May 2023 12:19:29 +0200 Subject: [PATCH 22/22] Tidy up dependencies --- go.sum | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/go.sum b/go.sum index 2e6b2c532..8a4920625 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f h1:IXSA5gow10s7zIOJfPOpXDtNBWCTA0715BDAhoJBXEs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/chromedp/cdproto v0.0.0-20221023212508-67ada9507fb2 h1:xESwMZNYkDnZf9MUk+6lXfMbpDnEJwlEuIxKYKM1vJY= github.com/chromedp/cdproto v0.0.0-20221023212508-67ada9507fb2/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= @@ -19,6 +21,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= @@ -32,6 +35,7 @@ github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8Wlg github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible h1:bopx7t9jyUNX1ebhr0G4gtQWmUOgwQRI0QsYhdYLgkU= github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= @@ -53,10 +57,16 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/xk6-redis v0.1.1 h1:rvWnLanRB2qzDwuY6NMBe6PXei3wJ3kjYvfCwRJ+q+8= +github.com/grafana/xk6-timers v0.1.2 h1:YVM6hPDgvy4SkdZQpd+/r9M0kDi1g+QdbSxW5ClfwDk= +github.com/grafana/xk6-webcrypto v0.1.0 h1:StrQZkUi4vo3bAMmBUHvIQ8P+zNKCH3AwN22TZdDwHs= +github.com/grafana/xk6-websockets v0.2.0 h1:oZcq4lm/p/Tc94ZMMNeYDML0DjU39jasC6kTyc6iF+8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/jhump/protoreflect v1.15.0 h1:U5T5/2LF0AZQFP9T4W5GfBjBaTruomrKobiR4E+oA/Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= @@ -81,6 +91,7 @@ github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa/go.mod h1:f github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd h1:AC3N94irbx2kWGA8f/2Ks7EQl2LxKIRQYuT9IJDwgiI= github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd/go.mod h1:9vRHVuLCjoFfE3GT06X0spdOAO+Zzo4AMjdIwUHBvAk= github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1 h1:94EkGmhXrVUEal+uLwFUf4fMXPhZpM5tYxuIsxrCCbI= +github.com/mstoykov/k6-taskqueue-lib v0.1.0 h1:M3eww1HSOLEN6rIkbNOJHhOVhlqnqkhYj7GTieiMBz4= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=