Skip to content

Commit

Permalink
Refactor the k6/html and k6/http modules to the new JS module API
Browse files Browse the repository at this point in the history
  • Loading branch information
na-- committed Dec 13, 2021
1 parent 922c963 commit 8a475ab
Show file tree
Hide file tree
Showing 28 changed files with 456 additions and 404 deletions.
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ linters-settings:
min-confidence: 0
gocyclo:
min-complexity: 25
cyclop: #TODO: see https://github.com/grafana/k6/issues/2294, leave only one of these?
max-complexity: 25
maligned:
suggest-new: true
dupl:
Expand Down
16 changes: 5 additions & 11 deletions js/modules/k6/html/element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
package html

import (
"context"
"testing"

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"

"go.k6.io/k6/js/common"
"github.com/stretchr/testify/require"
)

const testHTMLElem = `
Expand Down Expand Up @@ -55,17 +53,13 @@ const testHTMLElem = `
`

func TestElement(t *testing.T) {
rt := goja.New()
rt.SetFieldNameMapper(common.FieldNameMapper{})

ctx := common.WithRuntime(context.Background(), rt)
rt.Set("src", testHTMLElem)
rt.Set("html", common.Bind(rt, &HTML{}, &ctx))
// compileProtoElem()
t.Parallel()
rt, _ := getTestModuleInstance(t)
require.NoError(t, rt.Set("src", testHTMLElem))

_, err := rt.RunString(`var doc = html.parseHTML(src)`)

assert.NoError(t, err)
require.NoError(t, err)
assert.IsType(t, Selection{}, rt.Get("doc").Export())

t.Run("NodeName", func(t *testing.T) {
Expand Down
19 changes: 6 additions & 13 deletions js/modules/k6/html/elements_gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@
package html

import (
"context"
"testing"

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"

"go.k6.io/k6/js/common"
"github.com/stretchr/testify/require"
)

var textTests = []struct {
Expand Down Expand Up @@ -404,16 +401,13 @@ const testGenElems = `<html><body>
`

func TestGenElements(t *testing.T) {
rt := goja.New()
rt.SetFieldNameMapper(common.FieldNameMapper{})

ctx := common.WithRuntime(context.Background(), rt)
rt.Set("src", testGenElems)
rt.Set("html", common.Bind(rt, &HTML{}, &ctx))
t.Parallel()
rt, mi := getTestModuleInstance(t)
require.NoError(t, rt.Set("src", testGenElems))

_, err := rt.RunString("var doc = html.parseHTML(src)")

assert.NoError(t, err)
require.NoError(t, err)
assert.IsType(t, Selection{}, rt.Get("doc").Export())

t.Run("Test text properties", func(t *testing.T) {
Expand Down Expand Up @@ -468,8 +462,7 @@ func TestGenElements(t *testing.T) {
})

t.Run("Test url properties", func(t *testing.T) {
html := HTML{}
sel, parseError := html.ParseHTML(ctx, testGenElems)
sel, parseError := mi.parseHTML(testGenElems)
if parseError != nil {
t.Errorf("Unable to parse html")
}
Expand Down
15 changes: 5 additions & 10 deletions js/modules/k6/html/elements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
package html

import (
"context"
"testing"

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"

"go.k6.io/k6/js/common"
"github.com/stretchr/testify/require"
)

const testHTMLElems = `
Expand Down Expand Up @@ -86,16 +84,13 @@ const testHTMLElems = `
`

func TestElements(t *testing.T) {
rt := goja.New()
rt.SetFieldNameMapper(common.FieldNameMapper{})

ctx := common.WithRuntime(context.Background(), rt)
rt.Set("src", testHTMLElems)
rt.Set("html", common.Bind(rt, &HTML{}, &ctx))
t.Parallel()
rt, _ := getTestModuleInstance(t)
require.NoError(t, rt.Set("src", testHTMLElems))

_, err := rt.RunString(`var doc = html.parseHTML(src)`)

assert.NoError(t, err)
require.NoError(t, err)
assert.IsType(t, Selection{}, rt.Get("doc").Export())

t.Run("AnchorElement", func(t *testing.T) {
Expand Down
53 changes: 47 additions & 6 deletions js/modules/k6/html/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package html

import (
"context"
"errors"
"fmt"
"strings"
Expand All @@ -31,20 +30,62 @@ import (
gohtml "golang.org/x/net/html"

"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
)

type HTML struct{}
// RootModule is the global module object type. It is instantiated once per test
// run and will be used to create k6/html module instances for each VU.
type RootModule struct{}

func New() *HTML {
return &HTML{}
// ModuleInstance represents an instance of the HTML module for every VU.
type ModuleInstance struct {
vu modules.VU
rootModule *RootModule
exports *goja.Object
}

func (HTML) ParseHTML(ctx context.Context, src string) (Selection, error) {
var (
_ modules.Module = &RootModule{}
_ modules.Instance = &ModuleInstance{}
)

// New returns a pointer to a new HTML RootModule.
func New() *RootModule {
return &RootModule{}
}

// Exports returns the JS values this module exports.
func (mi *ModuleInstance) Exports() modules.Exports {
return modules.Exports{
Default: mi.exports,
}
}

// NewModuleInstance returns an HTML module instance for each VU.
func (r *RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
rt := vu.Runtime()
mi := &ModuleInstance{
vu: vu,
rootModule: r,
exports: rt.NewObject(),
}
if err := mi.exports.Set("parseHTML", mi.parseHTML); err != nil {
common.Throw(rt, err)
}
return mi
}

func (mi *ModuleInstance) parseHTML(src string) (Selection, error) {
return ParseHTML(mi.vu.Runtime(), src)
}

// ParseHTML parses the provided HTML source into a Selection object.
func ParseHTML(rt *goja.Runtime, src string) (Selection, error) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(src))
if err != nil {
return Selection{}, err
}
return Selection{rt: common.GetRuntime(ctx), sel: doc.Selection}, nil
return Selection{rt: rt, sel: doc.Selection}, nil
}

type Selection struct {
Expand Down
37 changes: 32 additions & 5 deletions js/modules/k6/html/html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ import (

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modulestest"
"go.k6.io/k6/lib/metrics"
)

const testHTML = `
Expand Down Expand Up @@ -63,18 +66,42 @@ const testHTML = `
</body>
`

func TestParseHTML(t *testing.T) {
func getTestModuleInstance(t testing.TB) (*goja.Runtime, *ModuleInstance) {
rt := goja.New()
rt.SetFieldNameMapper(common.FieldNameMapper{})
ctx := common.WithRuntime(context.Background(), rt)
rt.Set("src", testHTML)
rt.Set("html", common.Bind(rt, New(), &ctx))

ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)

root := New()
mockVU := &modulestest.VU{
RuntimeField: rt,
InitEnvField: &common.InitEnvironment{
Registry: metrics.NewRegistry(),
},
CtxField: ctx,
StateField: nil,
}
mi, ok := root.NewModuleInstance(mockVU).(*ModuleInstance)
require.True(t, ok)

require.NoError(t, rt.Set("html", mi.Exports().Default))

return rt, mi
}

// TODO: split apart?
// nolint: cyclop, tparallel
func TestParseHTML(t *testing.T) {
t.Parallel()
rt, _ := getTestModuleInstance(t)
require.NoError(t, rt.Set("src", testHTML))

// TODO: I literally cannot think of a snippet that makes goquery error.
// I'm not sure if it's even possible without like, an invalid reader or something, which would
// be impossible to cause from the JS side.
_, err := rt.RunString(`var doc = html.parseHTML(src)`)
assert.NoError(t, err)
require.NoError(t, err)
assert.IsType(t, Selection{}, rt.Get("doc").Export())

t.Run("Find", func(t *testing.T) {
Expand Down
14 changes: 5 additions & 9 deletions js/modules/k6/html/serialize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
package html

import (
"context"
"testing"

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"

"go.k6.io/k6/js/common"
"github.com/stretchr/testify/require"
)

const testSerializeHTML = `
Expand Down Expand Up @@ -69,14 +67,12 @@ const testSerializeHTML = `
`

func TestSerialize(t *testing.T) {
rt := goja.New()
rt.SetFieldNameMapper(common.FieldNameMapper{})
ctx := common.WithRuntime(context.Background(), rt)
rt.Set("src", testSerializeHTML)
rt.Set("html", common.Bind(rt, New(), &ctx))
t.Parallel()
rt, _ := getTestModuleInstance(t)
require.NoError(t, rt.Set("src", testSerializeHTML))

_, err := rt.RunString(`var doc = html.parseHTML(src)`)
assert.NoError(t, err)
require.NoError(t, err)
assert.IsType(t, Selection{}, rt.Get("doc").Export())

t.Run("SerializeArray", func(t *testing.T) {
Expand Down
29 changes: 12 additions & 17 deletions js/modules/k6/http/cookiejar.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package http

import (
"context"
"fmt"
"net/http"
"net/http/cookiejar"
Expand All @@ -30,27 +29,23 @@ import (
"time"

"github.com/dop251/goja"

"go.k6.io/k6/js/common"
)

// HTTPCookieJar is cookiejar.Jar wrapper to be used in js scripts
type HTTPCookieJar struct {
// js is to make it not be accessible from inside goja/js, the json is because it's used if we return it from setup
Jar *cookiejar.Jar `js:"-" json:"-"`
ctx *context.Context
}
// ErrJarForbiddenInInitContext is used when a cookie jar was made in the init context
// TODO: unexport this? there's no reason for this to be exported
var ErrJarForbiddenInInitContext = common.NewInitContextError("Making cookie jars in the init context is not supported")

func newCookieJar(ctxPtr *context.Context) *HTTPCookieJar {
jar, err := cookiejar.New(nil)
if err != nil {
common.Throw(common.GetRuntime(*ctxPtr), err)
}
return &HTTPCookieJar{jar, ctxPtr}
// CookieJar is cookiejar.Jar wrapper to be used in js scripts
type CookieJar struct {
moduleInstance *ModuleInstance
// js is to make it not be accessible from inside goja/js, the json is
// for when it is returned from setup().
Jar *cookiejar.Jar `js:"-" json:"-"`
}

// CookiesForURL return the cookies for a given url as a map of key and values
func (j HTTPCookieJar) CookiesForURL(url string) map[string][]string {
func (j CookieJar) CookiesForURL(url string) map[string][]string {
u, err := neturl.Parse(url)
if err != nil {
panic(err)
Expand All @@ -65,8 +60,8 @@ func (j HTTPCookieJar) CookiesForURL(url string) map[string][]string {
}

// Set sets a cookie for a particular url with the given name value and additional opts
func (j HTTPCookieJar) Set(url, name, value string, opts goja.Value) (bool, error) {
rt := common.GetRuntime(*j.ctx)
func (j CookieJar) Set(url, name, value string, opts goja.Value) (bool, error) {
rt := j.moduleInstance.vu.Runtime()

u, err := neturl.Parse(url)
if err != nil {
Expand Down
7 changes: 3 additions & 4 deletions js/modules/k6/http/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package http

import (
"context"
"fmt"
"strings"
"time"
Expand All @@ -42,8 +41,8 @@ func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}

// File returns a FileData parameter
func (h *HTTP) File(ctx context.Context, data interface{}, args ...string) FileData {
// File returns a FileData object.
func (mi *ModuleInstance) file(data interface{}, args ...string) FileData {
// supply valid default if filename and content-type are not specified
fname, ct := fmt.Sprintf("%d", time.Now().UnixNano()), "application/octet-stream"

Expand All @@ -57,7 +56,7 @@ func (h *HTTP) File(ctx context.Context, data interface{}, args ...string) FileD

dt, err := common.ToBytes(data)
if err != nil {
common.Throw(common.GetRuntime(ctx), err)
common.Throw(mi.vu.Runtime(), err)
}

return FileData{
Expand Down
Loading

0 comments on commit 8a475ab

Please sign in to comment.