diff --git a/browser/mapping_test.go b/browser/mapping_test.go index 4f2c61f3f..037e4fddb 100644 --- a/browser/mapping_test.go +++ b/browser/mapping_test.go @@ -479,21 +479,22 @@ type requestAPI interface { // responseAPI is the interface of an HTTP response. type responseAPI interface { AllHeaders() map[string]string - Body() goja.ArrayBuffer + Body() ([]byte, error) Frame() *common.Frame - HeaderValue(string) goja.Value + HeaderValue(string) (string, bool) HeaderValues(string) []string Headers() map[string]string HeadersArray() []common.HTTPHeader - JSON() goja.Value + JSON() (any, error) Ok() bool Request() *common.Request - SecurityDetails() goja.Value - ServerAddr() goja.Value + SecurityDetails() *common.SecurityDetails + ServerAddr() *common.RemoteAddress Size() common.HTTPMessageSize Status() int64 StatusText() string URL() string + Text() (string, error) } // locatorAPI represents a way to find element(s) on a page at any moment. diff --git a/browser/response_mapping.go b/browser/response_mapping.go index c82abb0c2..dc63a5ba9 100644 --- a/browser/response_mapping.go +++ b/browser/response_mapping.go @@ -4,37 +4,85 @@ import ( "github.com/dop251/goja" "github.com/grafana/xk6-browser/common" + "github.com/grafana/xk6-browser/k6ext" ) // mapResponse to the JS module. -func mapResponse(vu moduleVU, r *common.Response) mapping { +func mapResponse(vu moduleVU, r *common.Response) mapping { //nolint:funlen if r == nil { return nil } - rt := vu.Runtime() maps := mapping{ - "allHeaders": r.AllHeaders, - "body": r.Body, - "frame": func() *goja.Object { - mf := mapFrame(vu, r.Frame()) - return rt.ToValue(mf).ToObject(rt) - }, - "headerValue": r.HeaderValue, - "headerValues": r.HeaderValues, - "headers": r.Headers, - "headersArray": r.HeadersArray, - "json": r.JSON, - "ok": r.Ok, - "request": func() *goja.Object { - mr := mapRequest(vu, r.Request()) - return rt.ToValue(mr).ToObject(rt) - }, - "securityDetails": r.SecurityDetails, - "serverAddr": r.ServerAddr, - "size": r.Size, - "status": r.Status, - "statusText": r.StatusText, - "url": r.URL, + "allHeaders": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.AllHeaders(), nil + }) + }, + "body": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + body, err := r.Body() + if err != nil { + return nil, err //nolint: wrapcheck + } + buf := vu.Runtime().NewArrayBuffer(body) + return &buf, nil + }) + }, + "frame": func() mapping { + return mapFrame(vu, r.Frame()) + }, + "headerValue": func(name string) *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + v, ok := r.HeaderValue(name) + if !ok { + return nil, nil + } + return v, nil + }) + }, + "headerValues": func(name string) *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.HeaderValues(name), nil + }) + }, + "headers": r.Headers, + "headersArray": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.HeadersArray(), nil + }) + }, + "json": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.JSON() //nolint: wrapcheck + }) + }, + "ok": r.Ok, + "request": func() mapping { + return mapRequest(vu, r.Request()) + }, + "securityDetails": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.SecurityDetails(), nil + }) + }, + "serverAddr": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.ServerAddr(), nil + }) + }, + "size": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.Size(), nil + }) + }, + "status": r.Status, + "statusText": r.StatusText, + "url": r.URL, + "text": func() *goja.Promise { + return k6ext.Promise(vu.Context(), func() (any, error) { + return r.Text() //nolint:wrapcheck + }) + }, } return maps diff --git a/common/http.go b/common/http.go index 13e5d2742..2d3afb1e1 100644 --- a/common/http.go +++ b/common/http.go @@ -418,18 +418,19 @@ func (r *Response) AllHeaders() map[string]string { return headers } -// Body returns the response body as a binary buffer. -func (r *Response) Body() goja.ArrayBuffer { +// Body returns the response body as a bytes buffer. +func (r *Response) Body() ([]byte, error) { if r.status >= 300 && r.status <= 399 { - k6ext.Panic(r.ctx, "Response body is unavailable for redirect responses") + return nil, fmt.Errorf("response body is unavailable for redirect responses") } if err := r.fetchBody(); err != nil { - k6ext.Panic(r.ctx, "getting response body: %w", err) + return nil, fmt.Errorf("getting response body: %w", err) } + r.bodyMu.RLock() defer r.bodyMu.RUnlock() - rt := r.vu.Runtime() - return rt.NewArrayBuffer(r.body) + + return r.body, nil } // bodySize returns the size in bytes of the response body. @@ -456,14 +457,11 @@ func (r *Response) Frame() *Frame { } // HeaderValue returns the value of the given header. -func (r *Response) HeaderValue(name string) goja.Value { +// Returns true if the header is present, false otherwise. +func (r *Response) HeaderValue(name string) (string, bool) { headers := r.AllHeaders() - val, ok := headers[name] - if !ok { - return goja.Null() - } - rt := r.vu.Runtime() - return rt.ToValue(val) + v, ok := headers[name] + return v, ok } // HeaderValues returns the values of the given header. @@ -508,23 +506,24 @@ func (r *Response) HeadersArray() []HTTPHeader { } // JSON returns the response body as JSON data. -func (r *Response) JSON() goja.Value { - if r.cachedJSON == nil { - if err := r.fetchBody(); err != nil { - k6ext.Panic(r.ctx, "getting response body: %w", err) - } +func (r *Response) JSON() (any, error) { + if r.cachedJSON != nil { + return r.cachedJSON, nil + } + if err := r.fetchBody(); err != nil { + return nil, fmt.Errorf("getting response body: %w", err) + } - var v any - r.bodyMu.RLock() - defer r.bodyMu.RUnlock() - if err := json.Unmarshal(r.body, &v); err != nil { - k6ext.Panic(r.ctx, "unmarshalling response body to JSON: %w", err) - } - r.cachedJSON = v + r.bodyMu.RLock() + defer r.bodyMu.RUnlock() + + var v any + if err := json.Unmarshal(r.body, &v); err != nil { + return nil, fmt.Errorf("unmarshalling response body to JSON: %w", err) } - rt := r.vu.Runtime() + r.cachedJSON = v - return rt.ToValue(r.cachedJSON) + return v, nil } // Ok returns true if status code of response if considered ok, otherwise returns false. @@ -541,15 +540,13 @@ func (r *Response) Request() *Request { } // SecurityDetails returns the security details of the response. -func (r *Response) SecurityDetails() goja.Value { - rt := r.vu.Runtime() - return rt.ToValue(r.securityDetails) +func (r *Response) SecurityDetails() *SecurityDetails { + return r.securityDetails } // ServerAddr returns the remote address of the server. -func (r *Response) ServerAddr() goja.Value { - rt := r.vu.Runtime() - return rt.ToValue(r.remoteAddress) +func (r *Response) ServerAddr() *RemoteAddress { + return r.remoteAddress } // Size returns the size in bytes of the response. @@ -571,13 +568,15 @@ func (r *Response) StatusText() string { } // Text returns the response body as a string. -func (r *Response) Text() string { +func (r *Response) Text() (string, error) { if err := r.fetchBody(); err != nil { - k6ext.Panic(r.ctx, "getting response body as text: %w", err) + return "", fmt.Errorf("getting response body as text: %w", err) } + r.bodyMu.RLock() defer r.bodyMu.RUnlock() - return string(r.body) + + return string(r.body), nil } // URL returns the request URL. diff --git a/tests/browser_context_options_test.go b/tests/browser_context_options_test.go index 0e3ab30a6..ee062036f 100644 --- a/tests/browser_context_options_test.go +++ b/tests/browser_context_options_test.go @@ -105,11 +105,17 @@ func TestBrowserContextOptionsExtraHTTPHeaders(t *testing.T) { return err } require.NotNil(t, resp) + + responseBody, err := resp.Body() + require.NoError(t, err) + var body struct{ Headers map[string][]string } - require.NoError(t, json.Unmarshal(resp.Body().Bytes(), &body)) + require.NoError(t, json.Unmarshal(responseBody, &body)) + h := body.Headers["Some-Header"] require.NotEmpty(t, h) assert.Equal(t, "Some-Value", h[0]) + return nil }) require.NoError(t, err) diff --git a/tests/page_test.go b/tests/page_test.go index 188e56efd..50a97f904 100644 --- a/tests/page_test.go +++ b/tests/page_test.go @@ -679,8 +679,11 @@ func TestPageSetExtraHTTPHeaders(t *testing.T) { require.NoError(t, err) require.NotNil(t, resp) + responseBody, err := resp.Body() + require.NoError(t, err) + var body struct{ Headers map[string][]string } - err = json.Unmarshal(resp.Body().Bytes(), &body) + err = json.Unmarshal(responseBody, &body) require.NoError(t, err) h := body.Headers["Some-Header"]