diff --git a/go.mod b/go.mod index 15dd794dea5..cf4442d2fd7 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible github.com/golang/protobuf v1.5.3 github.com/gorilla/websocket v1.5.1 - github.com/grafana/xk6-browser v1.4.1 + github.com/grafana/xk6-browser v1.4.2 github.com/grafana/xk6-dashboard v0.7.2 github.com/grafana/xk6-output-prometheus-remote v0.3.1 github.com/grafana/xk6-redis v0.2.0 diff --git a/go.sum b/go.sum index ec0278f531f..71402220841 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/grafana/xk6-browser v1.4.1 h1:NXU5wGf/QKkH4m8APMMlylsyX1znvC9g5qkHe9cx4GE= -github.com/grafana/xk6-browser v1.4.1/go.mod h1:pj4OWy+VL3vM5qmsuAxwGleySq/6fhRsocfmHGQV7YQ= +github.com/grafana/xk6-browser v1.4.2 h1:9GuPE9g4bGyXg/ZN2h7cgx/kSCh9KvuALP1pyNY5zT8= +github.com/grafana/xk6-browser v1.4.2/go.mod h1:pj4OWy+VL3vM5qmsuAxwGleySq/6fhRsocfmHGQV7YQ= github.com/grafana/xk6-dashboard v0.7.2 h1:CLaWeRfPZ388IS6rBn0nI+lqtX50QoQ73z0Hz5BIrS4= github.com/grafana/xk6-dashboard v0.7.2/go.mod h1:7HLAY4udlWGXGDQL5gWIi+In3eZRljXi8AnHt1Z+lFM= github.com/grafana/xk6-output-prometheus-remote v0.3.1 h1:X23rQzlJD8dXWB31DkxR4uPnuRFo8L0Y0H22fSG9xl0= diff --git a/vendor/github.com/grafana/xk6-browser/common/element_handle.go b/vendor/github.com/grafana/xk6-browser/common/element_handle.go index d206c65c9e3..b8a6c276d75 100644 --- a/vendor/github.com/grafana/xk6-browser/common/element_handle.go +++ b/vendor/github.com/grafana/xk6-browser/common/element_handle.go @@ -2,13 +2,9 @@ package common import ( "context" - "encoding/base64" "errors" "fmt" "math" - "mime" - "os" - "path/filepath" "reflect" "strings" "time" @@ -23,8 +19,10 @@ import ( "github.com/grafana/xk6-browser/k6ext" ) -const resultDone = "done" -const resultNeedsInput = "needsinput" +const ( + resultDone = "done" + resultNeedsInput = "needsinput" +) type ( elementHandleActionFunc func(context.Context, *ElementHandle) (any, error) @@ -1271,22 +1269,6 @@ func (h *ElementHandle) SetInputFiles(files goja.Value, opts goja.Value) error { return nil } -func (h *ElementHandle) resolveFiles(payload []*File) error { - for _, file := range payload { - if strings.TrimSpace(file.Path) != "" { - buffer, err := os.ReadFile(file.Path) - if err != nil { - return fmt.Errorf("reading file: %w", err) - } - file.Buffer = base64.StdEncoding.EncodeToString(buffer) - file.Name = filepath.Base(file.Path) - file.Mimetype = mime.TypeByExtension(filepath.Ext(file.Path)) - } - } - - return nil -} - func (h *ElementHandle) setInputFiles(apiCtx context.Context, payload []*File) error { fn := ` (node, injected, payload) => { @@ -1297,10 +1279,6 @@ func (h *ElementHandle) setInputFiles(apiCtx context.Context, payload []*File) e forceCallable: true, returnByValue: true, } - err := h.resolveFiles(payload) - if err != nil { - return err - } result, err := h.evalWithScript(apiCtx, evalOpts, fn, payload) if err != nil { return err diff --git a/vendor/github.com/grafana/xk6-browser/common/element_handle_options.go b/vendor/github.com/grafana/xk6-browser/common/element_handle_options.go index 1b57208c61e..7ab0be9193c 100644 --- a/vendor/github.com/grafana/xk6-browser/common/element_handle_options.go +++ b/vendor/github.com/grafana/xk6-browser/common/element_handle_options.go @@ -78,7 +78,6 @@ type ElementHandleHoverOptions struct { // File is the descriptor of a single file. type File struct { - Path string `json:"-"` Name string `json:"name"` Mimetype string `json:"mimeType"` Buffer string `json:"buffer"` @@ -204,7 +203,7 @@ func NewElementHandleSetInputFilesOptions(defaultTimeout time.Duration) *Element } } -// addFile to the struct. Input value can be a path, or a file descriptor object. +// addFile to the struct. Input value can only be a file descriptor object. func (f *Files) addFile(ctx context.Context, file goja.Value) error { if !gojaValueExists(file) { return nil @@ -218,10 +217,6 @@ func (f *Files) addFile(ctx context.Context, file goja.Value) error { return fmt.Errorf("parsing file descriptor: %w", err) } f.Payload = append(f.Payload, &parsedFile) - case reflect.String: // file path - if v, ok := file.Export().(string); ok { - f.Payload = append(f.Payload, &File{Path: v}) - } default: return fmt.Errorf("invalid parameter type : %s", fileType.Kind().String()) } diff --git a/vendor/github.com/grafana/xk6-browser/common/js/injected_script.js b/vendor/github.com/grafana/xk6-browser/common/js/injected_script.js index bee3bec3851..a37093abf1c 100644 --- a/vendor/github.com/grafana/xk6-browser/common/js/injected_script.js +++ b/vendor/github.com/grafana/xk6-browser/common/js/injected_script.js @@ -124,6 +124,18 @@ class XPathQueryEngine { selector = "." + selector; } const result = []; + + // DocumentFragments cannot be queried with XPath and they do not implement + // evaluate. It first needs to be converted to a Document before being able + // to run the evaluate against it. + // + // This avoids the following error: + // - Failed to execute 'evaluate' on 'Document': The node provided is + // '#document-fragment', which is not a valid context node type. + if (root instanceof DocumentFragment) { + root = convertToDocument(root); + } + const document = root instanceof Document ? root : root.ownerDocument; if (!document) { return result; @@ -143,6 +155,43 @@ class XPathQueryEngine { } } +// convertToDocument will convert a DocumentFragment into a Document. It does +// this by creating a new Document and copying the elements from the +// DocumentFragment to the Document. +function convertToDocument(fragment) { + var newDoc = document.implementation.createHTMLDocument("Temporary Document"); + + copyNodesToDocument(fragment, newDoc.body); + + return newDoc; +} + +// copyNodesToDocument manually copies nodes to a new document, excluding +// ShadowRoot nodes -- ShadowRoot are not cloneable so we need to manually +// clone them one element at a time. +function copyNodesToDocument(sourceNode, targetNode) { + sourceNode.childNodes.forEach((child) => { + if (child.nodeType === Node.ELEMENT_NODE) { + // Clone the child node without its descendants + let clonedChild = child.cloneNode(false); + targetNode.appendChild(clonedChild); + + // If the child has a shadow root, recursively copy its children + // instead of the shadow root itself. + if (child.shadowRoot) { + copyNodesToDocument(child.shadowRoot, clonedChild); + } else { + // Recursively copy normal child nodes + copyNodesToDocument(child, clonedChild); + } + } else { + // For non-element nodes (like text nodes), clone them directly. + let clonedChild = child.cloneNode(true); + targetNode.appendChild(clonedChild); + } + }); +} + class InjectedScript { constructor() { this._replaceRafWithTimeout = false; @@ -777,26 +826,31 @@ class InjectedScript { resolve = res; reject = rej; }); - const observer = new MutationObserver(async () => { - if (timedOut) { + try { + const observer = new MutationObserver(async () => { + if (timedOut) { + observer.disconnect(); + reject(`timed out after ${timeout}ms`); + } + const success = predicate(); + if (success !== continuePolling) { + observer.disconnect(); + resolve(success); + } + }); + timeoutPoll = () => { observer.disconnect(); reject(`timed out after ${timeout}ms`); - } - const success = predicate(); - if (success !== continuePolling) { - observer.disconnect(); - resolve(success); - } - }); - timeoutPoll = () => { - observer.disconnect(); - reject(`timed out after ${timeout}ms`); - }; - observer.observe(document, { - childList: true, - subtree: true, - attributes: true, - }); + }; + observer.observe(document, { + childList: true, + subtree: true, + attributes: true, + }); + } catch(error) { + reject(error); + return; + } return result; } @@ -810,13 +864,22 @@ class InjectedScript { return result; async function onRaf() { - if (timedOut) { - reject(`timed out after ${timeout}ms`); + try { + if (timedOut) { + reject(`timed out after ${timeout}ms`); + return; + } + const success = predicate(); + if (success !== continuePolling) { + resolve(success); + return + } else { + requestAnimationFrame(onRaf); + } + } catch (error) { + reject(error); return; } - const success = predicate(); - if (success !== continuePolling) resolve(success); - else requestAnimationFrame(onRaf); } } @@ -830,13 +893,18 @@ class InjectedScript { return result; async function onTimeout() { - if (timedOut) { - reject(`timed out after ${timeout}ms`); + try{ + if (timedOut) { + reject(`timed out after ${timeout}ms`); + return; + } + const success = predicate(); + if (success !== continuePolling) resolve(success); + else setTimeout(onTimeout, pollInterval); + } catch(error) { + reject(error); return; } - const success = predicate(); - if (success !== continuePolling) resolve(success); - else setTimeout(onTimeout, pollInterval); } } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 1e88be8e4b1..9a3768d721c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -163,7 +163,7 @@ github.com/google/uuid # github.com/gorilla/websocket v1.5.1 ## explicit; go 1.20 github.com/gorilla/websocket -# github.com/grafana/xk6-browser v1.4.1 +# github.com/grafana/xk6-browser v1.4.2 ## explicit; go 1.20 github.com/grafana/xk6-browser/browser github.com/grafana/xk6-browser/chromium