From 63b072a8ce23dcab070d54bb2fd1e7c6c07b998b Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Thu, 6 Jan 2022 16:33:43 +0200 Subject: [PATCH] Add PromiseRejectionHandler This just logs every time a promise gets rejected but has not reject handler (yet). The message is basically the one firefox gives. The simplest variant of #2318 --- js/bundle.go | 22 ++++++++++++++++++++++ js/bundle_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/js/bundle.go b/js/bundle.go index 39bf259f6ff..00c2bff7303 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -307,6 +307,28 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * } rt.Set("__ENV", env) rt.Set("__VU", vuID) + rt.SetPromiseRejectionTracker(func(p *goja.Promise, op goja.PromiseRejectionOperation) { + // TODO this only handles the case where a promise get's rejected without having a reject handler added. + // But the other case where for an already rejected promise (one that this call has already handled) + // there is now a reject handler attached is not handled. + // + // More complete implementation might need to wait for some time, the end of an iteration, + // some other event before logging a "rejection" that never got "handled" + // + // Read Notes on https://tc39.es/ecma262/#sec-host-promise-rejection-tracker + // see issue https://github.com/grafana/k6/issues/2318 for more info + if op == goja.PromiseRejectionReject { + frames := rt.CaptureCallStack(20, nil) + buf := &bytes.Buffer{} + for _, frame := range frames { + frame.Write(buf) + buf.WriteRune('\n') + } + + logger.Warnf("Uncaught (in Promise) %s\n %s", p.Result(), buf.String()) + } + }) + rt.Set("console", common.Bind(rt, newConsole(logger), init.ctxPtr)) if init.compatibilityMode == lib.CompatibilityModeExtended { diff --git a/js/bundle_test.go b/js/bundle_test.go index 56bb107bfd1..531a5276cf5 100644 --- a/js/bundle_test.go +++ b/js/bundle_test.go @@ -976,3 +976,33 @@ func TestBundleMakeArchive(t *testing.T) { }) } } + +func TestPromiseRejectionHandler(t *testing.T) { + t.Parallel() + logger := logrus.New() + logger.SetLevel(logrus.InfoLevel) + logger.Out = ioutil.Discard + hook := testutils.SimpleLogrusHook{ + HookedLevels: []logrus.Level{logrus.WarnLevel, logrus.InfoLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel}, + } + logger.AddHook(&hook) + _, err := getSimpleBundle(t, "/script.js", ` + export let options = { + vus: 5, + teardownTimeout: '1s', + }; + let val = true; + Promise.reject("here").catch((l) => console.log("caught", l)); + export default function() {} + `, logger) + require.NoError(t, err) + + entries := hook.Drain() + require.Len(t, entries, 2) + first := entries[0] + assert.Equal(t, logrus.WarnLevel, first.Level) + assert.Contains(t, first.Message, "Uncaught (in Promise) here") + second := entries[1] + assert.Equal(t, logrus.InfoLevel, second.Level) + assert.Contains(t, second.Message, "caught here") +}