or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-vitest--web-worker

Web Worker support for testing in Vitest without requiring JSDom

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@vitest/web-worker@3.2.x

To install, run

npx @tessl/cli install tessl/npm-vitest--web-worker@3.2.0

index.mddocs/

@vitest/web-worker

@vitest/web-worker provides Web Worker support for Vitest testing without requiring JSDom. It simulates Web Worker functionality in the same thread, enabling developers to test web worker code in unit tests while maintaining full compatibility with the Web Worker API.

Package Information

  • Package Name: @vitest/web-worker
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install -D @vitest/web-worker

Core Imports

Main entry (side-effect only, no exports):

import '@vitest/web-worker';

Pure entry for manual setup:

import { defineWebWorkers } from '@vitest/web-worker/pure';

CommonJS (pure entry only):

const { defineWebWorkers } = require('@vitest/web-worker/pure');

Basic Usage

Global Setup

Add to your Vitest configuration for global Web Worker support:

import { defineConfig } from 'vitest/node';

export default defineConfig({
  test: {
    setupFiles: ['@vitest/web-worker'],
  },
});

Test Usage

import '@vitest/web-worker';
import MyWorker from './worker?worker';

const worker = new MyWorker();
worker.postMessage('hello');
worker.onmessage = (e) => {
  console.log(e.data); // 'hello world'
};

// Or with URL-based workers
const worker2 = new Worker(new URL('./worker.ts', import.meta.url));

Architecture

@vitest/web-worker is built around these key components:

  • Automatic Setup: Main entry point automatically configures global Worker/SharedWorker
  • Pure Setup: Manual configuration through defineWebWorkers function
  • Same-thread Simulation: Workers run in the same thread as tests for synchronous testing
  • Message Cloning: Configurable message cloning strategies (native, ponyfill, none)
  • Inline Execution: Uses VitestExecutor to run worker code in isolated contexts

Capabilities

Worker Configuration

Configure Web Worker behavior and message cloning strategies.

/**
 * Defines Worker and SharedWorker constructors on globalThis
 * @param options - Optional configuration for worker behavior
 */
function defineWebWorkers(options?: DefineWorkerOptions): void;

interface DefineWorkerOptions {
  /** Cloning strategy for message passing in Worker communication */
  clone: CloneOption;
}

type CloneOption = 'native' | 'ponyfill' | 'none';

Worker Constructor

Create simulated Web Workers for testing.

/**
 * Web Worker constructor that simulates worker behavior in same thread
 * Available globally after importing '@vitest/web-worker' or calling defineWebWorkers()
 */
declare class Worker extends EventTarget {
  /** Message event handler */
  onmessage: ((event: MessageEvent) => void) | null;
  /** Message error event handler */
  onmessageerror: ((event: MessageEvent) => void) | null;
  /** Error event handler */
  onerror: ((event: ErrorEvent) => void) | null;

  /**
   * Create a new Worker instance
   * @param url - Worker script URL or path
   * @param options - Worker options including name
   */
  constructor(url: URL | string, options?: WorkerOptions);

  /**
   * Send a message to the worker
   * @param data - Data to send
   * @param transferOrOptions - Transfer options or transferable objects
   */
  postMessage(
    data: any,
    transferOrOptions?: StructuredSerializeOptions | Transferable[]
  ): void;

  /** Terminate the worker and clean up resources */
  terminate(): void;
}

interface WorkerOptions {
  /** Optional name for the worker */
  name?: string;
}

SharedWorker Constructor

Create simulated Shared Web Workers for testing.

/**
 * SharedWorker constructor that simulates shared worker behavior
 * Available globally after importing '@vitest/web-worker' or calling defineWebWorkers()
 */
declare class SharedWorker extends EventTarget {
  /** Communication port for the shared worker */
  readonly port: MessagePort;
  /** Error event handler */
  onerror: ((event: ErrorEvent) => void) | null;

  /**
   * Create a new SharedWorker instance
   * @param url - SharedWorker script URL or path
   * @param options - SharedWorker options or name string
   */
  constructor(url: URL | string, options?: WorkerOptions | string);
}

Worker Context API

API available within worker execution context.

/**
 * Dedicated Worker Global Scope - available as 'self' within worker context
 * Compatible with standard DedicatedWorkerGlobalScope interface
 */
interface DedicatedWorkerGlobalScope {
  /** Message event handler from main thread */
  onmessage: ((event: MessageEvent) => void) | null;
  /** Message error event handler */
  onmessageerror: ((event: MessageEvent) => void) | null;
  /** Error event handler */
  onerror: ((event: ErrorEvent) => void) | null;
  /** Worker origin */
  readonly origin: string;
  /** Worker name */
  readonly name: string;
  /** Cross-origin isolation status */
  readonly crossOriginIsolated: boolean;

  /**
   * Post message to main thread
   * @param data - Data to send
   * @param transferOrOptions - Transfer options or transferable objects
   */
  postMessage(
    data: any,
    transferOrOptions?: StructuredSerializeOptions | Transferable[]
  ): void;

  /** Close/terminate the worker */
  close(): void;

  /** Import scripts (not supported in Vite - throws error) */
  importScripts(): never;

  /** Reference to worker global scope */
  readonly self: DedicatedWorkerGlobalScope;
}

/**
 * Shared Worker Global Scope - available as 'self' within shared worker context
 * Compatible with standard SharedWorkerGlobalScope interface
 */
interface SharedWorkerGlobalScope {
  /** Connect event handler when new clients connect */
  onconnect: ((event: MessageEvent) => void) | null;
  /** Worker name */
  readonly name: string;
  /** Worker origin */
  readonly origin: string;
  /** Cross-origin isolation status */
  readonly crossOriginIsolated: boolean;

  /** Close the worker port */
  close(): void;

  /** Import scripts (not supported in Vite - throws error) */
  importScripts(): never;

  /** Reference to shared worker global scope */
  readonly self: SharedWorkerGlobalScope;
}

Supporting Types

Types used by the defineWebWorkers function.

/** Message cloning strategy options */
type CloneOption = 'native' | 'ponyfill' | 'none';

/** Configuration options for defining web workers */
interface DefineWorkerOptions {
  /** Message cloning strategy for Worker communication */
  clone: CloneOption;
}

Configuration

Environment Variables

Configure worker behavior through environment variables:

  • VITEST_WEB_WORKER_CLONE: Set default cloning strategy ('native' | 'ponyfill' | 'none')

Debug Logging

Enable debug logging with the environment variable:

DEBUG=vitest:web-worker

Import Patterns

The package supports various worker import patterns:

// Vite worker imports with ?worker suffix
import MyWorker from './worker?worker';
const worker = new MyWorker();

// Vite shared worker imports with ?sharedworker suffix
import MySharedWorker from './worker?sharedworker';
const sharedWorker = new MySharedWorker();

// URL-based worker creation
const worker = new Worker(new URL('./worker.ts', import.meta.url));
const sharedWorker = new SharedWorker(new URL('./worker.ts', import.meta.url));

Error Handling

  • Worker Initialization Errors: Dispatched as 'error' events on the worker instance
  • Message Errors: Failed message cloning results in 'messageerror' events
  • Import Scripts: Throws error as importScripts is not supported in Vite workers

Usage Examples

Basic Worker Communication

import '@vitest/web-worker';

// worker.ts
self.onmessage = (e) => {
  self.postMessage(`${e.data} world`);
};

// test file
import MyWorker from './worker?worker';

const worker = new MyWorker();
worker.postMessage('hello');
worker.onmessage = (e) => {
  expect(e.data).toBe('hello world');
};

SharedWorker Communication

import '@vitest/web-worker';

// shared-worker.ts
self.onconnect = (e) => {
  const port = e.ports[0];
  port.onmessage = (event) => {
    port.postMessage(event.data);
  };
};

// test file
import MySharedWorker from './shared-worker?sharedworker';

const worker = new MySharedWorker();
worker.port.postMessage('test');
worker.port.onmessage = (e) => {
  expect(e.data).toBe('test');
};

Custom Cloning Configuration

import { defineWebWorkers } from '@vitest/web-worker/pure';

// Configure with no message cloning for performance
defineWebWorkers({ clone: 'none' });

// Configure with ponyfill cloning for compatibility
defineWebWorkers({ clone: 'ponyfill' });

// Configure with native cloning (requires Node 17+)
defineWebWorkers({ clone: 'native' });

Conditional Setup

import { defineWebWorkers } from '@vitest/web-worker/pure';

if (process.env.SUPPORT_WORKERS) {
  defineWebWorkers({ clone: 'none' });
}

Notes

  • Worker does not support onmessage = () => {}. Use self.onmessage = () => {} instead
  • Shared worker does not support onconnect = () => {}. Use self.onconnect = () => {} instead
  • Transferring Buffer will not change its byteLength
  • Workers have access to the same global space as tests
  • Requires Node 17+ for native structuredClone, otherwise falls back to polyfill or no cloning