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 isRemoteBrowser check #889

Merged
merged 9 commits into from
May 23, 2023
Merged
4 changes: 1 addition & 3 deletions browser/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import (
"context"
"errors"
"fmt"
"os"

"github.com/dop251/goja"

"github.com/grafana/xk6-browser/api"
"github.com/grafana/xk6-browser/chromium"
"github.com/grafana/xk6-browser/env"
"github.com/grafana/xk6-browser/k6error"
"github.com/grafana/xk6-browser/k6ext"

Expand All @@ -37,7 +35,7 @@ func mapBrowserToGoja(vu moduleVU) *goja.Object {
obj = rt.NewObject()
// TODO: Use k6 LookupEnv instead of OS package methods.
// See https://github.com/grafana/xk6-browser/issues/822.
wsURL, isRemoteBrowser = env.IsRemoteBrowser(os.LookupEnv)
wsURL, isRemoteBrowser = vu.remoteRegistry.IsRemoteBrowser()
browserType = chromium.NewBrowserType(vu)
)
for k, v := range mapBrowserType(vu, browserType, wsURL, isRemoteBrowser) {
Expand Down
14 changes: 10 additions & 4 deletions browser/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
package browser

import (
"os"

"github.com/dop251/goja"

"github.com/grafana/xk6-browser/common"
"github.com/grafana/xk6-browser/registry"

k6modules "go.k6.io/k6/js/modules"
)
Expand All @@ -13,7 +16,8 @@ type (
// RootModule is the global module instance that will create module
// instances for each VU.
RootModule struct {
PidRegistry *pidRegistry
PidRegistry *pidRegistry
remoteRegistry *registry.RemoteRegistry
}

// JSModule exposes the properties available to the JS script.
Expand All @@ -37,7 +41,8 @@ var (
// New returns a pointer to a new RootModule instance.
func New() *RootModule {
return &RootModule{
PidRegistry: &pidRegistry{},
PidRegistry: &pidRegistry{},
remoteRegistry: registry.NewRemoteRegistry(os.LookupEnv),
}
}

Expand All @@ -47,8 +52,9 @@ func (m *RootModule) NewModuleInstance(vu k6modules.VU) k6modules.Instance {
return &ModuleInstance{
mod: &JSModule{
Chromium: mapBrowserToGoja(moduleVU{
VU: vu,
pidRegistry: m.PidRegistry,
VU: vu,
pidRegistry: m.PidRegistry,
remoteRegistry: m.remoteRegistry,
}),
Devices: common.GetDevices(),
},
Expand Down
2 changes: 2 additions & 0 deletions browser/modulevu.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/grafana/xk6-browser/k6ext"
"github.com/grafana/xk6-browser/registry"

k6modules "go.k6.io/k6/js/modules"
)
Expand All @@ -16,6 +17,7 @@ type moduleVU struct {
k6modules.VU

*pidRegistry
remoteRegistry *registry.RemoteRegistry
ka3de marked this conversation as resolved.
Show resolved Hide resolved
}

func (vu moduleVU) Context() context.Context {
Expand Down
2 changes: 0 additions & 2 deletions env/doc.go

This file was deleted.

38 changes: 1 addition & 37 deletions env/env.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
// Package env provides types to interact with environment setup.
package env

import (
"crypto/rand"
"math/big"
"strings"
)

// LookupFunc defines a function to look up a key from the environment.
type LookupFunc func(key string) (string, bool)

// IsRemoteBrowser returns true and the corresponding CDP
// WS URL if this one is set through the K6_BROWSER_WS_URL
// environment variable. Otherwise returns false.
// If K6_BROWSER_WS_URL is set as a comma separated list of
// URLs, this method returns a randomly chosen URL from the list
// so connections are done in a round-robin fashion for all the
// entries in the list.
func IsRemoteBrowser(envLookup LookupFunc) (wsURL string, isRemote bool) {
wsURL, isRemote = envLookup("K6_BROWSER_WS_URL")
if !isRemote {
return "", false
}
if !strings.ContainsRune(wsURL, ',') {
return wsURL, isRemote
}

// If last parts element is a void string,
// because WS URL contained an ending comma,
// remove it
parts := strings.Split(wsURL, ",")
if parts[len(parts)-1] == "" {
parts = parts[:len(parts)-1]
}

// Choose a random WS URL from the provided list
i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(parts))))
wsURL = parts[i.Int64()]

return wsURL, isRemote
}
2 changes: 1 addition & 1 deletion examples/multiple-scenario.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const options = {
},
},
thresholds: {
webvital_first_contentful_paint: ['max < 1000'],
browser_web_vital_fcp: ['max < 1000'],
checks: ["rate==1.0"]
}
}
Expand Down
12 changes: 6 additions & 6 deletions k6ext/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ type CustomMetrics struct {
// VU Registry and returns our internal struct pointer.
func RegisterCustomMetrics(registry *k6metrics.Registry) *CustomMetrics {
wvs := map[string]string{
webVitalFID: "webvital_first_input_delay",
webVitalTTFB: "webvital_time_to_first_byte",
webVitalLCP: "webvital_largest_content_paint",
webVitalCLS: "webvital_cumulative_layout_shift",
webVitalINP: "webvital_interaction_to_next_paint",
webVitalFCP: "webvital_first_contentful_paint",
webVitalFID: "browser_web_vital_fid", // first input delay
webVitalTTFB: "browser_web_vital_ttfb", // time to first byte
webVitalLCP: "browser_web_vital_lcp", // largest content paint
webVitalCLS: "browser_web_vital_cls", // cumulative layout shift
webVitalINP: "browser_web_vital_inp", // interaction to next paint
webVitalFCP: "browser_web_vital_fcp", // first contentful paint
}
webVitals := make(map[string]*k6metrics.Metric)

Expand Down
3 changes: 3 additions & 0 deletions registry/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package registry provides types and methods that store
// information that currently span the whole test run.
package registry
ka3de marked this conversation as resolved.
Show resolved Hide resolved
66 changes: 66 additions & 0 deletions registry/remote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package registry

import (
"crypto/rand"
"math/big"
"strings"

"github.com/grafana/xk6-browser/env"
)

// RemoteRegistry contains the details of the remote web browsers.
// At the moment it's the WS URLs.
type RemoteRegistry struct {
isRemoteBrowser bool
wsURLs []string
}

// NewRemoteRegistry will create a new RemoteRegistry. This will
// parse the K6_BROWSER_WS_URL env var to retrieve the defined
// list of WS URLs.
//
// K6_BROWSER_WS_URL can be defined as a single WS URL or a
// comma separated list of URLs.
func NewRemoteRegistry(envLookup env.LookupFunc) *RemoteRegistry {
r := &RemoteRegistry{}

wsURL, isRemote := envLookup("K6_BROWSER_WS_URL")
if !isRemote {
return r
}

if !strings.ContainsRune(wsURL, ',') {
r.isRemoteBrowser = true
r.wsURLs = []string{wsURL}
return r
}

// If last parts element is a void string,
// because WS URL contained an ending comma,
// remove it
parts := strings.Split(wsURL, ",")
if parts[len(parts)-1] == "" {
parts = parts[:len(parts)-1]
}

r.isRemoteBrowser = true
r.wsURLs = parts

return r
}

// IsRemoteBrowser returns a WS URL and true when a WS URL is defined,
// otherwise it returns an empty string and false. If more than one
// WS URL was registered in newRemoteRegistry, a randomly chosen URL from
// the list in a round-robin fashion is selected and returned.
func (r *RemoteRegistry) IsRemoteBrowser() (string, bool) {
if !r.isRemoteBrowser {
return "", false
}

// Choose a random WS URL from the provided list
i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(r.wsURLs))))
wsURL := r.wsURLs[i.Int64()]

return wsURL, true
}
9 changes: 6 additions & 3 deletions env/env_test.go → registry/remote_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package env
package registry

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/grafana/xk6-browser/env"
)

func TestIsRemoteBrowser(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
envLookup LookupFunc
envLookup env.LookupFunc
expIsRemote bool
expValidWSURLs []string
}{
Expand Down Expand Up @@ -70,7 +72,8 @@ func TestIsRemoteBrowser(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

wsURL, isRemote := IsRemoteBrowser(tc.envLookup)
rr := NewRemoteRegistry(tc.envLookup)
wsURL, isRemote := rr.IsRemoteBrowser()

require.Equal(t, tc.expIsRemote, isRemote)
if isRemote {
Expand Down
20 changes: 10 additions & 10 deletions tests/webvital_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ func TestWebVitalMetric(t *testing.T) {
browser = newTestBrowser(t, withFileServer(), withSamplesListener(samples))
page = browser.NewPage(nil)
expected = map[string]bool{
"webvital_time_to_first_byte": false,
"webvital_time_to_first_byte_good": false,
"webvital_first_contentful_paint": false,
"webvital_first_contentful_paint_good": false,
"webvital_largest_content_paint": false,
"webvital_largest_content_paint_good": false,
"webvital_first_input_delay": false,
"webvital_first_input_delay_good": false,
"webvital_cumulative_layout_shift": false,
"webvital_cumulative_layout_shift_good": false,
"browser_web_vital_ttfb": false,
"browser_web_vital_ttfb_good": false,
"browser_web_vital_fcp": false,
"browser_web_vital_fcp_good": false,
"browser_web_vital_lcp": false,
"browser_web_vital_lcp_good": false,
"browser_web_vital_fid": false,
"browser_web_vital_fid_good": false,
"browser_web_vital_cls": false,
"browser_web_vital_cls_good": false,
}
)

Expand Down