diff --git a/browser/mapping_test.go b/browser/mapping_test.go index 7dc090f73..3b27e6bf4 100644 --- a/browser/mapping_test.go +++ b/browser/mapping_test.go @@ -193,6 +193,12 @@ func TestMappings(t *testing.T) { return mapConsoleMessage(moduleVU{VU: vu}, &common.ConsoleMessage{}) }, }, + "mapMetricEvent": { + apiInterface: (*metricEventAPI)(nil), + mapp: func() mapping { + return mapMetricEvent(moduleVU{VU: vu}, &common.MetricEvent{}) + }, + }, "mapTouchscreen": { apiInterface: (*touchscreenAPI)(nil), mapp: func() mapping { @@ -326,7 +332,7 @@ type pageAPI interface { IsVisible(selector string, opts sobek.Value) (bool, error) Locator(selector string, opts sobek.Value) *common.Locator MainFrame() *common.Frame - On(event string, handler func(*common.ConsoleMessage) error) error + On(event string, handler func(any) error) error Opener() pageAPI Press(selector string, key string, opts sobek.Value) error Query(selector string) (*common.ElementHandle, error) @@ -366,6 +372,11 @@ type consoleMessageAPI interface { Type() string } +// metricEventAPI is the interface of a metric event. +type metricEventAPI interface { + Tag(matchesRegex common.K6BrowserCheckRegEx, patterns common.URLTagPatterns) error +} + // frameAPI is the interface of a CDP target frame. type frameAPI interface { Check(selector string, opts sobek.Value) error diff --git a/browser/metric_event_mapping.go b/browser/metric_event_mapping.go index 84967fb50..d93279a2d 100644 --- a/browser/metric_event_mapping.go +++ b/browser/metric_event_mapping.go @@ -7,25 +7,11 @@ import ( ) // mapMetricEvent to the JS module. -func mapMetricEvent(vu moduleVU, cm *common.MetricEvent) (mapping, error) { +func mapMetricEvent(vu moduleVU, cm *common.MetricEvent) mapping { rt := vu.VU.Runtime() - // We're setting up the function in the Sobek context that will be reused - // for this VU. - _, err := rt.RunString(` - function _k6BrowserCheckRegEx(pattern, url) { - let r = pattern; - if (typeof pattern === 'string') { - r = new RegExp(pattern); - } - return r.test(url); - }`) - if err != nil { - return nil, fmt.Errorf("evaluating regex function: %w", err) - } - return mapping{ - "Tag": func(urls common.URLTagPatterns) error { + "tag": func(urls common.URLTagPatterns) error { callback := func(pattern, url string) (bool, error) { js := fmt.Sprintf(`_k6BrowserCheckRegEx(%s, '%s')`, pattern, url) @@ -39,5 +25,5 @@ func mapMetricEvent(vu moduleVU, cm *common.MetricEvent) (mapping, error) { return cm.Tag(callback, urls) }, - }, nil + } } diff --git a/browser/page_mapping.go b/browser/page_mapping.go index 3149ab82f..22786a9ab 100644 --- a/browser/page_mapping.go +++ b/browser/page_mapping.go @@ -243,14 +243,11 @@ func mapPage(vu moduleVU, p *common.Page) mapping { //nolint:gocognit,cyclop return errors.New("incorrect metric message") } - mapping, err := mapMetricEvent(vu, m) - if err != nil { - return fmt.Errorf("mapping the metric: %w", err) - } - - if _, err = handler(sobek.Undefined(), vu.VU.Runtime().ToValue(mapping)); err != nil { + mapping := mapMetricEvent(vu, m) + if _, err := handler(sobek.Undefined(), vu.VU.Runtime().ToValue(mapping)); err != nil { return fmt.Errorf("executing page.on('metric') handler: %w", err) } + return nil }) <-c @@ -259,6 +256,25 @@ func mapPage(vu moduleVU, p *common.Page) mapping { //nolint:gocognit,cyclop return fmt.Errorf("unknown page event: %q", event) } + if event == common.EventPageMetricCalled { + // Register a custom regex function for the metric event + // that will be used to check URLs against the patterns. + // This is needed because we want to use the JavaScript regex + // to comply with what users expect when using the `tag` method. + _, err := rt.RunString(` + function _k6BrowserCheckRegEx(pattern, url) { + let r = pattern; + if (typeof pattern === 'string') { + r = new RegExp(pattern); + } + return r.test(url); + } + `) + if err != nil { + return fmt.Errorf("evaluating regex function: %w", err) + } + } + return p.On(event, runInTaskQueue) //nolint:wrapcheck }, "opener": func() *sobek.Promise { diff --git a/common/page.go b/common/page.go index bf5ffac67..8c6505253 100644 --- a/common/page.go +++ b/common/page.go @@ -438,11 +438,13 @@ type URLTagPattern struct { TagName string `js:"name"` } -type k6BrowserCheckRegEx func(pattern, url string) (bool, error) +// K6BrowserCheckRegEx is a function that will be used to check the URL tag +// against the user defined regexes in the Sobek runtime. +type K6BrowserCheckRegEx func(pattern, url string) (bool, error) // Tag will find the first match given the URLTagPatterns and the URL from // the metric tag and update the name field. -func (e *MetricEvent) Tag(matchesRegex k6BrowserCheckRegEx, overrides URLTagPatterns) error { +func (e *MetricEvent) Tag(matchesRegex K6BrowserCheckRegEx, patterns URLTagPatterns) error { for _, o := range overrides.URLs { name := strings.TrimSpace(o.TagName) if name == "" { diff --git a/examples/pageon-metric.js b/examples/pageon-metric.js index bb82b20f0..978f301e1 100644 --- a/examples/pageon-metric.js +++ b/examples/pageon-metric.js @@ -17,7 +17,7 @@ export default async function() { const page = await browser.newPage(); page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^https:\/\/test\.k6\.io\/\?q=[0-9a-z]+$/, name:'test'}, ] diff --git a/tests/page_test.go b/tests/page_test.go index 280d66449..a23f8324c 100644 --- a/tests/page_test.go +++ b/tests/page_test.go @@ -1924,7 +1924,7 @@ func TestPageOnMetric(t *testing.T) { // Just a single page.on. name: "single_page.on", fun: `page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-1'}, ] @@ -1936,12 +1936,12 @@ func TestPageOnMetric(t *testing.T) { // A single page.on but with multiple calls to Tag. name: "multi_tag", fun: `page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-1'}, ] }); - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-2'}, ] @@ -1953,19 +1953,19 @@ func TestPageOnMetric(t *testing.T) { // Two page.on and in one of them multiple calls to Tag. name: "multi_tag_page.on", fun: `page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-1'}, ] }); - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-2'}, ] }); }); page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-3'}, ] @@ -1977,13 +1977,13 @@ func TestPageOnMetric(t *testing.T) { // A single page.on but within it another page.on. name: "multi_page.on_call", fun: `page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-1'}, ] }); page.on('metric', (metric) => { - metric.Tag({ + metric.tag({ urls: [ {url: /^http:\/\/127\.0\.0\.1\:[0-9]+\/ping\?h=[0-9a-z]+$/, name:'ping-4'}, ]