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
Changes from 12 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
353 changes: 181 additions & 172 deletions tests/test_browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,31 @@ 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
// 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.
logCache *logCache
// lookupFunc is set by the withEnvLookup option.
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.
Expand All @@ -55,25 +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(opts...)
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)

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
tbr.vu.StateField.Transport = tbr.http.HTTPTransport
}
if tbopts.fileServer {
tbr = tbr.withFileServer()
}
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 {
Expand All @@ -91,7 +94,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()
}
}
Expand All @@ -100,6 +103,160 @@ func newTestBrowser(tb testing.TB, opts ...func(*testBrowserOptions)) *testBrows
return tbr
}

// newBrowserTypeWithVU creates a new browser type with a VU.
func newBrowserTypeWithVU(tb testing.TB, tbr *testBrowser) (
_ *chromium.BrowserType,
_ *k6test.VU,
cancel func(),
) {
tb.Helper()

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))
metricsCtx := k6ext.WithCustomMetrics(
vu.Context(),
k6ext.RegisterCustomMetrics(k6metrics.NewRegistry()),
)
ctx, cancel := context.WithCancel(metricsCtx)
vu.CtxField = ctx
vu.InitEnvField.LookupEnv = tbr.lookupFunc

bt := chromium.NewBrowserType(vu)
vu.RestoreVUState()
inancgumus marked this conversation as resolved.
Show resolved Hide resolved

return bt, vu, cancel
}

// 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.
b.lookupFunc = env.Lookup
}

// applyOptions applies the given options to the testBrowser.
func (b *testBrowser) applyOptions(opts ...func(*testBrowser)) {
for _, opt := range opts {
opt(b)
}
}

// withEnvLookup sets the lookup function for environment variables.
//
// example:
//
// b := TestBrowser(t, withEnvLookup(env.ConstLookup(env.BrowserHeadless, "0")))
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
// for static files.
//
// see: WithFileServer
//
// example:
//
// b := TestBrowser(t, withFileServer())
func withFileServer() func(*testBrowser) {
return func(tb *testBrowser) {
if !tb.isBrowserTypeInitialized {
return
}
if tb.http == nil {
// file server needs HTTP server.
apply := withHTTPServer()
apply(tb)
}
_ = tb.withFileServer()
}
}

// 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 {
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(*testBrowser) {
return func(tb *testBrowser) {
if !tb.isBrowserTypeInitialized {
return
}
if tb.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.
//
// example:
//
// b := TestBrowser(t, withLogCache())
func withLogCache() func(*testBrowser) {
return func(tb *testBrowser) {
if !tb.isBrowserTypeInitialized {
return
}
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(*testBrowser) {
return func(tb *testBrowser) { 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() 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 {
Expand Down Expand Up @@ -218,151 +375,3 @@ func (b *testBrowser) awaitWithTimeout(timeout time.Duration, fn func() error) e
return fmt.Errorf("test timed out after %s", timeout)
}
}

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

// testBrowserOptions is a helper for creating testBrowser options.
type testBrowserOptions struct {
fileServer bool
logCache bool
httpMultiBin bool
samples chan k6metrics.SampleContainer
skipClose bool
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.
tbo := &testBrowserOptions{
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)
}
}

// 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) {
return func(tb *testBrowserOptions) { tb.lookupFunc = lookupFunc }
}

// withFileServer enables the HTTP test server and serves a file server
// for static files.
//
// see: WithFileServer
//
// example:
//
// b := TestBrowser(t, withFileServer())
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
}

// 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() 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.
//
// example:
//
// b := TestBrowser(t, withSkipClose())
func withSkipClose() func(tb *testBrowserOptions) {
return func(tb *testBrowserOptions) { tb.skipClose = true }
}

func newBrowserTypeWithVU(tb testing.TB, opts *testBrowserOptions) (
_ *chromium.BrowserType,
_ *k6test.VU,
cancel func(),
) {
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)
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)
// Delete the pre-init stage data.
vu.RestoreVUState()

return bt, vu, cancel
}
Loading