Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor integration test browser #935

Merged
merged 46 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
290b912
Use *Browser in test browser
inancgumus Jun 14, 2023
303ddc2
Use *Page in test browser
inancgumus Jun 14, 2023
de26ffa
Add t.Helper to TestBrowserOptionsSlowMo
inancgumus Jun 14, 2023
b13b991
Use *Page in tb.NewPage
inancgumus Jun 14, 2023
8063184
Use *Frame in tb.attachFrame
inancgumus Jun 14, 2023
89602b5
Remove testPromise
inancgumus Jun 14, 2023
11053e1
Fix linter errs in tb.run
inancgumus Jun 14, 2023
ba7247a
Fix linter import testBrowser
inancgumus Jun 14, 2023
7eeed2e
Fix linter runJavaScript
inancgumus Jun 14, 2023
e8cc906
Move testBrowser opts to a type
inancgumus Jun 14, 2023
a482486
Refactor vu init in testBrowser
inancgumus Jun 14, 2023
ae6f79a
Move testBrowserType init to func
inancgumus Jun 14, 2023
627497e
Refactor newBrowserTypeWithVU
inancgumus Jun 14, 2023
5843b5a
Move setupHTTPTestModuleInstance
inancgumus Jun 14, 2023
bf958c5
Refactor newBrowserTypeWithVU
inancgumus Jun 14, 2023
74f494b
Refactor testBrowser.withSkipClose
inancgumus Jun 14, 2023
e4d991c
Refactor testBrowser.withHTTPServer
inancgumus Jun 14, 2023
162cf16
Refactor testBrowser.withFileServer
inancgumus Jun 14, 2023
4517114
Refactor testBrowser.withLogCache
inancgumus Jun 14, 2023
30b0ff7
Refactor testBrowser.withSampleListener
inancgumus Jun 14, 2023
480f406
Rename testBrowser.withSampleListener
inancgumus Jun 14, 2023
9da5169
Move testBrowserOptions closer
inancgumus Jun 14, 2023
fa42d5e
Add testBrowser.withEnvLookup for consistency
inancgumus Jun 14, 2023
7d3bafd
Use testBrowser.withEnvLookup
inancgumus Jun 14, 2023
469a619
Refactor to testBrowserOptions setting
inancgumus Jun 14, 2023
0be2d88
Refactor newTestBrowser
inancgumus Jun 14, 2023
7e111ca
Use testBrowser helpers in tests
inancgumus Jun 14, 2023
3d076a2
Remove testBrowser.attachFrame
inancgumus Jun 14, 2023
52887c4
Refactor testBrowser exports
inancgumus Jun 14, 2023
7486f9f
Rename MoveToContext to RestoreVUState
inancgumus Jun 14, 2023
930640f
Add testBrowserOptions.apply
inancgumus Jun 15, 2023
037049c
Reorder testBrowser fields
inancgumus Jun 15, 2023
7be58e8
Add testBrowser to testBrowserOptions
inancgumus Jun 14, 2023
b0547d2
Add testBrowser.isBrowserInitialized
inancgumus Jun 15, 2023
50ebe93
Refactor testBrowser.withLogCache
inancgumus Jun 15, 2023
4bc306e
Refactor testBrowser.withHTTPServer
inancgumus Jun 15, 2023
2c30d93
Refactor testBrowser.withFileServer
inancgumus Jun 15, 2023
dce89e4
Reorder testBrowserOptions for organization
inancgumus Jun 15, 2023
6fe57d0
Move lookUpfunc to testBrowser
inancgumus Jun 15, 2023
4001c14
Move samples to testBrowser
inancgumus Jun 15, 2023
3543759
Move skipClose to testBrowser
inancgumus Jun 15, 2023
779e83b
Remove testBrowserOptions in favor of testBrowser
inancgumus Jun 15, 2023
0fc72a9
Reorder testBrowser options near testBrowser
inancgumus Jun 15, 2023
a98cfff
Move browser type init out of newBrowserTypeWithVU
inancgumus Jun 16, 2023
f5dd191
Move VU cancelation into newTestBrowserVU
inancgumus Jun 16, 2023
48d9d7f
Rename RestoreVUState to ActivateVU
inancgumus Jun 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions k6ext/k6test/vu.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion tests/browser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion tests/frame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
273 changes: 126 additions & 147 deletions tests/test_browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,111 +47,54 @@ 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.
// - withSamplesListener: provides a channel to receive the browser metrics.
// - env.LookupFunc: provides a custom lookup function for environment variables.
// - 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()

// 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
}
}

vu := setupHTTPTestModuleInstance(tb, 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 = lookupFunc

bt := chromium.NewBrowserType(vu)

// Delete the pre-init stage data.
vu.MoveToVUContext()

// enable the HTTP test server only when necessary
var (
testServer *k6httpmultibin.HTTPMultiBin
state = vu.StateField
lc *logCache
)
tbr := &testBrowser{t: tb}
tbopts := newTestBrowserOptions(opts...)
tbr.browserType, tbr.vu, tbr.cancel = newBrowserTypeWithVU(tb, tbopts)
tb.Cleanup(tbr.cancel)

if enableLogCache {
lc = attachLogCache(tb, state.Logger)
if tbopts.logCache {
tbr.logCache = attachLogCache(tb, tbr.vu.StateField.Logger)
}
if enableHTTPMultiBin {
testServer = k6httpmultibin.NewHTTPMultiBin(tb)
state.TLSConfig = testServer.TLSClientConfig
state.Transport = testServer.HTTPTransport
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()
}

b, pid, err := bt.Launch(dummyCtx)
b, pid, err := tbr.browserType.Launch(tbr.vu.Context())
if err != nil {
tb.Fatalf("testBrowser: %v", err)
}
cb, ok := b.(*common.Browser)
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 !skipClose {
b.Close()
if !tbopts.skipClose {
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: cancel,
}
if enableFileServer {
tbr = tbr.withFileServer()
}

return tbr
}

Expand All @@ -169,38 +112,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()
Expand Down Expand Up @@ -341,23 +254,56 @@ 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{}
// 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)
}

type testBrowserOptions struct {
fileServer bool
logCache bool
httpMultiBin bool
samples chan k6metrics.SampleContainer
skipClose bool
lookupFunc env.LookupFunc
}

// withHTTPServer enables the HTTP test server.
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{
samples: make(chan k6metrics.SampleContainer, 1000),
lookupFunc: env.Lookup,
}
for _, opt := range opts {
opt(tbo)
inancgumus marked this conversation as resolved.
Show resolved Hide resolved
}

return tbo
}

// withEnvLookup sets the lookup function for environment variables.
//
// example:
//
// b := TestBrowser(t, withHTTPServer())
func withHTTPServer() httpServerOption {
return struct{}{}
// b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0")))
func withEnvLookup(lookupFunc env.LookupFunc) func(*testBrowserOptions) {
return func(tb *testBrowserOptions) { tb.lookupFunc = lookupFunc }
}

// 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.
//
Expand All @@ -366,51 +312,84 @@ 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
}
}

// 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
}

// logCacheOption is used to detect whether to enable the log cache.
type logCacheOption 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() func(tb *testBrowserOptions) {
return func(tb *testBrowserOptions) { tb.httpMultiBin = true }
}

// 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 }
}

// 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{}
// 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.
//
// example:
//
// b := TestBrowser(t, withSkipClose())
func withSkipClose() skipCloseOption {
return struct{}{}
func withSkipClose() func(tb *testBrowserOptions) {
return func(tb *testBrowserOptions) { tb.skipClose = true }
inancgumus marked this conversation as resolved.
Show resolved Hide resolved
}

// 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 setupHTTPTestModuleInstance(tb testing.TB, samples chan k6metrics.SampleContainer) *k6test.VU {
func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) (
_ *chromium.BrowserType,
_ *k6test.VU,
cancel func(),
) {
tb.Helper()

var (
vu = k6test.NewVU(tb, k6test.WithSamplesListener(samples))
root = k6http.New()
// 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)
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

mi, ok := root.NewModuleInstance(vu).(*k6http.ModuleInstance)
require.True(tb, ok)

require.NoError(tb, vu.Runtime().Set("http", mi.Exports().Default))
bt := chromium.NewBrowserType(vu)
// Delete the pre-init stage data.
vu.MoveToVUContext()

return vu
return bt, vu, cancel
}
Loading