Skip to content
This repository has been archived by the owner on Jan 30, 2025. It is now read-only.

Streamline the addition and running of new page.on events #1475

Merged
merged 20 commits into from
Oct 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 58 additions & 47 deletions browser/page_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,74 +419,85 @@ func mapPage(vu moduleVU, p *common.Page) mapping { //nolint:gocognit,cyclop
}

// mapPageOn maps the requested page.on event to the Sobek runtime.
// It generalizes the handling of page.on events on a taskqueue.
func mapPageOn(vu moduleVU, p *common.Page) func(common.PageOnEventName, sobek.Callable) error { //nolint:funlen
// It generalizes the handling of page.on events.
func mapPageOn(vu moduleVU, p *common.Page) func(common.PageOnEventName, sobek.Callable) error {
rt := vu.Runtime()

pageOnEvents := map[common.PageOnEventName]struct {
mapp func(vu moduleVU, event common.PageOnEvent) mapping
init func() error // If set, runs before the event handler.
wait bool // Whether to wait for the handler to complete.
}{
common.EventPageConsoleAPICalled: {
mapp: mapConsoleMessage,
wait: false,
},
common.EventPageMetricCalled: {
mapp: mapMetricEvent,
init: prepK6BrowserRegExChecker(rt),
wait: true,
},
}

return func(eventName common.PageOnEventName, handleEvent sobek.Callable) error {
tq := vu.taskQueueRegistry.get(vu.Context(), p.TargetID())
pageOnEvent, ok := pageOnEvents[eventName]
if !ok {
return fmt.Errorf("unknown page on event: %q", eventName)
}

onEventPageConsoleAPICalled := func(event common.PageOnEvent) {
tq.Queue(func() error {
mapping := mapConsoleMessage(vu, event)
_, err := handleEvent(sobek.Undefined(), rt.ToValue(mapping))
if err != nil {
return fmt.Errorf("executing page.on handler: %w", err)
}
return nil
})
// Initializes the environment for the event handler if necessary.
if pageOnEvent.init != nil {
if err := pageOnEvent.init(); err != nil {
return fmt.Errorf("initiating page.on('%s'): %w", eventName, err)
}
}

onEventPageMetricCalled := func(event common.PageOnEvent) {
// The function on the taskqueue runs in its own goroutine
// so we need to use a channel to wait for it to complete
// since we're waiting for updates from the handler which
// will be written to the ExportedMetric.
// Run the the event handler in the task queue to
// ensure that the handler is executed on the event loop.
tq := vu.taskQueueRegistry.get(vu.Context(), p.TargetID())
eventHandler := func(event common.PageOnEvent) {
mapping := pageOnEvent.mapp(vu, event)

done := make(chan struct{})

tq.Queue(func() error {
defer close(done)

mapping := mapMetricEvent(vu, event)
_, err := handleEvent(sobek.Undefined(), rt.ToValue(mapping))
_, err := handleEvent(
sobek.Undefined(),
rt.ToValue(mapping),
)
if err != nil {
return fmt.Errorf("executing page.on('metric') handler: %w", err)
return fmt.Errorf("executing page.on('%s') handler: %w", eventName, err)
}

return nil
})
<-done
}

var mapHandler func(common.PageOnEvent)
switch eventName {
case common.EventPageConsoleAPICalled:
mapHandler = onEventPageConsoleAPICalled
case common.EventPageMetricCalled:
mapHandler = onEventPageMetricCalled
default:
return fmt.Errorf("unknown page event: %q", eventName)
if pageOnEvent.wait {
<-done
}
}

if eventName == 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(eventName, eventHandler) //nolint:wrapcheck
}
}

// prepK6BrowserRegExChecker is a helper function to check the regex pattern
// on Sobek runtime. Unlike Go's regexp package, Sobek's runtime checks
// regex patterns using JavaScript's regular expression features.
func prepK6BrowserRegExChecker(rt *sobek.Runtime) func() error {
return func() error {
_, err := rt.RunString(`
function _k6BrowserCheckRegEx(pattern, url) {
return pattern.test(url);
}
`)
if err != nil {
return fmt.Errorf("evaluating regex function: %w", err)
}

return p.On(eventName, mapHandler) //nolint:wrapcheck
return nil
}
}

Expand Down
Loading