vitest icon indicating copy to clipboard operation
vitest copied to clipboard

console.log from webworker, when using browser runner, does not get captured

Open jbms opened this issue 10 months ago • 4 comments

Describe the bug

When using the browser runner, if a test starts a webworker, and that worker calls console.log, the log messages are not shown as stdout like log messages from the main thread.

The only way I've found to see the log messages is to run in non-headless mode and open the JavaScript console.

Reproduction

https://stackblitz.com/edit/vitest-dev-vitest-oszejgfz?file=test%2Fbasic.test.ts

System Info

N/A

See stackblitz reproduction.

Used Package Manager

npm

Validations

jbms avatar Feb 06 '25 19:02 jbms

EDIT: I mixed up and I initially posted the comment https://github.com/vitest-dev/vitest/issues/7440#issuecomment-2641581817 here.

Similar to https://github.com/vitest-dev/vitest/issues/7440, here is node worker thread version https://stackblitz.com/edit/vitest-dev-vitest-q9nkjrvd?file=test%2Fbasic.test.ts.

This is also a fundamental difference how main and worker tied together on browser and on node. For node worker threads, stdio are reused, so console log shows up automatically in the terminal. To support something similar for browser worker, we need an extra mechanism to send log from worker to main.


Here is an example to proxy logs and unhandled errors using BroadcastChannel https://stackblitz.com/edit/vitest-dev-vitest-yzjj5amy?file=test%2Fbasic.test.ts

// worker.js
const channel = new BroadcastChannel('my-worker-channel');
self.onerror = (event) => {
  channel.postMessage({ type: 'error', payload: event });
};
console.log = (...args) => {
  channel.postMessage({ type: 'log', payload: args });
};

// basic.test.js
const channel = new BroadcastChannel('my-worker-channel');
channel.onmessage = (event) => {
  if (event.data.type === 'error') {
    throw new Error('[Worker error] ' + event.data.payload);
  }
  if (event.data.type === 'log') {
    console.log('[Worker console]', ...event.data.payload);
  }
};
 chromium  Browser runner started at http://localhost:63315/

...
stdout | test/basic.test.ts
[Worker console] Hello
...
 ✓  chromium  test/basic.test.ts (1 test) 32ms
   ✓ create worker
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Error: [Worker error] Uncaught Error: Some error not caught
 ❯ channel.onmessage test/basic.test.ts:8:12

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

hi-ogawa avatar Feb 07 '25 01:02 hi-ogawa

Is there a way that vitest can inject some code to do that in every worker automatically? Maybe by monkey patching the Worker global?

jbms avatar Feb 07 '25 04:02 jbms

Yeah, that's probably the plan. Vite uses a special url for worker entry file and we already do this:

https://github.com/vitest-dev/vitest/blob/c82387d36f7dc0cf8f558b96bf97f782b0b3637a/packages/browser/src/node/plugin.ts#L398-L411

hi-ogawa avatar Feb 07 '25 05:02 hi-ogawa

This issue has been stalled for 3 months, and is complicating our migration to vitest browser mode. We are using vitest-fail-on-console to throw an error on every console.error. I maintain a design system, and do not want console errors to go unnoticed during my unit tests, otherwise client applications will get them too. With my only error message being "null", I have to spy console.error just to add a debugger statement so I can go back in the stack trace to find the original error.

It doesn't happen on every console error, but this time it was "ResizeObserver loop completed with undelivered notifications."