From 290b9129e896457bcb3347df0d379cbe9f21ac51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:31:33 +0300 Subject: [PATCH 01/46] Use *Browser in test browser --- tests/test_browser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 1a8e74d39..969756b1f 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -38,7 +38,7 @@ type testBrowser struct { browserType *chromium.BrowserType - api.Browser + *common.Browser cancel context.CancelFunc } @@ -143,7 +143,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { http: testServer, vu: vu, logCache: lc, - Browser: b, + Browser: cb, browserType: bt, pid: pid, wsURL: cb.WsURL(), From 303ddc2fb542bb5c18729fb45c7baed448eb8396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:36:52 +0300 Subject: [PATCH 02/46] Use *Page in test browser --- tests/launch_options_slowmo_test.go | 46 +++++++++++++++-------------- tests/test_browser.go | 2 +- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/tests/launch_options_slowmo_test.go b/tests/launch_options_slowmo_test.go index d297a1cb4..4fa67caf0 100644 --- a/tests/launch_options_slowmo_test.go +++ b/tests/launch_options_slowmo_test.go @@ -24,14 +24,14 @@ func TestBrowserOptionsSlowMo(t *testing.T) { t.Run("check", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Check(".check", nil) }) }) t.Run("click", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { err := p.Click("button", nil) assert.NoError(t, err) }) @@ -39,21 +39,21 @@ func TestBrowserOptionsSlowMo(t *testing.T) { t.Run("dblClick", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Dblclick("button", nil) }) }) t.Run("dispatchEvent", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.DispatchEvent("button", "click", goja.Null(), nil) }) }) t.Run("emulateMedia", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.EmulateMedia(tb.toGojaValue(struct { Media string `js:"media"` }{ @@ -64,35 +64,35 @@ func TestBrowserOptionsSlowMo(t *testing.T) { t.Run("evaluate", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Evaluate(tb.toGojaValue("() => void 0")) }) }) t.Run("evaluateHandle", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.EvaluateHandle(tb.toGojaValue("() => window")) }) }) t.Run("fill", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Fill(".fill", "foo", nil) }) }) t.Run("focus", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Focus("button", nil) }) }) t.Run("goto", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { _, err := p.Goto("about:blank", nil) require.NoError(t, err) }) @@ -100,61 +100,61 @@ func TestBrowserOptionsSlowMo(t *testing.T) { t.Run("hover", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Hover("button", nil) }) }) t.Run("press", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Press("button", "Enter", nil) }) }) t.Run("reload", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Reload(nil) }) }) t.Run("setContent", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.SetContent("hello world", nil) }) }) /*t.Run("setInputFiles", func(t *testing.T) { - testPageSlowMoImpl(t, tb, func(_ *Browser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *Browser, p *common.Page) { p.SetInputFiles(".file", nil, nil) }) })*/ t.Run("selectOption", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.SelectOption("select", tb.toGojaValue("foo"), nil) }) }) t.Run("setViewportSize", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.SetViewportSize(nil) }) }) t.Run("type", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Type(".fill", "a", nil) }) }) t.Run("uncheck", func(t *testing.T) { t.Parallel() tb := newTestBrowser(t, withFileServer()) - testPageSlowMoImpl(t, tb, func(_ *testBrowser, p api.Page) { + testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) { p.Uncheck(".uncheck", nil) }) }) @@ -301,8 +301,9 @@ func testSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser)) { require.True(t, didSlowMo, "expected action to have been slowed down") } -func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, api.Page)) { - p := tb.NewPage(nil) +func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, *common.Page)) { + p, ok := tb.NewPage(nil).(*common.Page) + require.True(t, ok, "expected page to be a *common.Page") p.SetContent(` @@ -318,7 +319,8 @@ func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, api } func testFrameSlowMoImpl(t *testing.T, tb *testBrowser, fn func(bt *testBrowser, f api.Frame)) { - p := tb.NewPage(nil) + p, ok := tb.NewPage(nil).(*common.Page) + require.True(t, ok, "expected page to be a *common.Page") f := tb.attachFrame(p, "frame1", tb.staticURL("empty.html")) f.SetContent(` diff --git a/tests/test_browser.go b/tests/test_browser.go index 969756b1f..d713b3fa0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -227,7 +227,7 @@ func (b *testBrowser) Cancel() { } // attachFrame attaches the frame to the page and returns it. -func (b *testBrowser) attachFrame(page api.Page, frameID string, url string) api.Frame { +func (b *testBrowser) attachFrame(page *common.Page, frameID string, url string) api.Frame { b.t.Helper() pageFn := ` From de26ffa429ba35d29b3f612779e9cc200706d504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:37:24 +0300 Subject: [PATCH 03/46] Add t.Helper to TestBrowserOptionsSlowMo --- tests/launch_options_slowmo_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/launch_options_slowmo_test.go b/tests/launch_options_slowmo_test.go index 4fa67caf0..459cb28e2 100644 --- a/tests/launch_options_slowmo_test.go +++ b/tests/launch_options_slowmo_test.go @@ -281,6 +281,8 @@ func TestBrowserOptionsSlowMo(t *testing.T) { } func testSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser)) { + t.Helper() + hooks := common.GetHooks(tb.ctx) currentHook := hooks.Get(common.HookApplySlowMo) chCalled := make(chan bool, 1) @@ -302,6 +304,8 @@ func testSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser)) { } func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, *common.Page)) { + t.Helper() + p, ok := tb.NewPage(nil).(*common.Page) require.True(t, ok, "expected page to be a *common.Page") @@ -319,6 +323,8 @@ func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, *co } func testFrameSlowMoImpl(t *testing.T, tb *testBrowser, fn func(bt *testBrowser, f api.Frame)) { + t.Helper() + p, ok := tb.NewPage(nil).(*common.Page) require.True(t, ok, "expected page to be a *common.Page") From b13b991809c6e6b6da5120dc4baa376d96d689c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:39:13 +0300 Subject: [PATCH 04/46] Use *Page in tb.NewPage --- tests/launch_options_slowmo_test.go | 7 ++----- tests/test_browser.go | 9 ++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/launch_options_slowmo_test.go b/tests/launch_options_slowmo_test.go index 459cb28e2..628d38a42 100644 --- a/tests/launch_options_slowmo_test.go +++ b/tests/launch_options_slowmo_test.go @@ -306,9 +306,7 @@ func testSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser)) { func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, *common.Page)) { t.Helper() - p, ok := tb.NewPage(nil).(*common.Page) - require.True(t, ok, "expected page to be a *common.Page") - + p := tb.NewPage(nil) p.SetContent(` @@ -325,8 +323,7 @@ func testPageSlowMoImpl(t *testing.T, tb *testBrowser, fn func(*testBrowser, *co func testFrameSlowMoImpl(t *testing.T, tb *testBrowser, fn func(bt *testBrowser, f api.Frame)) { t.Helper() - p, ok := tb.NewPage(nil).(*common.Page) - require.True(t, ok, "expected page to be a *common.Page") + p := tb.NewPage(nil) f := tb.attachFrame(p, "frame1", tb.staticURL("empty.html")) f.SetContent(` diff --git a/tests/test_browser.go b/tests/test_browser.go index d713b3fa0..c9d494de6 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -156,15 +156,18 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { return tbr } -// NewPage is a wrapper around api.Browser.NewPage that fails the test if an +// NewPage is a wrapper around Browser.NewPage that fails the test if an // error occurs. Added this helper to avoid boilerplate code in tests. -func (b *testBrowser) NewPage(opts goja.Value) api.Page { +func (b *testBrowser) NewPage(opts goja.Value) *common.Page { b.t.Helper() p, err := b.Browser.NewPage(opts) require.NoError(b.t, err) - return p + pp, ok := p.(*common.Page) + require.Truef(b.t, ok, "want *common.Page, got %T", p) + + return pp } // withHandler adds the given handler to the HTTP test server and makes it From 80631844a4fd908463d08f54d0010b124042ebb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:41:02 +0300 Subject: [PATCH 05/46] Use *Frame in tb.attachFrame --- tests/test_browser.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index c9d494de6..32e8c8808 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -8,7 +8,6 @@ import ( "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" @@ -230,7 +229,7 @@ func (b *testBrowser) Cancel() { } // attachFrame attaches the frame to the page and returns it. -func (b *testBrowser) attachFrame(page *common.Page, frameID string, url string) api.Frame { +func (b *testBrowser) attachFrame(page *common.Page, frameID string, url string) *common.Frame { b.t.Helper() pageFn := ` @@ -253,7 +252,10 @@ func (b *testBrowser) attachFrame(page *common.Page, frameID string, url string) f, err := h.AsElement().ContentFrame() require.NoError(b.t, err) - return f + ff, ok := f.(*common.Frame) + require.Truef(b.t, ok, "want *common.Frame, got %T", f) + + return ff } // runtime returns a VU runtime. From 89602b5777ebef9afe9205c1ba004f4467698193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:46:20 +0300 Subject: [PATCH 06/46] Remove testPromise --- tests/test_browser.go | 70 ------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 32e8c8808..e3a3bd036 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -331,76 +331,6 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e } } -func (b *testBrowser) promiseAll(promises ...*goja.Promise) testPromise { - b.t.Helper() - rt := b.runtime() - val, err := rt.RunString(`(function(...promises) { return Promise.all(...promises) })`) - require.NoError(b.t, err) - cal, ok := goja.AssertFunction(val) - require.True(b.t, ok) - valPromises := make([]goja.Value, len(promises)) - for i, promise := range promises { - valPromises[i] = rt.ToValue(promise) - } - val, err = cal(goja.Undefined(), rt.ToValue(valPromises)) - require.NoError(b.t, err) - newPromise, ok := val.Export().(*goja.Promise) - require.True(b.t, ok) - - return testPromise{ - Promise: newPromise, - call: cal, - tb: b, - } -} - -func (b *testBrowser) promise(promise *goja.Promise) testPromise { - b.t.Helper() - rt := b.runtime() - val, err := rt.RunString(` - (function(promise, resolve, reject) { - return promise.then(resolve, reject) - }) - `) - require.NoError(b.t, err) - cal, ok := goja.AssertFunction(val) - require.True(b.t, ok) - - return testPromise{ - Promise: promise, - call: cal, - tb: b, - } -} - -type testPromise struct { - *goja.Promise - tb *testBrowser - call goja.Callable -} - -func (t testPromise) then(resolve any, reject ...any) testPromise { - var ( - val goja.Value - err error - rt = t.tb.runtime() - ) - switch len(reject) { - case 0: - val, err = t.call(goja.Undefined(), rt.ToValue(t.Promise), rt.ToValue(resolve)) - case 1: - val, err = t.call(goja.Undefined(), rt.ToValue(t.Promise), rt.ToValue(resolve), rt.ToValue(reject[0])) - default: - panic("too many arguments to promiseThen") - } - require.NoError(t.tb.t, err) - - p, ok := val.Export().(*goja.Promise) - require.True(t.tb.t, ok) - - return t.tb.promise(p) -} - // httpServerOption is used to detect whether to enable the HTTP test // server. type httpServerOption struct{} From 11053e157925ee8cca97fe9fb03e21108db13838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:46:42 +0300 Subject: [PATCH 07/46] Fix linter errs in tb.run --- tests/test_browser.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index e3a3bd036..9d552b726 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -287,7 +287,7 @@ func (b *testBrowser) runJavaScript(s string, args ...any) (goja.Value, error) { } // Run the given functions in parallel and waits for them to finish. -func (b *testBrowser) run(ctx context.Context, fs ...func() error) error { //nolint:unused,deadcode +func (b *testBrowser) run(ctx context.Context, fs ...func() error) error { b.t.Helper() g, ctx := errgroup.WithContext(ctx) @@ -295,19 +295,25 @@ func (b *testBrowser) run(ctx context.Context, fs ...func() error) error { //nol f := f g.Go(func() error { errc := make(chan error, 1) - go func() { - errc <- f() - }() + go func() { errc <- f() }() select { case err := <-errc: return err case <-ctx.Done(): - return ctx.Err() + if err := ctx.Err(); err != nil { + return fmt.Errorf("while running %T: %w", f, err) + } } + + return nil }) } - return g.Wait() + if err := g.Wait(); err != nil { + return fmt.Errorf("while waiting for %T: %w", fs, err) + } + + return nil } // awaitWithTimeout is the same as await but takes a timeout and times out the function after the time runs out. From ba7247a915ffc1d745bda1a7f363d30c7a7a4706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:48:56 +0300 Subject: [PATCH 08/46] Fix linter import testBrowser --- tests/test_browser.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 9d552b726..8ea4752e0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -8,6 +8,10 @@ import ( "testing" "time" + "github.com/dop251/goja" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" + "github.com/grafana/xk6-browser/chromium" "github.com/grafana/xk6-browser/common" "github.com/grafana/xk6-browser/env" @@ -17,10 +21,6 @@ import ( k6http "go.k6.io/k6/js/modules/k6/http" k6httpmultibin "go.k6.io/k6/lib/testutils/httpmultibin" k6metrics "go.k6.io/k6/metrics" - - "github.com/dop251/goja" - "github.com/stretchr/testify/require" - "golang.org/x/sync/errgroup" ) // testBrowser is a test testBrowser for integration testing. From 7eeed2e25d0628b55019bdde96da3b0b0b263ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 10:49:48 +0300 Subject: [PATCH 09/46] Fix linter runJavaScript --- tests/test_browser.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 8ea4752e0..19adaa686 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -283,7 +283,11 @@ func (b *testBrowser) asGojaBool(v any) bool { // runJavaScript in the goja runtime. func (b *testBrowser) runJavaScript(s string, args ...any) (goja.Value, error) { b.t.Helper() - return b.runtime().RunString(fmt.Sprintf(s, args...)) + v, err := b.runtime().RunString(fmt.Sprintf(s, args...)) + if err != nil { + return nil, fmt.Errorf("while running %q(%v): %w", s, args, err) + } + return v, nil } // Run the given functions in parallel and waits for them to finish. From e8cc90623a146d3fdee9f832d2f86ed41378069e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:01:33 +0300 Subject: [PATCH 10/46] Move testBrowser opts to a type --- tests/test_browser.go | 78 ++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 19adaa686..f222916cc 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -42,6 +42,43 @@ type testBrowser struct { cancel context.CancelFunc } +type testBrowserOptions struct { + fileServer bool + logCache bool + httpMultiBin bool + samples chan k6metrics.SampleContainer + skipClose bool + lookupFunc env.LookupFunc +} + +func newTestBrowserOptions(opts ...any) *testBrowserOptions { + // default lookup function is env.Lookup so that we can + // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. + tbo := &testBrowserOptions{ + samples: make(chan k6metrics.SampleContainer, 1000), + lookupFunc: env.Lookup, + } + for _, opt := range opts { + switch opt := opt.(type) { + case httpServerOption: + tbo.httpMultiBin = true + case fileServerOption: + tbo.fileServer = true + tbo.httpMultiBin = true + case logCacheOption: + tbo.logCache = true + case skipCloseOption: + tbo.skipClose = true + case withSamplesListener: + tbo.samples = opt + case env.LookupFunc: + tbo.lookupFunc = opt + } + } + + return tbo +} + // newTestBrowser configures and launches a new chrome browser. // // It automatically closes it when `t` returns unless `withSkipClose` option is provided. @@ -56,36 +93,9 @@ type testBrowser struct { func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tb.Helper() - // set default options and then customize them - var ( - enableHTTPMultiBin = false - enableFileServer = false - enableLogCache = false - skipClose = false - samples = make(chan k6metrics.SampleContainer, 1000) - // default lookup function is env.Lookup so that we can - // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. - lookupFunc = env.Lookup - ) - for _, opt := range opts { - switch opt := opt.(type) { - case httpServerOption: - enableHTTPMultiBin = true - case fileServerOption: - enableFileServer = true - enableHTTPMultiBin = true - case logCacheOption: - enableLogCache = true - case skipCloseOption: - skipClose = true - case withSamplesListener: - samples = opt - case env.LookupFunc: - lookupFunc = opt - } - } + tbopts := newTestBrowserOptions(opts...) - vu := setupHTTPTestModuleInstance(tb, samples) + vu := setupHTTPTestModuleInstance(tb, tbopts.samples) dummyCtx, cancel := context.WithCancel(vu.Context()) tb.Cleanup(cancel) @@ -94,7 +104,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { registry := k6metrics.NewRegistry() k6m := k6ext.RegisterCustomMetrics(registry) vu.CtxField = k6ext.WithCustomMetrics(vu.Context(), k6m) - vu.InitEnvField.LookupEnv = lookupFunc + vu.InitEnvField.LookupEnv = tbopts.lookupFunc bt := chromium.NewBrowserType(vu) @@ -108,10 +118,10 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { lc *logCache ) - if enableLogCache { + if tbopts.logCache { lc = attachLogCache(tb, state.Logger) } - if enableHTTPMultiBin { + if tbopts.httpMultiBin { testServer = k6httpmultibin.NewHTTPMultiBin(tb) state.TLSConfig = testServer.TLSClientConfig state.Transport = testServer.HTTPTransport @@ -130,7 +140,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { select { case <-vu.Context().Done(): default: - if !skipClose { + if !tbopts.skipClose { b.Close() } } @@ -148,7 +158,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { wsURL: cb.WsURL(), cancel: cancel, } - if enableFileServer { + if tbopts.fileServer { tbr = tbr.withFileServer() } From a48248603da3c93103aea1ac1ffc64e624d9c9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:13:41 +0300 Subject: [PATCH 11/46] Refactor vu init in testBrowser This commit prepares the testBrowser BrowserType init before the next refactoring commit. No behavior change is involved! --- tests/test_browser.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index f222916cc..f6d106ba0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -96,16 +96,15 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tbopts := newTestBrowserOptions(opts...) vu := setupHTTPTestModuleInstance(tb, tbopts.samples) - - dummyCtx, cancel := context.WithCancel(vu.Context()) - tb.Cleanup(cancel) - vu.CtxField = dummyCtx - registry := k6metrics.NewRegistry() k6m := k6ext.RegisterCustomMetrics(registry) vu.CtxField = k6ext.WithCustomMetrics(vu.Context(), k6m) vu.InitEnvField.LookupEnv = tbopts.lookupFunc + ctx, cancel := context.WithCancel(vu.Context()) + vu.CtxField = ctx + tb.Cleanup(cancel) + bt := chromium.NewBrowserType(vu) // Delete the pre-init stage data. @@ -127,7 +126,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { state.Transport = testServer.HTTPTransport } - b, pid, err := bt.Launch(dummyCtx) + b, pid, err := bt.Launch(vu.Context()) if err != nil { tb.Fatalf("testBrowser: %v", err) } From ae6f79a4fc61750d4dd69caa5c83dc700b513f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:26:10 +0300 Subject: [PATCH 12/46] Move testBrowserType init to func --- tests/test_browser.go | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index f6d106ba0..e3a96323a 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -94,21 +94,8 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tb.Helper() tbopts := newTestBrowserOptions(opts...) - - vu := setupHTTPTestModuleInstance(tb, tbopts.samples) - registry := k6metrics.NewRegistry() - k6m := k6ext.RegisterCustomMetrics(registry) - vu.CtxField = k6ext.WithCustomMetrics(vu.Context(), k6m) - vu.InitEnvField.LookupEnv = tbopts.lookupFunc - - ctx, cancel := context.WithCancel(vu.Context()) - vu.CtxField = ctx - tb.Cleanup(cancel) - - bt := chromium.NewBrowserType(vu) - - // Delete the pre-init stage data. - vu.MoveToVUContext() + bt, vu, stop := newBrowserTypeWithVU(tb, tbopts) + tb.Cleanup(stop) // enable the HTTP test server only when necessary var ( @@ -155,7 +142,7 @@ func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { browserType: bt, pid: pid, wsURL: cb.WsURL(), - cancel: cancel, + cancel: stop, } if tbopts.fileServer { tbr = tbr.withFileServer() @@ -423,3 +410,27 @@ func setupHTTPTestModuleInstance(tb testing.TB, samples chan k6metrics.SampleCon return vu } + +func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( + _ *chromium.BrowserType, + _ *k6test.VU, + cancel func(), +) { + tb.Helper() + + vu := setupHTTPTestModuleInstance(tb, opts.samples) + registry := k6metrics.NewRegistry() + k6m := k6ext.RegisterCustomMetrics(registry) + vu.CtxField = k6ext.WithCustomMetrics(vu.Context(), k6m) + vu.InitEnvField.LookupEnv = opts.lookupFunc + + ctx, cancel := context.WithCancel(vu.Context()) + vu.CtxField = ctx + + bt := chromium.NewBrowserType(vu) + + // Delete the pre-init stage data. + vu.MoveToVUContext() + + return bt, vu, cancel +} From 627497e82eb3dba79e36000a3235bd56be9df054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:30:15 +0300 Subject: [PATCH 13/46] Refactor newBrowserTypeWithVU --- tests/test_browser.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index e3a96323a..3fdbbaad5 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -419,9 +419,10 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( tb.Helper() vu := setupHTTPTestModuleInstance(tb, opts.samples) - registry := k6metrics.NewRegistry() - k6m := k6ext.RegisterCustomMetrics(registry) - vu.CtxField = k6ext.WithCustomMetrics(vu.Context(), k6m) + vu.CtxField = k6ext.WithCustomMetrics( + vu.Context(), + k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), + ) vu.InitEnvField.LookupEnv = opts.lookupFunc ctx, cancel := context.WithCancel(vu.Context()) From 5843b5aa97c9ffd988ff5d7c94d90135749e7ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:33:21 +0300 Subject: [PATCH 14/46] Move setupHTTPTestModuleInstance --- tests/test_browser.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 3fdbbaad5..03a24be7e 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -395,22 +395,6 @@ func withSkipClose() skipCloseOption { // so that the test can read the metrics being emitted to the channel. type withSamplesListener chan k6metrics.SampleContainer -func setupHTTPTestModuleInstance(tb testing.TB, samples chan k6metrics.SampleContainer) *k6test.VU { - tb.Helper() - - var ( - vu = k6test.NewVU(tb, k6test.WithSamplesListener(samples)) - root = k6http.New() - ) - - mi, ok := root.NewModuleInstance(vu).(*k6http.ModuleInstance) - require.True(tb, ok) - - require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) - - return vu -} - func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( _ *chromium.BrowserType, _ *k6test.VU, @@ -418,18 +402,20 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( ) { tb.Helper() - vu := setupHTTPTestModuleInstance(tb, opts.samples) + // Prepare the VU. + vu := k6test.NewVU(tb, k6test.WithSamplesListener(opts.samples)) + mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) + require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) + require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) vu.CtxField = k6ext.WithCustomMetrics( vu.Context(), k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), ) vu.InitEnvField.LookupEnv = opts.lookupFunc - ctx, cancel := context.WithCancel(vu.Context()) vu.CtxField = ctx bt := chromium.NewBrowserType(vu) - // Delete the pre-init stage data. vu.MoveToVUContext() From bf958c54678b2ff2b240b2bd129c165b2d403aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:35:16 +0300 Subject: [PATCH 15/46] Refactor newBrowserTypeWithVU --- tests/test_browser.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 03a24be7e..341350365 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -407,13 +407,13 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) - vu.CtxField = k6ext.WithCustomMetrics( + metricsCtx := k6ext.WithCustomMetrics( vu.Context(), k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), ) - vu.InitEnvField.LookupEnv = opts.lookupFunc - ctx, cancel := context.WithCancel(vu.Context()) + ctx, cancel := context.WithCancel(metricsCtx) vu.CtxField = ctx + vu.InitEnvField.LookupEnv = opts.lookupFunc bt := chromium.NewBrowserType(vu) // Delete the pre-init stage data. From 74f494b168f5993184b1173b6135c02e8a7faa15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:39:33 +0300 Subject: [PATCH 16/46] Refactor testBrowser.withSkipClose --- tests/test_browser.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 341350365..1d2738d1f 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -67,12 +67,12 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { tbo.httpMultiBin = true case logCacheOption: tbo.logCache = true - case skipCloseOption: - tbo.skipClose = true case withSamplesListener: tbo.samples = opt case env.LookupFunc: tbo.lookupFunc = opt + case func(*testBrowserOptions): + opt(tbo) } } @@ -378,17 +378,15 @@ func withLogCache() logCacheOption { return struct{}{} } -// skipCloseOption is used to indicate that we shouldn't call Browser.Close() in -// t.Cleanup(), since it will presumably be done by the test. -type skipCloseOption struct{} - // withSkipClose skips calling Browser.Close() in t.Cleanup(). +// It indicates that we shouldn't call Browser.Close() in +// t.Cleanup(), since it will presumably be done by the test. // // example: // // b := TestBrowser(t, withSkipClose()) -func withSkipClose() skipCloseOption { - return struct{}{} +func withSkipClose() func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.skipClose = true } } // withSamplesListener is used to indicate we want to use a bidirectional channel From e4d991c7bfb4b1992a1ef1023f5f9436ef24a631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:42:05 +0300 Subject: [PATCH 17/46] Refactor testBrowser.withHTTPServer --- tests/test_browser.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 1d2738d1f..5e1a5b3c0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,8 +60,6 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { } for _, opt := range opts { switch opt := opt.(type) { - case httpServerOption: - tbo.httpMultiBin = true case fileServerOption: tbo.fileServer = true tbo.httpMultiBin = true @@ -337,17 +335,14 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e } } -// httpServerOption is used to detect whether to enable the HTTP test -// server. -type httpServerOption struct{} - // withHTTPServer enables the HTTP test server. +// It is used to detect whether to enable the HTTP test server. // // example: // // b := TestBrowser(t, withHTTPServer()) -func withHTTPServer() httpServerOption { - return struct{}{} +func withHTTPServer() func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.httpMultiBin = true } } // fileServerOption is used to detect whether enable the static file From 162cf167520d31155df4d410b668d8c9406c48c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:44:52 +0300 Subject: [PATCH 18/46] Refactor testBrowser.withFileServer --- tests/test_browser.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 5e1a5b3c0..c8d281ed2 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,9 +60,6 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { } for _, opt := range opts { switch opt := opt.(type) { - case fileServerOption: - tbo.fileServer = true - tbo.httpMultiBin = true case logCacheOption: tbo.logCache = true case withSamplesListener: @@ -345,10 +342,6 @@ func withHTTPServer() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.httpMultiBin = true } } -// fileServerOption is used to detect whether enable the static file -// server. -type fileServerOption struct{} - // withFileServer enables the HTTP test server and serves a file server // for static files. // @@ -357,8 +350,11 @@ type fileServerOption struct{} // example: // // b := TestBrowser(t, withFileServer()) -func withFileServer() fileServerOption { - return struct{}{} +func withFileServer() func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { + tb.httpMultiBin = true + tb.fileServer = true + } } // logCacheOption is used to detect whether to enable the log cache. From 45171142bdf615bfda44c792266bcdef8e6178d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:45:28 +0300 Subject: [PATCH 19/46] Refactor testBrowser.withLogCache --- tests/test_browser.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index c8d281ed2..d03e1f42d 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,8 +60,6 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { } for _, opt := range opts { switch opt := opt.(type) { - case logCacheOption: - tbo.logCache = true case withSamplesListener: tbo.samples = opt case env.LookupFunc: @@ -357,16 +355,13 @@ func withFileServer() func(tb *testBrowserOptions) { } } -// logCacheOption is used to detect whether to enable the log cache. -type logCacheOption struct{} - // withLogCache enables the log cache. // // example: // // b := TestBrowser(t, withLogCache()) -func withLogCache() logCacheOption { - return struct{}{} +func withLogCache() func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.logCache = true } } // withSkipClose skips calling Browser.Close() in t.Cleanup(). From 30b0ff733b5a816b83d9340d3216acbee5936a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:48:38 +0300 Subject: [PATCH 20/46] Refactor testBrowser.withSampleListener --- tests/test_browser.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index d03e1f42d..a243d5825 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,8 +60,6 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { } for _, opt := range opts { switch opt := opt.(type) { - case withSamplesListener: - tbo.samples = opt case env.LookupFunc: tbo.lookupFunc = opt case func(*testBrowserOptions): @@ -377,7 +375,9 @@ func withSkipClose() func(tb *testBrowserOptions) { // withSamplesListener is used to indicate we want to use a bidirectional channel // so that the test can read the metrics being emitted to the channel. -type withSamplesListener chan k6metrics.SampleContainer +func withSamplesListener(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.samples = sc } +} func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( _ *chromium.BrowserType, From 480f4061db41fd84434ba417ad73bd8ba99fe507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:49:09 +0300 Subject: [PATCH 21/46] Rename testBrowser.withSampleListener --- tests/test_browser.go | 4 ++-- tests/webvital_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index a243d5825..110dba335 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -373,9 +373,9 @@ func withSkipClose() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.skipClose = true } } -// withSamplesListener is used to indicate we want to use a bidirectional channel +// withSamples is used to indicate we want to use a bidirectional channel // so that the test can read the metrics being emitted to the channel. -func withSamplesListener(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { +func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.samples = sc } } diff --git a/tests/webvital_test.go b/tests/webvital_test.go index 025e289f4..fa946703d 100644 --- a/tests/webvital_test.go +++ b/tests/webvital_test.go @@ -17,7 +17,7 @@ import ( func TestWebVitalMetric(t *testing.T) { var ( samples = make(chan k6metrics.SampleContainer) - browser = newTestBrowser(t, withFileServer(), withSamplesListener(samples)) + browser = newTestBrowser(t, withFileServer(), withSamples(samples)) page = browser.NewPage(nil) expected = map[string]bool{ "browser_web_vital_ttfb": false, From 9da516965d94e813ebc642482fca3e041645ade9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:54:49 +0300 Subject: [PATCH 22/46] Move testBrowserOptions closer --- k6ext/k6test/vu.go | 6 +- tests/test_browser.go | 146 +++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/k6ext/k6test/vu.go b/k6ext/k6test/vu.go index 2f5ab5bb9..f1bcc0014 100644 --- a/k6ext/k6test/vu.go +++ b/k6ext/k6test/vu.go @@ -53,9 +53,9 @@ func (v *VU) AssertSamples(assertSample func(s k6metrics.Sample)) int { return n } -// WithSamplesListener is used to indicate we want to use a bidirectional channel +// WithSamples is used to indicate we want to use a bidirectional channel // so that the test can read the metrics being emitted to the channel. -type WithSamplesListener chan k6metrics.SampleContainer +type WithSamples chan k6metrics.SampleContainer // NewVU returns a mock k6 VU. // @@ -71,7 +71,7 @@ func NewVU(tb testing.TB, opts ...any) *VU { ) for _, opt := range opts { switch opt := opt.(type) { - case WithSamplesListener: + case WithSamples: samples = opt case env.LookupFunc: lookupFunc = opt diff --git a/tests/test_browser.go b/tests/test_browser.go index 110dba335..8bcdf36fc 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -42,34 +42,6 @@ type testBrowser struct { cancel context.CancelFunc } -type testBrowserOptions struct { - fileServer bool - logCache bool - httpMultiBin bool - samples chan k6metrics.SampleContainer - skipClose bool - lookupFunc env.LookupFunc -} - -func newTestBrowserOptions(opts ...any) *testBrowserOptions { - // default lookup function is env.Lookup so that we can - // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. - tbo := &testBrowserOptions{ - samples: make(chan k6metrics.SampleContainer, 1000), - lookupFunc: env.Lookup, - } - for _, opt := range opts { - switch opt := opt.(type) { - case env.LookupFunc: - tbo.lookupFunc = opt - case func(*testBrowserOptions): - opt(tbo) - } - } - - return tbo -} - // newTestBrowser configures and launches a new chrome browser. // // It automatically closes it when `t` returns unless `withSkipClose` option is provided. @@ -78,7 +50,7 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { // - withHTTPServer: enables the HTTPMultiBin server. // - withFileServer: enables the HTTPMultiBin server and serves the given files. // - withLogCache: enables the log cache. -// - withSamplesListener: provides a channel to receive the browser metrics. +// - withSamples: provides a channel to receive the browser metrics. // - env.LookupFunc: provides a custom lookup function for environment variables. // - withSkipClose: skips closing the browser when the test finishes. func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { @@ -156,38 +128,8 @@ func (b *testBrowser) NewPage(opts goja.Value) *common.Page { return pp } -// withHandler adds the given handler to the HTTP test server and makes it -// accessible with the given pattern. -func (b *testBrowser) withHandler(pattern string, handler http.HandlerFunc) *testBrowser { - b.t.Helper() - - if b.http == nil { - b.t.Fatalf("You should enable HTTP test server, see: withHTTPServer option") - } - b.http.Mux.Handle(pattern, handler) - return b -} - const testBrowserStaticDir = "static" -// withFileServer serves a file server using the HTTP test server that is -// accessible via `testBrowserStaticDir` prefix. -// -// This method is for enabling the static file server after starting a test -// browser. For early starting the file server see withFileServer function. -func (b *testBrowser) withFileServer() *testBrowser { - b.t.Helper() - - const ( - slash = string(os.PathSeparator) - path = slash + testBrowserStaticDir + slash - ) - - fs := http.FileServer(http.Dir(testBrowserStaticDir)) - - return b.withHandler(path, http.StripPrefix(path, fs).ServeHTTP) -} - // URL returns the listening HTTP test server's URL combined with the given path. func (b *testBrowser) URL(path string) string { b.t.Helper() @@ -328,14 +270,50 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e } } -// withHTTPServer enables the HTTP test server. -// It is used to detect whether to enable the HTTP test server. -// -// example: +// withFileServer serves a file server using the HTTP test server that is +// accessible via `testBrowserStaticDir` prefix. // -// b := TestBrowser(t, withHTTPServer()) -func withHTTPServer() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.httpMultiBin = true } +// This method is for enabling the static file server after starting a test +// browser. For early starting the file server see withFileServer function. +func (b *testBrowser) withFileServer() *testBrowser { + b.t.Helper() + + const ( + slash = string(os.PathSeparator) + path = slash + testBrowserStaticDir + slash + ) + + fs := http.FileServer(http.Dir(testBrowserStaticDir)) + + return b.withHandler(path, http.StripPrefix(path, fs).ServeHTTP) +} + +type testBrowserOptions struct { + fileServer bool + logCache bool + httpMultiBin bool + samples chan k6metrics.SampleContainer + skipClose bool + lookupFunc env.LookupFunc +} + +func newTestBrowserOptions(opts ...any) *testBrowserOptions { + // default lookup function is env.Lookup so that we can + // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. + tbo := &testBrowserOptions{ + samples: make(chan k6metrics.SampleContainer, 1000), + lookupFunc: env.Lookup, + } + for _, opt := range opts { + switch opt := opt.(type) { + case env.LookupFunc: + tbo.lookupFunc = opt + case func(*testBrowserOptions): + opt(tbo) + } + } + + return tbo } // withFileServer enables the HTTP test server and serves a file server @@ -353,6 +331,28 @@ func withFileServer() func(tb *testBrowserOptions) { } } +// withHandler adds the given handler to the HTTP test server and makes it +// accessible with the given pattern. +func (b *testBrowser) withHandler(pattern string, handler http.HandlerFunc) *testBrowser { + b.t.Helper() + + if b.http == nil { + b.t.Fatalf("You should enable HTTP test server, see: withHTTPServer option") + } + b.http.Mux.Handle(pattern, handler) + return b +} + +// withHTTPServer enables the HTTP test server. +// It is used to detect whether to enable the HTTP test server. +// +// example: +// +// b := TestBrowser(t, withHTTPServer()) +func withHTTPServer() func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.httpMultiBin = true } +} + // withLogCache enables the log cache. // // example: @@ -362,6 +362,12 @@ func withLogCache() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.logCache = true } } +// withSamples is used to indicate we want to use a bidirectional channel +// so that the test can read the metrics being emitted to the channel. +func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { + return func(tb *testBrowserOptions) { tb.samples = sc } +} + // withSkipClose skips calling Browser.Close() in t.Cleanup(). // It indicates that we shouldn't call Browser.Close() in // t.Cleanup(), since it will presumably be done by the test. @@ -373,12 +379,6 @@ func withSkipClose() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.skipClose = true } } -// withSamples is used to indicate we want to use a bidirectional channel -// so that the test can read the metrics being emitted to the channel. -func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.samples = sc } -} - func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( _ *chromium.BrowserType, _ *k6test.VU, @@ -387,7 +387,7 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( tb.Helper() // Prepare the VU. - vu := k6test.NewVU(tb, k6test.WithSamplesListener(opts.samples)) + vu := k6test.NewVU(tb, k6test.WithSamples(opts.samples)) mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) From fa42d5e7d019cc86581f2f88ca743a1b20bba044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 11:58:05 +0300 Subject: [PATCH 23/46] Add testBrowser.withEnvLookup for consistency --- tests/test_browser.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 8bcdf36fc..c5ff9c4c8 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -47,11 +47,11 @@ type testBrowser struct { // It automatically closes it when `t` returns unless `withSkipClose` option is provided. // // The following opts are available to customize the testBrowser: -// - withHTTPServer: enables the HTTPMultiBin server. +// - withEnvLookup: provides a custom lookup function for environment variables. // - withFileServer: enables the HTTPMultiBin server and serves the given files. +// - withHTTPServer: enables the HTTPMultiBin server. // - withLogCache: enables the log cache. // - withSamples: provides a channel to receive the browser metrics. -// - env.LookupFunc: provides a custom lookup function for environment variables. // - withSkipClose: skips closing the browser when the test finishes. func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { tb.Helper() @@ -316,6 +316,15 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { return tbo } +// withEnvLookup sets the lookup function for environment variables. +// +// example: +// +// b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0"))) +func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { //nolint:unused + return func(tb *testBrowserOptions) { tb.lookupFunc = lookupFunc } +} + // withFileServer enables the HTTP test server and serves a file server // for static files. // From 7d3bafdc19e556475120bf638a2fb92448a9ca37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 12:04:12 +0300 Subject: [PATCH 24/46] Use testBrowser.withEnvLookup --- tests/browser_test.go | 6 +++++- tests/frame_test.go | 6 +++++- tests/test_browser.go | 2 +- tests/test_browser_test.go | 5 ++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/browser_test.go b/tests/browser_test.go index 9ec03f8ca..054c24d70 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -45,7 +45,11 @@ func TestTmpDirCleanup(t *testing.T) { const tmpDirPath = "./" - b := newTestBrowser(t, withSkipClose(), env.ConstLookup("TMPDIR", tmpDirPath)) + b := newTestBrowser( + t, + withSkipClose(), + withEnvLookup(env.ConstLookup("TMPDIR", tmpDirPath)), + ) p := b.NewPage(nil) err := p.Close(nil) require.NoError(t, err) diff --git a/tests/frame_test.go b/tests/frame_test.go index e3430b35b..5da1251d7 100644 --- a/tests/frame_test.go +++ b/tests/frame_test.go @@ -81,7 +81,11 @@ func TestFrameNoPanicWithEmbeddedIFrame(t *testing.T) { } // run the browser in headfull mode. - tb := newTestBrowser(t, withFileServer(), env.ConstLookup(env.BrowserHeadless, "0")) + tb := newTestBrowser( + t, + withFileServer(), + withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0")), + ) p := tb.NewPage(nil) _, err := p.Goto( diff --git a/tests/test_browser.go b/tests/test_browser.go index c5ff9c4c8..0519b2111 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -321,7 +321,7 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { // example: // // b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0"))) -func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { //nolint:unused +func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { return func(tb *testBrowserOptions) { tb.lookupFunc = lookupFunc } } diff --git a/tests/test_browser_test.go b/tests/test_browser_test.go index 24fb24016..7681ba6d9 100644 --- a/tests/test_browser_test.go +++ b/tests/test_browser_test.go @@ -39,6 +39,9 @@ func TestTestBrowserWithLookupFunc(t *testing.T) { tt := &testingT{TB: t} // this operation is expected to fail because the remote debugging port is // invalid, practically testing that the InitEnv.LookupEnv is used. - _ = newTestBrowser(tt, env.ConstLookup(env.BrowserArguments, "remote-debugging-port=99999")) + _ = newTestBrowser( + tt, + withEnvLookup(env.ConstLookup(env.BrowserArguments, "remote-debugging-port=99999")), + ) require.True(t, tt.fatalfCalled) } From 469a6190b9029756ef9023b53b30114ed8d6eb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 12:07:18 +0300 Subject: [PATCH 25/46] Refactor to testBrowserOptions setting --- tests/test_browser.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 0519b2111..7d82a99d6 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -53,7 +53,7 @@ type testBrowser struct { // - withLogCache: enables the log cache. // - withSamples: provides a channel to receive the browser metrics. // - withSkipClose: skips closing the browser when the test finishes. -func newTestBrowser(tb testing.TB, opts ...any) *testBrowser { +func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrowser { tb.Helper() tbopts := newTestBrowserOptions(opts...) @@ -297,7 +297,7 @@ type testBrowserOptions struct { lookupFunc env.LookupFunc } -func newTestBrowserOptions(opts ...any) *testBrowserOptions { +func newTestBrowserOptions(opts ...func(*testBrowserOptions)) *testBrowserOptions { // default lookup function is env.Lookup so that we can // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. tbo := &testBrowserOptions{ @@ -305,12 +305,7 @@ func newTestBrowserOptions(opts ...any) *testBrowserOptions { lookupFunc: env.Lookup, } for _, opt := range opts { - switch opt := opt.(type) { - case env.LookupFunc: - tbo.lookupFunc = opt - case func(*testBrowserOptions): - opt(tbo) - } + opt(tbo) } return tbo From 0be2d8873c5e6e0247ac7f1c76441bae6bec6f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 12:28:37 +0300 Subject: [PATCH 26/46] Refactor newTestBrowser --- tests/test_browser.go | 50 +++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 7d82a99d6..a5d8e152d 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -56,27 +56,24 @@ type testBrowser struct { func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrowser { tb.Helper() + tbr := &testBrowser{t: tb} tbopts := newTestBrowserOptions(opts...) - bt, vu, stop := newBrowserTypeWithVU(tb, tbopts) - tb.Cleanup(stop) - - // enable the HTTP test server only when necessary - var ( - testServer *k6httpmultibin.HTTPMultiBin - state = vu.StateField - lc *logCache - ) + tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbopts) + tb.Cleanup(tbr.cancel) if tbopts.logCache { - lc = attachLogCache(tb, state.Logger) + tbr.logCache = attachLogCache(tb, tbr.vu.StateField.Logger) } if tbopts.httpMultiBin { - testServer = k6httpmultibin.NewHTTPMultiBin(tb) - state.TLSConfig = testServer.TLSClientConfig - state.Transport = testServer.HTTPTransport + tbr.http = k6httpmultibin.NewHTTPMultiBin(tb) + tbr.vu.StateField.TLSConfig = tbr.http.TLSClientConfig + tbr.vu.StateField.Transport = tbr.http.HTTPTransport + } + if tbopts.fileServer { + tbr = tbr.withFileServer() } - b, pid, err := bt.Launch(vu.Context()) + b, pid, err := tbr.browserType.Launch(tbr.vu.Context()) if err != nil { tb.Fatalf("testBrowser: %v", err) } @@ -84,33 +81,20 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows if !ok { tb.Fatalf("testBrowser: unexpected browser %T", b) } - + tbr.Browser = cb + tbr.ctx = tbr.browserType.Ctx + tbr.pid = pid + tbr.wsURL = cb.WsURL() tb.Cleanup(func() { select { - case <-vu.Context().Done(): + case <-tbr.vu.Context().Done(): default: if !tbopts.skipClose { - b.Close() + cb.Close() } } }) - tbr := &testBrowser{ - t: tb, - ctx: bt.Ctx, - http: testServer, - vu: vu, - logCache: lc, - Browser: cb, - browserType: bt, - pid: pid, - wsURL: cb.WsURL(), - cancel: stop, - } - if tbopts.fileServer { - tbr = tbr.withFileServer() - } - return tbr } From 7e111cac486b3a7cc594d3ab0f698c0f20b0041f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 14:37:04 +0300 Subject: [PATCH 27/46] Use testBrowser helpers in tests --- tests/browser_test.go | 6 +++--- tests/browser_type_test.go | 2 +- tests/element_handle_test.go | 3 +-- tests/page_test.go | 18 ++++++------------ 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/tests/browser_test.go b/tests/browser_test.go index 054c24d70..47e13c66e 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -76,7 +76,7 @@ func TestBrowserOn(t *testing.T) { t.Parallel() b := newTestBrowser(t) - require.NoError(t, b.vu.Runtime().Set("b", b.Browser)) + require.NoError(t, b.runtime().Set("b", b.Browser)) _, err := b.runJavaScript(script, "wrongevent") require.Error(t, err) @@ -88,7 +88,7 @@ func TestBrowserOn(t *testing.T) { var ( b = newTestBrowser(t, withSkipClose()) - rt = b.vu.Runtime() + rt = b.runtime() log []string ) @@ -106,7 +106,7 @@ func TestBrowserOn(t *testing.T) { var ( b = newTestBrowser(t) - rt = b.vu.Runtime() + rt = b.runtime() log []string ) diff --git a/tests/browser_type_test.go b/tests/browser_type_test.go index e34b78b5a..156a1b6e2 100644 --- a/tests/browser_type_test.go +++ b/tests/browser_type_test.go @@ -37,7 +37,6 @@ func TestBrowserTypeLaunchToConnect(t *testing.T) { // We have to call launch method through JS API in Goja // to take mapping layer into account, instead of calling // BrowserType.Launch method directly - rt := vu.Runtime() root := browser.New() mod := root.NewModuleInstance(vu) jsMod, ok := mod.Exports().Default.(*browser.JSModule) @@ -45,6 +44,7 @@ func TestBrowserTypeLaunchToConnect(t *testing.T) { vu.MoveToVUContext() + rt := vu.Runtime() require.NoError(t, rt.Set("browser", jsMod.Browser)) _, err := rt.RunString(` const p = browser.newPage(); diff --git a/tests/element_handle_test.go b/tests/element_handle_test.go index 03ab56c18..85bf53537 100644 --- a/tests/element_handle_test.go +++ b/tests/element_handle_test.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/xk6-browser/api" "github.com/grafana/xk6-browser/common" - "github.com/dop251/goja" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -83,7 +82,7 @@ func TestElementHandleBoundingBoxSVG(t *testing.T) { }` var r api.Rect webBbox := p.Evaluate(tb.toGojaValue(pageFn), tb.toGojaValue(element)) - wb, _ := webBbox.(goja.Value) + wb := tb.asGojaValue(webBbox) err = tb.runtime().ExportTo(wb, &r) require.NoError(t, err) diff --git a/tests/page_test.go b/tests/page_test.go index 75508cfca..8a377b9e6 100644 --- a/tests/page_test.go +++ b/tests/page_test.go @@ -37,18 +37,15 @@ func TestPageEmulateMedia(t *testing.T) { })) result := p.Evaluate(tb.toGojaValue("() => matchMedia('print').matches")) - res, ok := result.(goja.Value) - require.True(t, ok) + res := tb.asGojaValue(result) assert.True(t, res.ToBoolean(), "expected media 'print'") result = p.Evaluate(tb.toGojaValue("() => matchMedia('(prefers-color-scheme: dark)').matches")) - res, ok = result.(goja.Value) - require.True(t, ok) + res = tb.asGojaValue(result) assert.True(t, res.ToBoolean(), "expected color scheme 'dark'") result = p.Evaluate(tb.toGojaValue("() => matchMedia('(prefers-reduced-motion: reduce)').matches")) - res, ok = result.(goja.Value) - require.True(t, ok) + res = tb.asGojaValue(result) assert.True(t, res.ToBoolean(), "expected reduced motion setting to be 'reduce'") } @@ -79,8 +76,7 @@ func TestPageEvaluate(t *testing.T) { ) require.IsType(t, tb.toGojaValue(""), got) - gotVal, ok := got.(goja.Value) - require.True(t, ok) + gotVal := tb.asGojaValue(got) assert.Equal(t, "test", gotVal.Export()) }) @@ -501,8 +497,7 @@ func TestPageWaitForFunction(t *testing.T) { assert.Contains(t, log, "ok: null") argEvalJS := p.Evaluate(tb.toGojaValue("() => window._arg")) - argEval, ok := argEvalJS.(goja.Value) - require.True(t, ok) + argEval := tb.asGojaValue(argEvalJS) var gotArg string _ = tb.runtime().ExportTo(argEval, &gotArg) assert.Equal(t, arg, gotArg) @@ -532,8 +527,7 @@ func TestPageWaitForFunction(t *testing.T) { assert.Contains(t, log, "ok: null") argEvalJS := p.Evaluate(tb.toGojaValue("() => window._args")) - argEval, ok := argEvalJS.(goja.Value) - require.True(t, ok) + argEval := tb.asGojaValue(argEvalJS) var gotArgs []int _ = tb.runtime().ExportTo(argEval, &gotArgs) assert.Equal(t, args, gotArgs) From 3d076a2bcdd1c52356fb280faf880bb771a94857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 14:40:58 +0300 Subject: [PATCH 28/46] Remove testBrowser.attachFrame It is only used in a single place. There is no need to make it reusable at the moment. --- tests/launch_options_slowmo_test.go | 26 +++++++++++++++++++++++-- tests/test_browser.go | 30 ----------------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/tests/launch_options_slowmo_test.go b/tests/launch_options_slowmo_test.go index 628d38a42..fdf97b863 100644 --- a/tests/launch_options_slowmo_test.go +++ b/tests/launch_options_slowmo_test.go @@ -325,7 +325,29 @@ func testFrameSlowMoImpl(t *testing.T, tb *testBrowser, fn func(bt *testBrowser, p := tb.NewPage(nil) - f := tb.attachFrame(p, "frame1", tb.staticURL("empty.html")) + pageFn := ` + async (frameId, url) => { + const frame = document.createElement('iframe'); + frame.src = url; + frame.id = frameId; + document.body.appendChild(frame); + await new Promise(x => frame.onload = x); + return frame; + } + ` + + h, err := p.EvaluateHandle( + tb.toGojaValue(pageFn), + tb.toGojaValue("frame1"), + tb.toGojaValue(tb.staticURL("empty.html"))) + require.NoError(tb.t, err) + + f, err := h.AsElement().ContentFrame() + require.NoError(tb.t, err) + + ff, ok := f.(*common.Frame) + require.Truef(tb.t, ok, "want *common.Frame, got %T", f) + f.SetContent(` @@ -336,5 +358,5 @@ func testFrameSlowMoImpl(t *testing.T, tb *testBrowser, fn func(bt *testBrowser, `, nil) - testSlowMoImpl(t, tb, func(tb *testBrowser) { fn(tb, f) }) + testSlowMoImpl(t, tb, func(tb *testBrowser) { fn(tb, ff) }) } diff --git a/tests/test_browser.go b/tests/test_browser.go index a5d8e152d..1ad82443f 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -141,36 +141,6 @@ func (b *testBrowser) Cancel() { b.cancel() } -// attachFrame attaches the frame to the page and returns it. -func (b *testBrowser) attachFrame(page *common.Page, frameID string, url string) *common.Frame { - b.t.Helper() - - pageFn := ` - async (frameId, url) => { - const frame = document.createElement('iframe'); - frame.src = url; - frame.id = frameId; - document.body.appendChild(frame); - await new Promise(x => frame.onload = x); - return frame; - } - ` - - h, err := page.EvaluateHandle( - b.toGojaValue(pageFn), - b.toGojaValue(frameID), - b.toGojaValue(url)) - require.NoError(b.t, err) - - f, err := h.AsElement().ContentFrame() - require.NoError(b.t, err) - - ff, ok := f.(*common.Frame) - require.Truef(b.t, ok, "want *common.Frame, got %T", f) - - return ff -} - // runtime returns a VU runtime. func (b *testBrowser) runtime() *goja.Runtime { return b.vu.Runtime() } From 52887c415456736be951d0bea6ae9a155248cf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 14:58:22 +0300 Subject: [PATCH 29/46] Refactor testBrowser exports --- tests/browser_context_options_test.go | 2 +- tests/browser_context_test.go | 2 +- tests/browser_test.go | 2 +- tests/frame_manager_test.go | 2 +- tests/lifecycle_wait_test.go | 2 +- tests/network_manager_test.go | 6 +++--- tests/page_test.go | 8 ++++---- tests/test_browser.go | 23 +++++++++-------------- 8 files changed, 21 insertions(+), 26 deletions(-) diff --git a/tests/browser_context_options_test.go b/tests/browser_context_options_test.go index b6fb36245..fb03f40c3 100644 --- a/tests/browser_context_options_test.go +++ b/tests/browser_context_options_test.go @@ -80,7 +80,7 @@ func TestBrowserContextOptionsExtraHTTPHeaders(t *testing.T) { require.NoError(t, err) err = tb.awaitWithTimeout(time.Second*5, func() error { - resp, err := p.Goto(tb.URL("/get"), nil) + resp, err := p.Goto(tb.url("/get"), nil) if err != nil { return err } diff --git a/tests/browser_context_test.go b/tests/browser_context_test.go index 4abfbcbdb..7b09ca49c 100644 --- a/tests/browser_context_test.go +++ b/tests/browser_context_test.go @@ -30,7 +30,7 @@ func TestBrowserContextAddCookies(t *testing.T) { url: "%v" } ]; - `, testCookieName, testCookieValue, tb.URL("")) + `, testCookieName, testCookieValue, tb.url("")) require.NoError(t, err) bc.AddCookies(cookies) diff --git a/tests/browser_test.go b/tests/browser_test.go index 47e13c66e..885596f57 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -113,7 +113,7 @@ func TestBrowserOn(t *testing.T) { 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, b.Cancel) + time.AfterFunc(100*time.Millisecond, b.cancelContext) _, err := b.runJavaScript(script, "disconnected") assert.ErrorContains(t, err, "browser.on promise rejected: context canceled") }) diff --git a/tests/frame_manager_test.go b/tests/frame_manager_test.go index 819694243..93163e5e8 100644 --- a/tests/frame_manager_test.go +++ b/tests/frame_manager_test.go @@ -92,7 +92,7 @@ func TestWaitForFrameNavigation(t *testing.T) { WaitUntil: common.LifecycleEventNetworkIdle, Timeout: common.DefaultTimeout, }) - _, err := p.Goto(tb.URL("/first"), opts) + _, err := p.Goto(tb.url("/first"), opts) require.NoError(t, err) waitForNav := func() error { diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index e8dc8c749..d24a60c89 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -716,7 +716,7 @@ func assertHome( WaitUntil: waitUntil, Timeout: 30 * time.Second, }) - _, err := p.Goto(tb.URL("/home"), opts) + _, err := p.Goto(tb.url("/home"), opts) if err == nil { resolved = true } else { diff --git a/tests/network_manager_test.go b/tests/network_manager_test.go index c43e9b0e3..9dba0ac3e 100644 --- a/tests/network_manager_test.go +++ b/tests/network_manager_test.go @@ -44,7 +44,7 @@ func TestBlockHostnames(t *testing.T) { require.Nil(t, res) tb.logCache.assertContains(t, "was interrupted: hostname host.test is in a blocked pattern") - res, err = p.Goto(tb.URL("/get"), nil) + res, err = p.Goto(tb.url("/get"), nil) require.NoError(t, err) assert.NotNil(t, res) } @@ -63,7 +63,7 @@ func TestBlockIPs(t *testing.T) { tb.logCache.assertContains(t, `was interrupted: IP 10.0.0.1 is in a blacklisted range "10.0.0.0/8"`) // Ensure other requests go through - res, err = p.Goto(tb.URL("/get"), nil) + res, err = p.Goto(tb.url("/get"), nil) require.NoError(t, err) assert.NotNil(t, res) } @@ -97,7 +97,7 @@ func TestBasicAuth(t *testing.T) { }{ WaitUntil: "load", }) - url := browser.URL(fmt.Sprintf("/basic-auth/%s/%s", validUser, validPassword)) + url := browser.url(fmt.Sprintf("/basic-auth/%s/%s", validUser, validPassword)) res, err := p.Goto(url, opts) require.NoError(t, err) diff --git a/tests/page_test.go b/tests/page_test.go index 8a377b9e6..0ed99ed04 100644 --- a/tests/page_test.go +++ b/tests/page_test.go @@ -430,7 +430,7 @@ func TestPageSetExtraHTTPHeaders(t *testing.T) { } p.SetExtraHTTPHeaders(headers) - resp, err := p.Goto(b.URL("/get"), nil) + resp, err := p.Goto(b.url("/get"), nil) require.NoError(t, err) require.NotNil(t, resp) @@ -640,8 +640,8 @@ func TestPageWaitForLoadState(t *testing.T) { func TestPageWaitForNavigationErrOnCtxDone(t *testing.T) { b := newTestBrowser(t) p := b.NewPage(nil) - go b.Cancel() - <-b.Context().Done() + go b.cancelContext() + <-b.context().Done() _, err := p.WaitForNavigation(nil) require.ErrorContains(t, err, "canceled") } @@ -666,7 +666,7 @@ func TestPageURL(t *testing.T) { p := b.NewPage(nil) assert.Equal(t, "about:blank", p.URL()) - resp, err := p.Goto(b.URL("/get"), nil) + resp, err := p.Goto(b.url("/get"), nil) require.NoError(t, err) require.NotNil(t, resp) assert.Regexp(t, "http://.*/get", p.URL()) diff --git a/tests/test_browser.go b/tests/test_browser.go index 1ad82443f..bb89447e7 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -23,6 +23,8 @@ import ( k6metrics "go.k6.io/k6/metrics" ) +const testBrowserStaticDir = "static" + // testBrowser is a test testBrowser for integration testing. type testBrowser struct { t testing.TB @@ -112,10 +114,8 @@ func (b *testBrowser) NewPage(opts goja.Value) *common.Page { return pp } -const testBrowserStaticDir = "static" - -// URL returns the listening HTTP test server's URL combined with the given path. -func (b *testBrowser) URL(path string) string { +// url returns the listening HTTP test server's url combined with the given path. +func (b *testBrowser) url(path string) string { b.t.Helper() if b.http == nil { @@ -127,19 +127,14 @@ func (b *testBrowser) URL(path string) string { // staticURL is a helper for URL("/`testBrowserStaticDir`/"+ path). func (b *testBrowser) staticURL(path string) string { b.t.Helper() - - return b.URL("/" + testBrowserStaticDir + "/" + path) + return b.url("/" + testBrowserStaticDir + "/" + path) } -// Context returns the testBrowser context. -func (b *testBrowser) Context() context.Context { - return b.ctx -} +// 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() -} +// cancelContext cancels the testBrowser context. +func (b *testBrowser) cancelContext() { b.cancel() } // runtime returns a VU runtime. func (b *testBrowser) runtime() *goja.Runtime { return b.vu.Runtime() } From 7486f9fbbe9aad0c608c0a1b8d3fa93c246fa729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 17:28:42 +0300 Subject: [PATCH 30/46] Rename MoveToContext to RestoreVUState --- common/network_manager_test.go | 4 ++-- k6ext/k6test/vu.go | 6 +++--- tests/browser_test.go | 2 +- tests/browser_type_test.go | 4 ++-- tests/test_browser.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/network_manager_test.go b/common/network_manager_test.go index b10212022..0f0c42862 100644 --- a/common/network_manager_test.go +++ b/common/network_manager_test.go @@ -61,7 +61,7 @@ func newTestNetworkManager(t *testing.T, k6opts k6lib.Options) (*NetworkManager, }) vu := k6test.NewVU(t) - vu.MoveToVUContext() + vu.RestoreVUState() st := vu.State() st.Options = k6opts logger := log.New(st.Logger, "") @@ -268,7 +268,7 @@ func TestNetworkManagerEmitRequestResponseMetricsTimingSkew(t *testing.T) { vu = k6test.NewVU(t) nm = &NetworkManager{ctx: vu.Context(), vu: vu, customMetrics: k6m} ) - vu.MoveToVUContext() + vu.RestoreVUState() req, err := NewRequest(vu.Context(), NewRequestParams{ event: &network.EventRequestWillBeSent{ diff --git a/k6ext/k6test/vu.go b/k6ext/k6test/vu.go index f1bcc0014..00b80c760 100644 --- a/k6ext/k6test/vu.go +++ b/k6ext/k6test/vu.go @@ -33,9 +33,9 @@ type VU struct { // ToGojaValue is a convenience method for converting any value to a goja value. func (v *VU) ToGojaValue(i any) goja.Value { return v.Runtime().ToValue(i) } -// MoveToVUContext moves the VU to VU context, adding a predefined k6 lib State and nilling the InitEnv -// to simulate how that is done in the real k6. -func (v *VU) MoveToVUContext() { +// RestoreVUState restores the state field of the VU and nilling the InitEnv field. +// This is done to simulate how k6 operates. +func (v *VU) RestoreVUState() { v.VU.StateField = v.toBeState v.VU.InitEnvField = nil } diff --git a/tests/browser_test.go b/tests/browser_test.go index 885596f57..ea415e2bd 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -150,7 +150,7 @@ func TestBrowserCrashErr(t *testing.T) { jsMod, ok := mod.Exports().Default.(*browser.JSModule) require.Truef(t, ok, "unexpected default mod export type %T", mod.Exports().Default) - vu.MoveToVUContext() + vu.RestoreVUState() rt := vu.Runtime() require.NoError(t, rt.Set("browser", jsMod.Browser)) diff --git a/tests/browser_type_test.go b/tests/browser_type_test.go index 156a1b6e2..a9870ef10 100644 --- a/tests/browser_type_test.go +++ b/tests/browser_type_test.go @@ -18,7 +18,7 @@ func TestBrowserTypeConnect(t *testing.T) { tb := newTestBrowser(t) vu := k6test.NewVU(t) bt := chromium.NewBrowserType(vu) - vu.MoveToVUContext() + vu.RestoreVUState() b, err := bt.Connect(context.Background(), tb.wsURL) require.NoError(t, err) @@ -42,7 +42,7 @@ func TestBrowserTypeLaunchToConnect(t *testing.T) { jsMod, ok := mod.Exports().Default.(*browser.JSModule) require.Truef(t, ok, "unexpected default mod export type %T", mod.Exports().Default) - vu.MoveToVUContext() + vu.RestoreVUState() rt := vu.Runtime() require.NoError(t, rt.Set("browser", jsMod.Browser)) diff --git a/tests/test_browser.go b/tests/test_browser.go index bb89447e7..b279ec529 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -354,7 +354,7 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( bt := chromium.NewBrowserType(vu) // Delete the pre-init stage data. - vu.MoveToVUContext() + vu.RestoreVUState() return bt, vu, cancel } From 930640f2131258a9f9e849ce959bfa7be46e34fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:03:10 +0300 Subject: [PATCH 31/46] Add testBrowserOptions.apply --- tests/test_browser.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index b279ec529..6b2b4286c 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -237,6 +237,7 @@ func (b *testBrowser) withFileServer() *testBrowser { return b.withHandler(path, http.StripPrefix(path, fs).ServeHTTP) } +// testBrowserOptions is a helper for creating testBrowser options. type testBrowserOptions struct { fileServer bool logCache bool @@ -246,6 +247,8 @@ type testBrowserOptions struct { lookupFunc env.LookupFunc } +// newTestBrowserOptions creates a new testBrowserOptions with the given options. +// call apply to reapply the options. func newTestBrowserOptions(opts ...func(*testBrowserOptions)) *testBrowserOptions { // default lookup function is env.Lookup so that we can // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. @@ -253,11 +256,16 @@ func newTestBrowserOptions(opts ...func(*testBrowserOptions)) *testBrowserOption samples: make(chan k6metrics.SampleContainer, 1000), lookupFunc: env.Lookup, } + tbo.apply(opts...) + + return tbo +} + +// apply applies the given options to the testBrowserOptions. +func (tbo *testBrowserOptions) apply(opts ...func(*testBrowserOptions)) { for _, opt := range opts { opt(tbo) } - - return tbo } // withEnvLookup sets the lookup function for environment variables. From 037049c789ad3503bf1711f5f064dd5bf8669d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:05:35 +0300 Subject: [PATCH 32/46] Reorder testBrowser fields --- tests/test_browser.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 6b2b4286c..2d69c084a 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -29,19 +29,20 @@ const testBrowserStaticDir = "static" type testBrowser struct { t testing.TB - ctx context.Context - http *k6httpmultibin.HTTPMultiBin - vu *k6test.VU - logCache *logCache - - pid int // the browser process ID - wsURL string + ctx context.Context + cancel context.CancelFunc + vu *k6test.VU browserType *chromium.BrowserType + pid int // the browser process ID + wsURL string *common.Browser - cancel context.CancelFunc + // http is set by the withHTTPServer option. + http *k6httpmultibin.HTTPMultiBin + // logCache is set by the withLogCache option. + logCache *logCache } // newTestBrowser configures and launches a new chrome browser. From 7be58e864d4c5076eef29e0d1a2bb2852e3e4c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Wed, 14 Jun 2023 13:54:35 +0300 Subject: [PATCH 33/46] Add testBrowser to testBrowserOptions This will be needed when options need to set testBrowser themselves. Instead of doing all the work in newTestBrowser and complicating it. --- tests/test_browser.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 2d69c084a..16bb4062e 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,7 +60,7 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows tb.Helper() tbr := &testBrowser{t: tb} - tbopts := newTestBrowserOptions(opts...) + tbopts := newTestBrowserOptions(tbr, opts...) tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbopts) tb.Cleanup(tbr.cancel) @@ -240,6 +240,10 @@ func (b *testBrowser) withFileServer() *testBrowser { // testBrowserOptions is a helper for creating testBrowser options. type testBrowserOptions struct { + // testBrowser field provides access to the testBrowser instance for + // the options to modify it. + testBrowser *testBrowser + fileServer bool logCache bool httpMultiBin bool @@ -250,12 +254,13 @@ type testBrowserOptions struct { // newTestBrowserOptions creates a new testBrowserOptions with the given options. // call apply to reapply the options. -func newTestBrowserOptions(opts ...func(*testBrowserOptions)) *testBrowserOptions { +func newTestBrowserOptions(tb *testBrowser, opts ...func(*testBrowserOptions)) *testBrowserOptions { // default lookup function is env.Lookup so that we can // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. tbo := &testBrowserOptions{ - samples: make(chan k6metrics.SampleContainer, 1000), - lookupFunc: env.Lookup, + testBrowser: tb, + samples: make(chan k6metrics.SampleContainer, 1000), + lookupFunc: env.Lookup, } tbo.apply(opts...) From b0547d273e23cbf3498fc7b2c1092d1353590fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:10:57 +0300 Subject: [PATCH 34/46] Add testBrowser.isBrowserInitialized This will let us the testBrowser's initialization stage. Since it first makes a test VU and then uses it to initialize a browser type, some options (i.e. withLogCache) can only be used after the initialization. However, some others (i.e. withSamples) can only be used before the initialization. This fields lets us use both kind of options and let them init by detecting the init stage. --- tests/test_browser.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 16bb4062e..dc2e40158 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -60,9 +60,11 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows tb.Helper() tbr := &testBrowser{t: tb} - tbopts := newTestBrowserOptions(tbr, opts...) + tbopts := newTestBrowserOptions(tbr, opts...) // apply pre-init stage options. tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbopts) tb.Cleanup(tbr.cancel) + tbopts.isBrowserTypeInitialized = true // some option require the browser type to be initialized. + tbopts.apply(opts...) // apply post-init stage options. if tbopts.logCache { tbr.logCache = attachLogCache(tb, tbr.vu.StateField.Logger) @@ -243,6 +245,11 @@ type testBrowserOptions struct { // testBrowser field provides access to the testBrowser instance for // the options to modify it. testBrowser *testBrowser + // isBrowserTypeInitialized is true if the + // browser type has been initialized with a VU. + isBrowserTypeInitialized bool + + // options fileServer bool logCache bool @@ -346,6 +353,7 @@ func withSkipClose() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.skipClose = true } } +// newBrowserTypeWithVU creates a new browser type with a VU. func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( _ *chromium.BrowserType, _ *k6test.VU, @@ -353,7 +361,6 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( ) { tb.Helper() - // Prepare the VU. vu := k6test.NewVU(tb, k6test.WithSamples(opts.samples)) mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) @@ -367,7 +374,6 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( vu.InitEnvField.LookupEnv = opts.lookupFunc bt := chromium.NewBrowserType(vu) - // Delete the pre-init stage data. vu.RestoreVUState() return bt, vu, cancel From 50ebe93b1ca0662c35fb3ab8b1d675019b7033f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:29:43 +0300 Subject: [PATCH 35/46] Refactor testBrowser.withLogCache The option is now responsible for setting the log cache. This has the benefit of reducing the pollution in the newTestBrowser function and separates the responsibilities. We also don't need to keep track of whether to set the log cache. The option does that. --- tests/test_browser.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index dc2e40158..f1f91325d 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -66,9 +66,6 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows tbopts.isBrowserTypeInitialized = true // some option require the browser type to be initialized. tbopts.apply(opts...) // apply post-init stage options. - if tbopts.logCache { - tbr.logCache = attachLogCache(tb, tbr.vu.StateField.Logger) - } if tbopts.httpMultiBin { tbr.http = k6httpmultibin.NewHTTPMultiBin(tb) tbr.vu.StateField.TLSConfig = tbr.http.TLSClientConfig @@ -252,7 +249,6 @@ type testBrowserOptions struct { // options fileServer bool - logCache bool httpMultiBin bool samples chan k6metrics.SampleContainer skipClose bool @@ -333,7 +329,13 @@ func withHTTPServer() func(tb *testBrowserOptions) { // // b := TestBrowser(t, withLogCache()) func withLogCache() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.logCache = true } + return func(opts *testBrowserOptions) { + if !opts.isBrowserTypeInitialized { + return + } + tb := opts.testBrowser + tb.logCache = attachLogCache(tb.t, tb.vu.StateField.Logger) + } } // withSamples is used to indicate we want to use a bidirectional channel From 4bc306e60df1bfa03a67341cdf9d74f0db68d1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:36:03 +0300 Subject: [PATCH 36/46] Refactor testBrowser.withHTTPServer --- tests/test_browser.go | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index f1f91325d..e03651fe4 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -66,11 +66,6 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows tbopts.isBrowserTypeInitialized = true // some option require the browser type to be initialized. tbopts.apply(opts...) // apply post-init stage options. - if tbopts.httpMultiBin { - tbr.http = k6httpmultibin.NewHTTPMultiBin(tb) - tbr.vu.StateField.TLSConfig = tbr.http.TLSClientConfig - tbr.vu.StateField.Transport = tbr.http.HTTPTransport - } if tbopts.fileServer { tbr = tbr.withFileServer() } @@ -248,11 +243,10 @@ type testBrowserOptions struct { // options - fileServer bool - httpMultiBin bool - samples chan k6metrics.SampleContainer - skipClose bool - lookupFunc env.LookupFunc + fileServer bool + samples chan k6metrics.SampleContainer + skipClose bool + lookupFunc env.LookupFunc } // newTestBrowserOptions creates a new testBrowserOptions with the given options. @@ -295,9 +289,14 @@ func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { // // b := TestBrowser(t, withFileServer()) func withFileServer() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { - tb.httpMultiBin = true - tb.fileServer = true + return func(opts *testBrowserOptions) { + tb := opts.testBrowser + // file server needs HTTP server. + if tb.http == nil { + apply := withHTTPServer() + apply(opts) + } + opts.fileServer = true } } @@ -320,7 +319,19 @@ func (b *testBrowser) withHandler(pattern string, handler http.HandlerFunc) *tes // // b := TestBrowser(t, withHTTPServer()) func withHTTPServer() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.httpMultiBin = true } + return func(opts *testBrowserOptions) { + if !opts.isBrowserTypeInitialized { + return + } + tb := opts.testBrowser + if opts.testBrowser.http != nil { + // already initialized. + return + } + tb.http = k6httpmultibin.NewHTTPMultiBin(tb.t) + tb.vu.StateField.TLSConfig = tb.http.TLSClientConfig + tb.vu.StateField.Transport = tb.http.HTTPTransport + } } // withLogCache enables the log cache. From 2c30d9375e97abeb1cfae35bdda5a41dda493b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:44:29 +0300 Subject: [PATCH 37/46] Refactor testBrowser.withFileServer --- tests/test_browser.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index e03651fe4..5375a964e 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -66,10 +66,6 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows tbopts.isBrowserTypeInitialized = true // some option require the browser type to be initialized. tbopts.apply(opts...) // apply post-init stage options. - if tbopts.fileServer { - tbr = tbr.withFileServer() - } - b, pid, err := tbr.browserType.Launch(tbr.vu.Context()) if err != nil { tb.Fatalf("testBrowser: %v", err) @@ -243,7 +239,6 @@ type testBrowserOptions struct { // options - fileServer bool samples chan k6metrics.SampleContainer skipClose bool lookupFunc env.LookupFunc @@ -290,13 +285,16 @@ func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { // b := TestBrowser(t, withFileServer()) func withFileServer() func(tb *testBrowserOptions) { return func(opts *testBrowserOptions) { + if !opts.isBrowserTypeInitialized { + return + } tb := opts.testBrowser - // file server needs HTTP server. if tb.http == nil { + // file server needs HTTP server. apply := withHTTPServer() apply(opts) } - opts.fileServer = true + opts.testBrowser = tb.withFileServer() } } From dce89e46c581856badbb221e65f28649a5660ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:47:08 +0300 Subject: [PATCH 38/46] Reorder testBrowserOptions for organization --- tests/test_browser.go | 76 +++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 5375a964e..6c3afe24a 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -210,22 +210,30 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e } } -// withFileServer serves a file server using the HTTP test server that is -// accessible via `testBrowserStaticDir` prefix. -// -// This method is for enabling the static file server after starting a test -// browser. For early starting the file server see withFileServer function. -func (b *testBrowser) withFileServer() *testBrowser { - b.t.Helper() +// newBrowserTypeWithVU creates a new browser type with a VU. +func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( + _ *chromium.BrowserType, + _ *k6test.VU, + cancel func(), +) { + tb.Helper() - const ( - slash = string(os.PathSeparator) - path = slash + testBrowserStaticDir + slash + vu := k6test.NewVU(tb, k6test.WithSamples(opts.samples)) + mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) + require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) + require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) + metricsCtx := k6ext.WithCustomMetrics( + vu.Context(), + k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), ) + ctx, cancel := context.WithCancel(metricsCtx) + vu.CtxField = ctx + vu.InitEnvField.LookupEnv = opts.lookupFunc - fs := http.FileServer(http.Dir(testBrowserStaticDir)) + bt := chromium.NewBrowserType(vu) + vu.RestoreVUState() - return b.withHandler(path, http.StripPrefix(path, fs).ServeHTTP) + return bt, vu, cancel } // testBrowserOptions is a helper for creating testBrowser options. @@ -298,6 +306,24 @@ func withFileServer() func(tb *testBrowserOptions) { } } +// withFileServer serves a file server using the HTTP test server that is +// accessible via `testBrowserStaticDir` prefix. +// +// This method is for enabling the static file server after starting a test +// browser. For early starting the file server see withFileServer function. +func (b *testBrowser) withFileServer() *testBrowser { + b.t.Helper() + + const ( + slash = string(os.PathSeparator) + path = slash + testBrowserStaticDir + slash + ) + + fs := http.FileServer(http.Dir(testBrowserStaticDir)) + + return b.withHandler(path, http.StripPrefix(path, fs).ServeHTTP) +} + // withHandler adds the given handler to the HTTP test server and makes it // accessible with the given pattern. func (b *testBrowser) withHandler(pattern string, handler http.HandlerFunc) *testBrowser { @@ -363,29 +389,3 @@ func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) func withSkipClose() func(tb *testBrowserOptions) { return func(tb *testBrowserOptions) { tb.skipClose = true } } - -// newBrowserTypeWithVU creates a new browser type with a VU. -func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( - _ *chromium.BrowserType, - _ *k6test.VU, - cancel func(), -) { - tb.Helper() - - vu := k6test.NewVU(tb, k6test.WithSamples(opts.samples)) - mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) - require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) - require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) - metricsCtx := k6ext.WithCustomMetrics( - vu.Context(), - k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), - ) - ctx, cancel := context.WithCancel(metricsCtx) - vu.CtxField = ctx - vu.InitEnvField.LookupEnv = opts.lookupFunc - - bt := chromium.NewBrowserType(vu) - vu.RestoreVUState() - - return bt, vu, cancel -} From 6fe57d0eb7b38d1cd3885377ba0e2202dfca84fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 09:56:39 +0300 Subject: [PATCH 39/46] Move lookUpfunc to testBrowser We're starting to remove the testBrowserOptions. --- tests/test_browser.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 6c3afe24a..eb221cef3 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -43,6 +43,8 @@ type testBrowser struct { http *k6httpmultibin.HTTPMultiBin // logCache is set by the withLogCache option. logCache *logCache + // lookupFunc is set by the withEnvLookup option. + lookupFunc env.LookupFunc } // newTestBrowser configures and launches a new chrome browser. @@ -228,7 +230,7 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( ) ctx, cancel := context.WithCancel(metricsCtx) vu.CtxField = ctx - vu.InitEnvField.LookupEnv = opts.lookupFunc + vu.InitEnvField.LookupEnv = opts.testBrowser.lookupFunc bt := chromium.NewBrowserType(vu) vu.RestoreVUState() @@ -247,9 +249,8 @@ type testBrowserOptions struct { // options - samples chan k6metrics.SampleContainer - skipClose bool - lookupFunc env.LookupFunc + samples chan k6metrics.SampleContainer + skipClose bool } // newTestBrowserOptions creates a new testBrowserOptions with the given options. @@ -260,8 +261,9 @@ func newTestBrowserOptions(tb *testBrowser, opts ...func(*testBrowserOptions)) * tbo := &testBrowserOptions{ testBrowser: tb, samples: make(chan k6metrics.SampleContainer, 1000), - lookupFunc: env.Lookup, } + tb.lookupFunc = env.Lookup + tbo.apply(opts...) return tbo @@ -280,7 +282,9 @@ func (tbo *testBrowserOptions) apply(opts ...func(*testBrowserOptions)) { // // b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0"))) func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.lookupFunc = lookupFunc } + return func(tb *testBrowserOptions) { + tb.testBrowser.lookupFunc = lookupFunc + } } // withFileServer enables the HTTP test server and serves a file server From 4001c14ae00a60ff7389ccc744c4028d512741eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 10:15:01 +0300 Subject: [PATCH 40/46] Move samples to testBrowser --- tests/test_browser.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index eb221cef3..1a5fee3d2 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -45,6 +45,8 @@ type testBrowser struct { logCache *logCache // lookupFunc is set by the withEnvLookup option. lookupFunc env.LookupFunc + // samples is set by the withSamples option. + samples chan k6metrics.SampleContainer } // newTestBrowser configures and launches a new chrome browser. @@ -220,7 +222,7 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( ) { tb.Helper() - vu := k6test.NewVU(tb, k6test.WithSamples(opts.samples)) + vu := k6test.NewVU(tb, k6test.WithSamples(opts.testBrowser.samples)) mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) @@ -249,7 +251,6 @@ type testBrowserOptions struct { // options - samples chan k6metrics.SampleContainer skipClose bool } @@ -260,8 +261,8 @@ func newTestBrowserOptions(tb *testBrowser, opts ...func(*testBrowserOptions)) * // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. tbo := &testBrowserOptions{ testBrowser: tb, - samples: make(chan k6metrics.SampleContainer, 1000), } + tb.samples = make(chan k6metrics.SampleContainer, 1000) tb.lookupFunc = env.Lookup tbo.apply(opts...) @@ -380,7 +381,7 @@ func withLogCache() func(tb *testBrowserOptions) { // withSamples is used to indicate we want to use a bidirectional channel // so that the test can read the metrics being emitted to the channel. func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.samples = sc } + return func(tb *testBrowserOptions) { tb.testBrowser.samples = sc } } // withSkipClose skips calling Browser.Close() in t.Cleanup(). From 3543759653dbc493d3ee85fe7e13af41513b2e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 10:18:43 +0300 Subject: [PATCH 41/46] Move skipClose to testBrowser --- tests/test_browser.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 1a5fee3d2..bb6a078e0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -47,6 +47,8 @@ type testBrowser struct { lookupFunc env.LookupFunc // samples is set by the withSamples option. samples chan k6metrics.SampleContainer + // skipClose is set by the withSkipClose option. + skipClose bool } // newTestBrowser configures and launches a new chrome browser. @@ -86,7 +88,7 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows select { case <-tbr.vu.Context().Done(): default: - if !tbopts.skipClose { + if !tbr.skipClose { cb.Close() } } @@ -248,10 +250,6 @@ type testBrowserOptions struct { // isBrowserTypeInitialized is true if the // browser type has been initialized with a VU. isBrowserTypeInitialized bool - - // options - - skipClose bool } // newTestBrowserOptions creates a new testBrowserOptions with the given options. @@ -392,5 +390,5 @@ func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) // // b := TestBrowser(t, withSkipClose()) func withSkipClose() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.skipClose = true } + return func(tb *testBrowserOptions) { tb.testBrowser.skipClose = true } } From 779e83b788aa23c117bb94f8f4fe849358c6cbed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 10:31:58 +0300 Subject: [PATCH 42/46] Remove testBrowserOptions in favor of testBrowser Simplify and remove testBrowserOptions. Options are now a part of testBrowser itself. --- tests/test_browser.go | 95 ++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index bb6a078e0..fe99b5d98 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -39,6 +39,11 @@ type testBrowser struct { *common.Browser + // isBrowserTypeInitialized is true if the browser type has been + // initialized with a VU. Some options can only be used in the + // post-init stage and require the browser type to be initialized. + isBrowserTypeInitialized bool + // http is set by the withHTTPServer option. http *k6httpmultibin.HTTPMultiBin // logCache is set by the withLogCache option. @@ -62,15 +67,16 @@ type testBrowser struct { // - withLogCache: enables the log cache. // - withSamples: provides a channel to receive the browser metrics. // - withSkipClose: skips closing the browser when the test finishes. -func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrowser { +func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { tb.Helper() tbr := &testBrowser{t: tb} - tbopts := newTestBrowserOptions(tbr, opts...) // apply pre-init stage options. - tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbopts) + tbr.applyDefaultOptions() + tbr.applyOptions(opts...) // apply pre-init stage options. + tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbr) tb.Cleanup(tbr.cancel) - tbopts.isBrowserTypeInitialized = true // some option require the browser type to be initialized. - tbopts.apply(opts...) // apply post-init stage options. + tbr.isBrowserTypeInitialized = true // some option require the browser type to be initialized. + tbr.applyOptions(opts...) // apply post-init stage options. b, pid, err := tbr.browserType.Launch(tbr.vu.Context()) if err != nil { @@ -217,14 +223,14 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e } // newBrowserTypeWithVU creates a new browser type with a VU. -func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( +func newBrowserTypeWithVU(tb testing.TB, tbr *testBrowser) ( _ *chromium.BrowserType, _ *k6test.VU, cancel func(), ) { tb.Helper() - vu := k6test.NewVU(tb, k6test.WithSamples(opts.testBrowser.samples)) + vu := k6test.NewVU(tb, k6test.WithSamples(tbr.samples)) mi, ok := k6http.New().NewModuleInstance(vu).(*k6http.ModuleInstance) require.Truef(tb, ok, "want *k6http.ModuleInstance; got %T", mi) require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default)) @@ -234,7 +240,7 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( ) ctx, cancel := context.WithCancel(metricsCtx) vu.CtxField = ctx - vu.InitEnvField.LookupEnv = opts.testBrowser.lookupFunc + vu.InitEnvField.LookupEnv = tbr.lookupFunc bt := chromium.NewBrowserType(vu) vu.RestoreVUState() @@ -242,36 +248,18 @@ func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) ( return bt, vu, cancel } -// testBrowserOptions is a helper for creating testBrowser options. -type testBrowserOptions struct { - // testBrowser field provides access to the testBrowser instance for - // the options to modify it. - testBrowser *testBrowser - // isBrowserTypeInitialized is true if the - // browser type has been initialized with a VU. - isBrowserTypeInitialized bool -} - -// newTestBrowserOptions creates a new testBrowserOptions with the given options. -// call apply to reapply the options. -func newTestBrowserOptions(tb *testBrowser, opts ...func(*testBrowserOptions)) *testBrowserOptions { +// applyDefaultOptions applies the default options for the testBrowser. +func (b *testBrowser) applyDefaultOptions() { + b.samples = make(chan k6metrics.SampleContainer, 1000) // default lookup function is env.Lookup so that we can // pass the environment variables while testing, i.e.: K6_BROWSER_LOG. - tbo := &testBrowserOptions{ - testBrowser: tb, - } - tb.samples = make(chan k6metrics.SampleContainer, 1000) - tb.lookupFunc = env.Lookup - - tbo.apply(opts...) - - return tbo + b.lookupFunc = env.Lookup } -// apply applies the given options to the testBrowserOptions. -func (tbo *testBrowserOptions) apply(opts ...func(*testBrowserOptions)) { +// applyOptions applies the given options to the testBrowser. +func (b *testBrowser) applyOptions(opts ...func(*testBrowser)) { for _, opt := range opts { - opt(tbo) + opt(b) } } @@ -280,10 +268,8 @@ func (tbo *testBrowserOptions) apply(opts ...func(*testBrowserOptions)) { // example: // // b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0"))) -func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { - return func(tb *testBrowserOptions) { - tb.testBrowser.lookupFunc = lookupFunc - } +func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowser) { + return func(tb *testBrowser) { tb.lookupFunc = lookupFunc } } // withFileServer enables the HTTP test server and serves a file server @@ -294,18 +280,17 @@ func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) { // example: // // b := TestBrowser(t, withFileServer()) -func withFileServer() func(tb *testBrowserOptions) { - return func(opts *testBrowserOptions) { - if !opts.isBrowserTypeInitialized { +func withFileServer() func(*testBrowser) { + return func(tb *testBrowser) { + if !tb.isBrowserTypeInitialized { return } - tb := opts.testBrowser if tb.http == nil { // file server needs HTTP server. apply := withHTTPServer() - apply(opts) + apply(tb) } - opts.testBrowser = tb.withFileServer() + _ = tb.withFileServer() } } @@ -345,13 +330,12 @@ func (b *testBrowser) withHandler(pattern string, handler http.HandlerFunc) *tes // example: // // b := TestBrowser(t, withHTTPServer()) -func withHTTPServer() func(tb *testBrowserOptions) { - return func(opts *testBrowserOptions) { - if !opts.isBrowserTypeInitialized { +func withHTTPServer() func(*testBrowser) { + return func(tb *testBrowser) { + if !tb.isBrowserTypeInitialized { return } - tb := opts.testBrowser - if opts.testBrowser.http != nil { + if tb.http != nil { // already initialized. return } @@ -366,20 +350,19 @@ func withHTTPServer() func(tb *testBrowserOptions) { // example: // // b := TestBrowser(t, withLogCache()) -func withLogCache() func(tb *testBrowserOptions) { - return func(opts *testBrowserOptions) { - if !opts.isBrowserTypeInitialized { +func withLogCache() func(*testBrowser) { + return func(tb *testBrowser) { + if !tb.isBrowserTypeInitialized { return } - tb := opts.testBrowser tb.logCache = attachLogCache(tb.t, tb.vu.StateField.Logger) } } // withSamples is used to indicate we want to use a bidirectional channel // so that the test can read the metrics being emitted to the channel. -func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.testBrowser.samples = sc } +func withSamples(sc chan k6metrics.SampleContainer) func(*testBrowser) { + return func(tb *testBrowser) { tb.samples = sc } } // withSkipClose skips calling Browser.Close() in t.Cleanup(). @@ -389,6 +372,6 @@ func withSamples(sc chan k6metrics.SampleContainer) func(tb *testBrowserOptions) // example: // // b := TestBrowser(t, withSkipClose()) -func withSkipClose() func(tb *testBrowserOptions) { - return func(tb *testBrowserOptions) { tb.testBrowser.skipClose = true } +func withSkipClose() func(*testBrowser) { + return func(tb *testBrowser) { tb.skipClose = true } } From 0fc72a91b8ebaeeea92b624c48c2ea6b2b37d7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 15 Jun 2023 10:34:00 +0300 Subject: [PATCH 43/46] Reorder testBrowser options near testBrowser This makes it easier to see the initialization of the test browser as they're related. --- tests/test_browser.go | 238 +++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index fe99b5d98..d698dd837 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -103,125 +103,6 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { return tbr } -// NewPage is a wrapper around Browser.NewPage that fails the test if an -// error occurs. Added this helper to avoid boilerplate code in tests. -func (b *testBrowser) NewPage(opts goja.Value) *common.Page { - b.t.Helper() - - p, err := b.Browser.NewPage(opts) - require.NoError(b.t, err) - - pp, ok := p.(*common.Page) - require.Truef(b.t, ok, "want *common.Page, got %T", p) - - return pp -} - -// url returns the listening HTTP test server's url combined with the given path. -func (b *testBrowser) url(path string) string { - b.t.Helper() - - if b.http == nil { - b.t.Fatalf("You should enable HTTP test server, see: withHTTPServer option") - } - return b.http.ServerHTTP.URL + path -} - -// staticURL is a helper for URL("/`testBrowserStaticDir`/"+ path). -func (b *testBrowser) staticURL(path string) string { - b.t.Helper() - return b.url("/" + testBrowserStaticDir + "/" + path) -} - -// context returns the testBrowser context. -func (b *testBrowser) context() context.Context { return b.ctx } - -// cancelContext cancels the testBrowser context. -func (b *testBrowser) cancelContext() { b.cancel() } - -// runtime returns a VU runtime. -func (b *testBrowser) runtime() *goja.Runtime { return b.vu.Runtime() } - -// toGojaValue converts a value to goja value. -func (b *testBrowser) toGojaValue(i any) goja.Value { return b.runtime().ToValue(i) } - -// asGojaValue asserts that v is a goja value and returns v as a goja.value. -func (b *testBrowser) asGojaValue(v any) goja.Value { - b.t.Helper() - gv, ok := v.(goja.Value) - require.Truef(b.t, ok, "want goja.Value; got %T", v) - return gv -} - -// asGojaBool asserts that v is a boolean goja value and returns v as a boolean. -func (b *testBrowser) asGojaBool(v any) bool { - b.t.Helper() - gv := b.asGojaValue(v) - require.IsType(b.t, b.toGojaValue(true), gv) - return gv.ToBoolean() -} - -// runJavaScript in the goja runtime. -func (b *testBrowser) runJavaScript(s string, args ...any) (goja.Value, error) { - b.t.Helper() - v, err := b.runtime().RunString(fmt.Sprintf(s, args...)) - if err != nil { - return nil, fmt.Errorf("while running %q(%v): %w", s, args, err) - } - return v, nil -} - -// Run the given functions in parallel and waits for them to finish. -func (b *testBrowser) run(ctx context.Context, fs ...func() error) error { - b.t.Helper() - - g, ctx := errgroup.WithContext(ctx) - for _, f := range fs { - f := f - g.Go(func() error { - errc := make(chan error, 1) - go func() { errc <- f() }() - select { - case err := <-errc: - return err - case <-ctx.Done(): - if err := ctx.Err(); err != nil { - return fmt.Errorf("while running %T: %w", f, err) - } - } - - return nil - }) - } - - if err := g.Wait(); err != nil { - return fmt.Errorf("while waiting for %T: %w", fs, err) - } - - return nil -} - -// awaitWithTimeout is the same as await but takes a timeout and times out the function after the time runs out. -func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) error { - b.t.Helper() - errC := make(chan error) - go func() { - defer close(errC) - errC <- fn() - }() - - // use timer instead of time.After to not leak time.After for the duration of the timeout - t := time.NewTimer(timeout) - defer t.Stop() - - select { - case err := <-errC: - return err - case <-t.C: - return fmt.Errorf("test timed out after %s", timeout) - } -} - // newBrowserTypeWithVU creates a new browser type with a VU. func newBrowserTypeWithVU(tb testing.TB, tbr *testBrowser) ( _ *chromium.BrowserType, @@ -375,3 +256,122 @@ func withSamples(sc chan k6metrics.SampleContainer) func(*testBrowser) { func withSkipClose() func(*testBrowser) { return func(tb *testBrowser) { tb.skipClose = true } } + +// NewPage is a wrapper around Browser.NewPage that fails the test if an +// error occurs. Added this helper to avoid boilerplate code in tests. +func (b *testBrowser) NewPage(opts goja.Value) *common.Page { + b.t.Helper() + + p, err := b.Browser.NewPage(opts) + require.NoError(b.t, err) + + pp, ok := p.(*common.Page) + require.Truef(b.t, ok, "want *common.Page, got %T", p) + + return pp +} + +// url returns the listening HTTP test server's url combined with the given path. +func (b *testBrowser) url(path string) string { + b.t.Helper() + + if b.http == nil { + b.t.Fatalf("You should enable HTTP test server, see: withHTTPServer option") + } + return b.http.ServerHTTP.URL + path +} + +// staticURL is a helper for URL("/`testBrowserStaticDir`/"+ path). +func (b *testBrowser) staticURL(path string) string { + b.t.Helper() + return b.url("/" + testBrowserStaticDir + "/" + path) +} + +// context returns the testBrowser context. +func (b *testBrowser) context() context.Context { return b.ctx } + +// cancelContext cancels the testBrowser context. +func (b *testBrowser) cancelContext() { b.cancel() } + +// runtime returns a VU runtime. +func (b *testBrowser) runtime() *goja.Runtime { return b.vu.Runtime() } + +// toGojaValue converts a value to goja value. +func (b *testBrowser) toGojaValue(i any) goja.Value { return b.runtime().ToValue(i) } + +// asGojaValue asserts that v is a goja value and returns v as a goja.value. +func (b *testBrowser) asGojaValue(v any) goja.Value { + b.t.Helper() + gv, ok := v.(goja.Value) + require.Truef(b.t, ok, "want goja.Value; got %T", v) + return gv +} + +// asGojaBool asserts that v is a boolean goja value and returns v as a boolean. +func (b *testBrowser) asGojaBool(v any) bool { + b.t.Helper() + gv := b.asGojaValue(v) + require.IsType(b.t, b.toGojaValue(true), gv) + return gv.ToBoolean() +} + +// runJavaScript in the goja runtime. +func (b *testBrowser) runJavaScript(s string, args ...any) (goja.Value, error) { + b.t.Helper() + v, err := b.runtime().RunString(fmt.Sprintf(s, args...)) + if err != nil { + return nil, fmt.Errorf("while running %q(%v): %w", s, args, err) + } + return v, nil +} + +// Run the given functions in parallel and waits for them to finish. +func (b *testBrowser) run(ctx context.Context, fs ...func() error) error { + b.t.Helper() + + g, ctx := errgroup.WithContext(ctx) + for _, f := range fs { + f := f + g.Go(func() error { + errc := make(chan error, 1) + go func() { errc <- f() }() + select { + case err := <-errc: + return err + case <-ctx.Done(): + if err := ctx.Err(); err != nil { + return fmt.Errorf("while running %T: %w", f, err) + } + } + + return nil + }) + } + + if err := g.Wait(); err != nil { + return fmt.Errorf("while waiting for %T: %w", fs, err) + } + + return nil +} + +// awaitWithTimeout is the same as await but takes a timeout and times out the function after the time runs out. +func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) error { + b.t.Helper() + errC := make(chan error) + go func() { + defer close(errC) + errC <- fn() + }() + + // use timer instead of time.After to not leak time.After for the duration of the timeout + t := time.NewTimer(timeout) + defer t.Stop() + + select { + case err := <-errC: + return err + case <-t.C: + return fmt.Errorf("test timed out after %s", timeout) + } +} From a98cfff1ca9a9e63354dcd4d1262d3fbabb0af7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Fri, 16 Jun 2023 08:14:51 +0300 Subject: [PATCH 44/46] Move browser type init out of newBrowserTypeWithVU Resolves: https://github.com/grafana/xk6-browser/pull/935#discussion_r1230604475 --- tests/test_browser.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index d698dd837..4e4cd12c0 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -73,8 +73,10 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { tbr := &testBrowser{t: tb} tbr.applyDefaultOptions() tbr.applyOptions(opts...) // apply pre-init stage options. - tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbr) + tbr.vu, tbr.cancel = newTestBrowserVU(tb, tbr) tb.Cleanup(tbr.cancel) + tbr.browserType = chromium.NewBrowserType(tbr.vu) + tbr.vu.RestoreVUState() tbr.isBrowserTypeInitialized = true // some option require the browser type to be initialized. tbr.applyOptions(opts...) // apply post-init stage options. @@ -103,12 +105,10 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { return tbr } -// newBrowserTypeWithVU creates a new browser type with a VU. -func newBrowserTypeWithVU(tb testing.TB, tbr *testBrowser) ( - _ *chromium.BrowserType, - _ *k6test.VU, - cancel func(), -) { +// newTestBrowserVU initializes a new VU for browser testing. +// It returns the VU and a cancel function to stop the VU. +// VU contains the context with the custom metrics registry. +func newTestBrowserVU(tb testing.TB, tbr *testBrowser) (_ *k6test.VU, cancel func()) { tb.Helper() vu := k6test.NewVU(tb, k6test.WithSamples(tbr.samples)) @@ -123,10 +123,7 @@ func newBrowserTypeWithVU(tb testing.TB, tbr *testBrowser) ( vu.CtxField = ctx vu.InitEnvField.LookupEnv = tbr.lookupFunc - bt := chromium.NewBrowserType(vu) - vu.RestoreVUState() - - return bt, vu, cancel + return vu, cancel } // applyDefaultOptions applies the default options for the testBrowser. From f5dd191ca9baeba84b0ead298bb6db8971e65c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Fri, 16 Jun 2023 08:18:39 +0300 Subject: [PATCH 45/46] Move VU cancelation into newTestBrowserVU Resolves: https://github.com/grafana/xk6-browser/pull/935#discussion_r1231108963 --- tests/test_browser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_browser.go b/tests/test_browser.go index 4e4cd12c0..5bb6de13c 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -74,7 +74,6 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { tbr.applyDefaultOptions() tbr.applyOptions(opts...) // apply pre-init stage options. tbr.vu, tbr.cancel = newTestBrowserVU(tb, tbr) - tb.Cleanup(tbr.cancel) tbr.browserType = chromium.NewBrowserType(tbr.vu) tbr.vu.RestoreVUState() tbr.isBrowserTypeInitialized = true // some option require the browser type to be initialized. @@ -120,6 +119,7 @@ func newTestBrowserVU(tb testing.TB, tbr *testBrowser) (_ *k6test.VU, cancel fun k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()), ) ctx, cancel := context.WithCancel(metricsCtx) + tb.Cleanup(cancel) vu.CtxField = ctx vu.InitEnvField.LookupEnv = tbr.lookupFunc From 48d9d7fc1879b818e089a0b0a63fbca674c212df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Fri, 16 Jun 2023 15:19:51 +0300 Subject: [PATCH 46/46] Rename RestoreVUState to ActivateVU --- common/network_manager_test.go | 4 ++-- k6ext/k6test/vu.go | 7 ++++--- tests/browser_test.go | 2 +- tests/browser_type_test.go | 4 ++-- tests/test_browser.go | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/common/network_manager_test.go b/common/network_manager_test.go index 0f0c42862..b6eb7a722 100644 --- a/common/network_manager_test.go +++ b/common/network_manager_test.go @@ -61,7 +61,7 @@ func newTestNetworkManager(t *testing.T, k6opts k6lib.Options) (*NetworkManager, }) vu := k6test.NewVU(t) - vu.RestoreVUState() + vu.ActivateVU() st := vu.State() st.Options = k6opts logger := log.New(st.Logger, "") @@ -268,7 +268,7 @@ func TestNetworkManagerEmitRequestResponseMetricsTimingSkew(t *testing.T) { vu = k6test.NewVU(t) nm = &NetworkManager{ctx: vu.Context(), vu: vu, customMetrics: k6m} ) - vu.RestoreVUState() + vu.ActivateVU() req, err := NewRequest(vu.Context(), NewRequestParams{ event: &network.EventRequestWillBeSent{ diff --git a/k6ext/k6test/vu.go b/k6ext/k6test/vu.go index 00b80c760..33622a2c3 100644 --- a/k6ext/k6test/vu.go +++ b/k6ext/k6test/vu.go @@ -33,9 +33,10 @@ type VU struct { // ToGojaValue is a convenience method for converting any value to a goja value. func (v *VU) ToGojaValue(i any) goja.Value { return v.Runtime().ToValue(i) } -// RestoreVUState restores the state field of the VU and nilling the InitEnv field. -// This is done to simulate how k6 operates. -func (v *VU) RestoreVUState() { +// ActivateVU mimicks activation of the VU as in k6. +// It transitions the VU from the init stage to the execution stage by +// setting the VU's state to the state that was passed to NewVU. +func (v *VU) ActivateVU() { v.VU.StateField = v.toBeState v.VU.InitEnvField = nil } diff --git a/tests/browser_test.go b/tests/browser_test.go index ea415e2bd..3b5f5bf9a 100644 --- a/tests/browser_test.go +++ b/tests/browser_test.go @@ -150,7 +150,7 @@ func TestBrowserCrashErr(t *testing.T) { jsMod, ok := mod.Exports().Default.(*browser.JSModule) require.Truef(t, ok, "unexpected default mod export type %T", mod.Exports().Default) - vu.RestoreVUState() + vu.ActivateVU() rt := vu.Runtime() require.NoError(t, rt.Set("browser", jsMod.Browser)) diff --git a/tests/browser_type_test.go b/tests/browser_type_test.go index a9870ef10..260ab4c34 100644 --- a/tests/browser_type_test.go +++ b/tests/browser_type_test.go @@ -18,7 +18,7 @@ func TestBrowserTypeConnect(t *testing.T) { tb := newTestBrowser(t) vu := k6test.NewVU(t) bt := chromium.NewBrowserType(vu) - vu.RestoreVUState() + vu.ActivateVU() b, err := bt.Connect(context.Background(), tb.wsURL) require.NoError(t, err) @@ -42,7 +42,7 @@ func TestBrowserTypeLaunchToConnect(t *testing.T) { jsMod, ok := mod.Exports().Default.(*browser.JSModule) require.Truef(t, ok, "unexpected default mod export type %T", mod.Exports().Default) - vu.RestoreVUState() + vu.ActivateVU() rt := vu.Runtime() require.NoError(t, rt.Set("browser", jsMod.Browser)) diff --git a/tests/test_browser.go b/tests/test_browser.go index 5bb6de13c..ab8dfa40e 100644 --- a/tests/test_browser.go +++ b/tests/test_browser.go @@ -75,7 +75,7 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowser)) *testBrowser { tbr.applyOptions(opts...) // apply pre-init stage options. tbr.vu, tbr.cancel = newTestBrowserVU(tb, tbr) tbr.browserType = chromium.NewBrowserType(tbr.vu) - tbr.vu.RestoreVUState() + tbr.vu.ActivateVU() tbr.isBrowserTypeInitialized = true // some option require the browser type to be initialized. tbr.applyOptions(opts...) // apply post-init stage options.