@rstest/browser
Advanced tools
Sorry, the diff of this file is too big to display
| /*! LICENSE: 3.d3d6c6e4cf.js.LICENSE.txt */ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| /*! LICENSE: lib-react.62b27a21db.js.LICENSE.txt */ |
+2
-1
@@ -6,2 +6,3 @@ const DISPATCH_MESSAGE_TYPE = '__rstest_dispatch__'; | ||
| const DISPATCH_NAMESPACE_BROWSER = 'browser'; | ||
| export { DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER, DISPATCH_RESPONSE_TYPE, DISPATCH_RPC_BRIDGE_NAME, DISPATCH_RPC_REQUEST_TYPE }; | ||
| const DISPATCH_NAMESPACE_SNAPSHOT = 'snapshot'; | ||
| export { DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER, DISPATCH_NAMESPACE_SNAPSHOT, DISPATCH_RESPONSE_TYPE, DISPATCH_RPC_BRIDGE_NAME, DISPATCH_RPC_REQUEST_TYPE }; |
@@ -15,3 +15,3 @@ <!doctype html> | ||
| </script> | ||
| <script defer src="/container-static/js/lib-react.5ae9b90ee5.js"></script><script defer src="/container-static/js/27.099feaf1da.js"></script><script defer src="/container-static/js/index.be8c8d2626.js"></script><link href="/container-static/css/index.5c72297783.css" rel="stylesheet"></head> | ||
| <script defer src="/container-static/js/lib-react.62b27a21db.js"></script><script defer src="/container-static/js/3.d3d6c6e4cf.js"></script><script defer src="/container-static/js/index.8fb1588e4a.js"></script><link href="/container-static/css/index.5c72297783.css" rel="stylesheet"></head> | ||
| <body> | ||
@@ -18,0 +18,0 @@ <div id="root"></div> |
+4
-4
@@ -1,2 +0,2 @@ | ||
| import { DISPATCH_RESPONSE_TYPE, DISPATCH_RPC_REQUEST_TYPE, DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER } from "./626.js"; | ||
| import { DISPATCH_RESPONSE_TYPE, DISPATCH_RPC_BRIDGE_NAME as protocol_DISPATCH_RPC_BRIDGE_NAME, DISPATCH_RPC_REQUEST_TYPE as protocol_DISPATCH_RPC_REQUEST_TYPE, DISPATCH_MESSAGE_TYPE as protocol_DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER } from "./626.js"; | ||
| const DEFAULT_RPC_TIMEOUT_MS = 30000; | ||
@@ -37,3 +37,3 @@ const getRpcTimeout = ()=>window.__RSTEST_BROWSER_OPTIONS__?.rpcTimeout ?? DEFAULT_RPC_TIMEOUT_MS; | ||
| if (window.parent === window) { | ||
| const dispatchBridge = window.__rstest_dispatch_rpc__; | ||
| const dispatchBridge = window[protocol_DISPATCH_RPC_BRIDGE_NAME]; | ||
| if (!dispatchBridge) throw new Error('Dispatch RPC bridge is not available in top-level runner.'); | ||
@@ -72,5 +72,5 @@ return new Promise((resolve, reject)=>{ | ||
| window.parent.postMessage({ | ||
| type: DISPATCH_MESSAGE_TYPE, | ||
| type: protocol_DISPATCH_MESSAGE_TYPE, | ||
| payload: { | ||
| type: DISPATCH_RPC_REQUEST_TYPE, | ||
| type: protocol_DISPATCH_RPC_REQUEST_TYPE, | ||
| payload: request | ||
@@ -77,0 +77,0 @@ } |
| import type { BrowserDispatchRequest } from '../protocol.js'; | ||
| export declare const getRpcTimeout: () => number; | ||
| export declare const createRequestId: (prefix: string) => string; | ||
| /** | ||
| * Build a runner-lifecycle dispatch request. | ||
| * | ||
| * Lifecycle events (`file-ready`, `suite-start`, `suite-result`, `case-start`) | ||
| * share the dispatch-rpc envelope but are delivered fire-and-forget via | ||
| * {@link sendRunnerLifecycle}, so the request id only needs to be unique — it is | ||
| * produced by the shared {@link createRequestId} factory rather than a bespoke | ||
| * per-runner counter. | ||
| */ | ||
| export declare const createRunnerLifecycleRequest: (method: string, args: unknown) => BrowserDispatchRequest; | ||
| /** | ||
| * Deliver a runner-lifecycle request fire-and-forget. | ||
| * | ||
| * Unlike {@link dispatchRpc}, this never awaits, unwraps, id-matches, or times | ||
| * out: the host echoes a response but the runner ignores it. Failures surface | ||
| * only through the optional `onError` hook (debug logging at the call site), | ||
| * keeping the hot test loop non-blocking. | ||
| */ | ||
| export declare const sendRunnerLifecycle: (request: BrowserDispatchRequest, onError?: (error: unknown) => void) => void; | ||
| export declare const dispatchRpc: <T>({ requestId, request, timeoutMs, timeoutMessage, staleMessage, }: { | ||
@@ -5,0 +24,0 @@ requestId: string; |
| /** | ||
| * Re-export runtime API from @rstest/core/browser-runtime for browser use. | ||
| * Re-export runtime API from @rstest/core/internal/browser-runtime for browser use. | ||
| * This file is used as an alias target for '@rstest/core' in browser mode. | ||
| * | ||
| * Uses @rstest/core/browser-runtime which only exports the test APIs | ||
| * Uses @rstest/core/internal/browser-runtime which only exports the test APIs | ||
| * (describe, it, expect, etc.) without any Node.js dependencies. | ||
| */ | ||
| export type { Assertion, Mock } from '@rstest/core'; | ||
| export * from '@rstest/core/browser-runtime'; | ||
| export * from '@rstest/core/internal/browser-runtime'; |
@@ -23,3 +23,3 @@ /** | ||
| * This is essential for inline snapshot support because the snapshot code | ||
| * runs in runner.js (which contains @rstest/core/browser-runtime). | ||
| * runs in runner.js (which contains @rstest/core/internal/browser-runtime). | ||
| * Without this, stack traces from inline snapshots cannot be mapped back | ||
@@ -26,0 +26,0 @@ * to the original source files. |
@@ -1,2 +0,4 @@ | ||
| import type { Rstest } from '@rstest/core/browser'; | ||
| import { getNumCpus, parseWorkers } from '@rstest/core/internal/browser'; | ||
| import type { Rstest } from '@rstest/core/internal/browser'; | ||
| export { getNumCpus, parseWorkers }; | ||
| type HeadlessConcurrencyContext = Pick<Rstest, 'command'> & { | ||
@@ -9,5 +11,3 @@ normalizedConfig: { | ||
| }; | ||
| export declare const parseWorkers: (maxWorkers: string | number, numCpus?: number) => number; | ||
| export declare const resolveDefaultHeadlessWorkers: (command: HeadlessConcurrencyContext['command'], numCpus?: number) => number; | ||
| export declare const getHeadlessConcurrency: (context: HeadlessConcurrencyContext, totalTests: number) => number; | ||
| export {}; |
@@ -1,2 +0,2 @@ | ||
| import type { Rstest } from '@rstest/core/browser'; | ||
| export declare const validateBrowserConfig: (context: Rstest) => void; | ||
| import type { RstestContext } from '@rstest/core/internal/browser'; | ||
| export declare const validateBrowserConfig: (context: RstestContext) => void; |
@@ -1,2 +0,2 @@ | ||
| import type { Reporter } from '@rstest/core/browser'; | ||
| import type { Reporter } from '@rstest/core/internal/browser'; | ||
| import { HostDispatchRouter } from './dispatchRouter.js'; | ||
@@ -3,0 +3,0 @@ import type { BrowserClientMessage, BrowserDispatchHandler, SnapshotRpcRequest } from './protocol.js'; |
@@ -1,2 +0,2 @@ | ||
| import { type BrowserTestRunOptions, type BrowserTestRunResult, type ListCommandResult, type Rstest } from '@rstest/core/browser'; | ||
| import { type BrowserTestRunOptions, type BrowserTestRunResult, type ListCommandResult, type RstestContext } from '@rstest/core/internal/browser'; | ||
| type LazyCompilationModule = { | ||
@@ -29,3 +29,3 @@ nameForCondition?: () => string | null | undefined; | ||
| }; | ||
| export declare const runBrowserController: (context: Rstest, options?: BrowserTestRunOptions) => Promise<BrowserTestRunResult | void>; | ||
| export declare const runBrowserController: (context: RstestContext, options?: BrowserTestRunOptions) => Promise<BrowserTestRunResult | void>; | ||
| /** | ||
@@ -45,3 +45,3 @@ * Result from collecting browser tests. | ||
| */ | ||
| export declare const listBrowserTests: (context: Rstest, options?: { | ||
| export declare const listBrowserTests: (context: RstestContext, options?: { | ||
| shardedEntries?: Map<string, { | ||
@@ -48,0 +48,0 @@ entries: Record<string, string>; |
+5
-5
@@ -1,7 +0,7 @@ | ||
| import type { BrowserTestRunOptions, BrowserTestRunResult, Rstest } from '@rstest/core/browser'; | ||
| import type { BrowserTestRunOptions, BrowserTestRunResult, RstestContext } from '@rstest/core/internal/browser'; | ||
| import { validateBrowserConfig } from './configValidation.js'; | ||
| import { type ListBrowserTestsResult } from './hostController.js'; | ||
| export { validateBrowserConfig } from './configValidation.js'; | ||
| export { BROWSER_VIEWPORT_PRESET_DIMENSIONS, BROWSER_VIEWPORT_PRESET_IDS, resolveBrowserViewportPreset, } from './viewportPresets.js'; | ||
| export declare function runBrowserTests(context: Rstest, options?: BrowserTestRunOptions): Promise<BrowserTestRunResult | void>; | ||
| export declare function listBrowserTests(context: Rstest): Promise<ListBrowserTestsResult>; | ||
| export { validateBrowserConfig }; | ||
| export declare function runBrowserTests(context: RstestContext, options?: BrowserTestRunOptions): Promise<BrowserTestRunResult | void>; | ||
| export declare function listBrowserTests(context: RstestContext, options?: Pick<BrowserTestRunOptions, 'shardedEntries'>): Promise<ListBrowserTestsResult>; | ||
| export type { BrowserTestRunOptions, BrowserTestRunResult, ListBrowserTestsResult, }; |
+45
-18
@@ -1,5 +0,5 @@ | ||
| import type { DevicePreset } from '@rstest/core/browser'; | ||
| import type { RuntimeConfig, TestFileResult, TestInfo, TestResult } from '@rstest/core/browser-runtime'; | ||
| import type { BrowserViewport } from '@rstest/core/internal/browser'; | ||
| import type { RuntimeConfig, TestFileResult, TestInfo, TestResult } from '@rstest/core/internal/browser-runtime'; | ||
| import type { SnapshotUpdateState } from '@vitest/snapshot'; | ||
| export type { BrowserLocatorIR, BrowserRpcRequest, SnapshotRpcRequest, } from './rpcProtocol.js'; | ||
| export type { BrowserLocatorIR, BrowserRpcRequest, SnapshotRpcCall, SnapshotRpcMethod, SnapshotRpcMethodArgs, SnapshotRpcRequest, } from './rpcProtocol.js'; | ||
| export { validateBrowserRpcRequest } from './rpcProtocol.js'; | ||
@@ -16,6 +16,3 @@ export declare const DISPATCH_MESSAGE_TYPE = "__rstest_dispatch__"; | ||
| export type SerializedRuntimeConfig = RuntimeConfig; | ||
| export type BrowserViewport = { | ||
| width: number; | ||
| height: number; | ||
| } | DevicePreset; | ||
| export type { BrowserViewport }; | ||
| export type BrowserProjectRuntime = { | ||
@@ -42,2 +39,20 @@ name: string; | ||
| export type BrowserExecutionMode = 'run' | 'collect'; | ||
| /** | ||
| * Wire shape of a `log` client message payload. The host receives this and maps | ||
| * it onto core's {@link UserConsoleLog} (notably `level` → `name`); that mapper | ||
| * (`hostController.ts` `handleLog`) annotates its result as `UserConsoleLog`, so | ||
| * the map→core direction is compiler-checked. Owning the wire shape here as one | ||
| * named type keeps the host's input type from drifting away from the producer. | ||
| */ | ||
| export type BrowserLogPayload = { | ||
| level: 'log' | 'warn' | 'error' | 'info' | 'debug'; | ||
| content: string; | ||
| taskId?: string; | ||
| taskName?: string; | ||
| taskParentNames?: string[]; | ||
| taskType?: 'file' | 'suite' | 'case'; | ||
| testPath: string; | ||
| type: 'stdout' | 'stderr'; | ||
| trace?: string; | ||
| }; | ||
| export type BrowserHostConfig = { | ||
@@ -93,13 +108,3 @@ rootPath: string; | ||
| type: 'log'; | ||
| payload: { | ||
| level: 'log' | 'warn' | 'error' | 'info' | 'debug'; | ||
| content: string; | ||
| taskId?: string; | ||
| taskName?: string; | ||
| taskParentNames?: string[]; | ||
| taskType?: 'file' | 'suite' | 'case'; | ||
| testPath: string; | ||
| type: 'stdout' | 'stderr'; | ||
| trace?: string; | ||
| }; | ||
| payload: BrowserLogPayload; | ||
| } | { | ||
@@ -127,2 +132,24 @@ type: 'fatal'; | ||
| /** | ||
| * Lifecycle methods the runner emits via `dispatchRunnerLifecycle()` as | ||
| * dispatch-rpc-requests on the `runner` namespace (as opposed to the | ||
| * {@link BrowserClientMessage} types it `send()`s). The runner client imports | ||
| * this instead of redeclaring the list, so the emit site cannot drift from the | ||
| * host router. | ||
| */ | ||
| export type RunnerLifecycleMethod = 'file-ready' | 'suite-start' | 'suite-result' | 'case-start'; | ||
| /** | ||
| * {@link BrowserClientMessage} types that are forwarded to the `runner` | ||
| * namespace (by message `type`) rather than handled at the transport layer. | ||
| * `Extract` keeps this a checked subset of the message union — renaming a | ||
| * message type drops it here, surfacing as a missing handler downstream. | ||
| */ | ||
| type RunnerMessageMethod = Extract<BrowserClientMessage['type'], 'file-start' | 'case-result' | 'file-complete' | 'log' | 'fatal'>; | ||
| /** | ||
| * Single source of truth for every method handled by the `runner` dispatch | ||
| * namespace. The host handler table is keyed by this union (a missing key is a | ||
| * compile error), so adding a runner method here forces a matching handler and | ||
| * cannot silently no-op at runtime. | ||
| */ | ||
| export type RunnerDispatchMethod = RunnerLifecycleMethod | RunnerMessageMethod; | ||
| /** | ||
| * Transport-agnostic envelope used by host routing. | ||
@@ -129,0 +156,0 @@ * `namespace + method + args + target` describes an operation independent of |
@@ -0,1 +1,2 @@ | ||
| import type { BrowserProvider } from '@rstest/core/internal/browser'; | ||
| import type { BrowserRpcRequest } from '../rpcProtocol.js'; | ||
@@ -15,4 +16,10 @@ /** | ||
| * - prefer direct passthrough to provider APIs over provider-specific translation | ||
| * | ||
| * The provider-name union is owned by `@rstest/core` (see `BROWSER_PROVIDERS`) | ||
| * because core's CLI `init` templates need it but cannot import this package | ||
| * (peer-dependency direction). `providerImplementations` below is keyed by the | ||
| * re-exported union, so a provider added in core without an implementation here | ||
| * is a compile error. | ||
| */ | ||
| export type BrowserProvider = 'playwright'; | ||
| export type { BrowserProvider }; | ||
| /** Minimal console shape needed by host logging bridge. */ | ||
@@ -19,0 +26,0 @@ export type BrowserConsoleMessage = { |
+32
-18
@@ -104,30 +104,44 @@ export type BrowserLocatorText = { | ||
| /** | ||
| * Snapshot RPC request from runner iframe. | ||
| * The container will forward these to the host via WebSocket RPC. | ||
| * Single source of truth for snapshot RPC methods and their argument shapes. | ||
| * The request union, the client sender, both host-side mappers, and every | ||
| * exhaustiveness check derive from this map — adding or renaming a method here | ||
| * forces a compile error at every site that has not been updated. | ||
| */ | ||
| export type SnapshotRpcRequest = { | ||
| id: string; | ||
| method: 'resolveSnapshotPath'; | ||
| args: { | ||
| export type SnapshotRpcMethodArgs = { | ||
| resolveSnapshotPath: { | ||
| testPath: string; | ||
| }; | ||
| } | { | ||
| id: string; | ||
| method: 'readSnapshotFile'; | ||
| args: { | ||
| readSnapshotFile: { | ||
| filepath: string; | ||
| }; | ||
| } | { | ||
| id: string; | ||
| method: 'saveSnapshotFile'; | ||
| args: { | ||
| saveSnapshotFile: { | ||
| filepath: string; | ||
| content: string; | ||
| }; | ||
| } | { | ||
| id: string; | ||
| method: 'removeSnapshotFile'; | ||
| args: { | ||
| removeSnapshotFile: { | ||
| filepath: string; | ||
| }; | ||
| }; | ||
| export type SnapshotRpcMethod = keyof SnapshotRpcMethodArgs; | ||
| /** | ||
| * Snapshot RPC request from runner iframe. | ||
| * The container will forward these to the host via WebSocket RPC. | ||
| */ | ||
| export type SnapshotRpcRequest = { | ||
| [M in SnapshotRpcMethod]: { | ||
| id: string; | ||
| method: M; | ||
| args: SnapshotRpcMethodArgs[M]; | ||
| }; | ||
| }[SnapshotRpcMethod]; | ||
| /** | ||
| * Client-side snapshot RPC call (request without the transport `id`). | ||
| * Distributive over {@link SnapshotRpcMethod} so `method` and `args` stay paired | ||
| * at the call site — a mismatched pair fails to compile. | ||
| */ | ||
| export type SnapshotRpcCall = { | ||
| [M in SnapshotRpcMethod]: { | ||
| method: M; | ||
| args: SnapshotRpcMethodArgs[M]; | ||
| }; | ||
| }[SnapshotRpcMethod]; |
@@ -7,2 +7,7 @@ /** | ||
| * autocomplete and runtime validation stay consistent. | ||
| * | ||
| * Upstream source of truth for ids/dimensions is the Chrome DevTools emulated | ||
| * device list — mirror each device's `screen.vertical` `{ width, height }`: | ||
| * https://github.com/ChromeDevTools/devtools-frontend/blob/main/front_end/models/emulation/EmulatedDevices.ts | ||
| * Last verified in sync (all 17): 2026-04-22, devtools-frontend commit abaac05e. | ||
| */ | ||
@@ -9,0 +14,0 @@ export declare const BROWSER_VIEWPORT_PRESET_IDS: readonly ['iPhoneSE', 'iPhoneXR', 'iPhone12Pro', 'iPhone14ProMax', 'Pixel7', 'SamsungGalaxyS8Plus', 'SamsungGalaxyS20Ultra', 'iPadMini', 'iPadAir', 'iPadPro', 'SurfacePro7', 'SurfaceDuo', 'GalaxyZFold5', 'AsusZenbookFold', 'SamsungGalaxyA51A71', 'NestHub', 'NestHubMax']; |
+6
-6
| { | ||
| "name": "@rstest/browser", | ||
| "version": "0.10.3", | ||
| "version": "0.10.4", | ||
| "description": "Browser mode support for Rstest testing framework.", | ||
@@ -51,13 +51,13 @@ "keywords": [ | ||
| "devDependencies": { | ||
| "@rslib/core": "0.21.5", | ||
| "@rslib/core": "0.22.0", | ||
| "@types/convert-source-map": "^2.0.3", | ||
| "@types/picomatch": "^4.0.3", | ||
| "@types/ws": "^8.18.1", | ||
| "@typescript/native-preview": "7.0.0-dev.20260527.1", | ||
| "@vitest/snapshot": "^3.2.4", | ||
| "@typescript/native-preview": "7.0.0-dev.20260608.1", | ||
| "@vitest/snapshot": "^3.2.6", | ||
| "birpc": "^4.0.0", | ||
| "picomatch": "^4.0.4", | ||
| "playwright": "^1.60.0", | ||
| "@rstest/core": "0.10.4", | ||
| "@rstest/browser-ui": "0.0.0", | ||
| "@rstest/core": "0.10.3", | ||
| "@rstest/tsconfig": "0.0.1" | ||
@@ -67,3 +67,3 @@ }, | ||
| "playwright": "^1.49.1", | ||
| "@rstest/core": "^0.10.3" | ||
| "@rstest/core": "^0.10.4" | ||
| }, | ||
@@ -70,0 +70,0 @@ "peerDependenciesMeta": { |
+7
-1
@@ -18,2 +18,5 @@ # Browser mode host architecture | ||
| LS["headlessLatestRerunScheduler.ts"] | ||
| HT["headlessTransport.ts\nattachHeadlessRunnerTransport()"] | ||
| CC["concurrency.ts\ngetHeadlessConcurrency()"] | ||
| HQ["headedSerialTaskQueue.ts\ncreateHeadedSerialTaskQueue()"] | ||
| end | ||
@@ -39,2 +42,5 @@ | ||
| RL --> SR | ||
| HC --> CC | ||
| HC --> HQ | ||
| HC --> HT | ||
@@ -47,3 +53,3 @@ UR <--> HC | ||
| HC -."headless bridge:\nexposeFunction(__rstest_dispatch__, __rstest_dispatch_rpc__)".-> Runner | ||
| HT -."headless bridge:\nexposeFunction(__rstest_dispatch__, __rstest_dispatch_rpc__)".-> Runner | ||
| ``` | ||
@@ -50,0 +56,0 @@ |
@@ -7,6 +7,11 @@ import type { | ||
| DISPATCH_MESSAGE_TYPE, | ||
| DISPATCH_NAMESPACE_RUNNER, | ||
| DISPATCH_RESPONSE_TYPE, | ||
| DISPATCH_RPC_BRIDGE_NAME, | ||
| DISPATCH_RPC_REQUEST_TYPE, | ||
| } from '../protocol'; | ||
| // Coincidentally equal to the host-side RUNNER_FRAMES_READY_TIMEOUT_MS and the | ||
| // runner's CONFIG_WAIT_TIMEOUT_MS (entry.ts), but a semantically distinct | ||
| // default in a different runtime, so deliberately not shared with them. | ||
| const DEFAULT_RPC_TIMEOUT_MS = 30_000; | ||
@@ -41,2 +46,59 @@ | ||
| /** | ||
| * Build a runner-lifecycle dispatch request. | ||
| * | ||
| * Lifecycle events (`file-ready`, `suite-start`, `suite-result`, `case-start`) | ||
| * share the dispatch-rpc envelope but are delivered fire-and-forget via | ||
| * {@link sendRunnerLifecycle}, so the request id only needs to be unique — it is | ||
| * produced by the shared {@link createRequestId} factory rather than a bespoke | ||
| * per-runner counter. | ||
| */ | ||
| export const createRunnerLifecycleRequest = ( | ||
| method: string, | ||
| args: unknown, | ||
| ): BrowserDispatchRequest => ({ | ||
| requestId: createRequestId('runner-lifecycle'), | ||
| namespace: DISPATCH_NAMESPACE_RUNNER, | ||
| method, | ||
| args, | ||
| }); | ||
| /** | ||
| * Deliver a runner-lifecycle request fire-and-forget. | ||
| * | ||
| * Unlike {@link dispatchRpc}, this never awaits, unwraps, id-matches, or times | ||
| * out: the host echoes a response but the runner ignores it. Failures surface | ||
| * only through the optional `onError` hook (debug logging at the call site), | ||
| * keeping the hot test loop non-blocking. | ||
| */ | ||
| export const sendRunnerLifecycle = ( | ||
| request: BrowserDispatchRequest, | ||
| onError?: (error: unknown) => void, | ||
| ): void => { | ||
| if (window.parent === window) { | ||
| const dispatchBridge = window[DISPATCH_RPC_BRIDGE_NAME]; | ||
| if (!dispatchBridge) { | ||
| onError?.( | ||
| new Error('Dispatch RPC bridge is not available in top-level runner.'), | ||
| ); | ||
| return; | ||
| } | ||
| void Promise.resolve(dispatchBridge(request)).catch((error: unknown) => { | ||
| onError?.(error); | ||
| }); | ||
| return; | ||
| } | ||
| window.parent.postMessage( | ||
| { | ||
| type: DISPATCH_MESSAGE_TYPE, | ||
| payload: { | ||
| type: DISPATCH_RPC_REQUEST_TYPE, | ||
| payload: request, | ||
| }, | ||
| }, | ||
| '*', | ||
| ); | ||
| }; | ||
| const isDispatchResponse = ( | ||
@@ -121,3 +183,3 @@ value: unknown, | ||
| if (window.parent === window) { | ||
| const dispatchBridge = window.__rstest_dispatch_rpc__; | ||
| const dispatchBridge = window[DISPATCH_RPC_BRIDGE_NAME]; | ||
| if (!dispatchBridge) { | ||
@@ -124,0 +186,0 @@ throw new Error( |
+25
-58
@@ -15,3 +15,3 @@ import { | ||
| WorkerState, | ||
| } from '@rstest/core/browser-runtime'; | ||
| } from '@rstest/core/internal/browser-runtime'; | ||
| import { | ||
@@ -21,16 +21,17 @@ createBrowserTaskContext, | ||
| globalApis, | ||
| RSTEST_ENV_SYMBOL_KEY, | ||
| setRealTimers, | ||
| } from '@rstest/core/browser-runtime'; | ||
| unwrapRegex, | ||
| } from '@rstest/core/internal/browser-runtime'; | ||
| import { normalize } from 'pathe'; | ||
| import type { | ||
| BrowserClientMessage, | ||
| BrowserDispatchRequest, | ||
| BrowserProjectRuntime, | ||
| RunnerLifecycleMethod, | ||
| } from '../protocol'; | ||
| import { DISPATCH_MESSAGE_TYPE, RSTEST_CONFIG_MESSAGE_TYPE } from '../protocol'; | ||
| import { | ||
| DISPATCH_MESSAGE_TYPE, | ||
| DISPATCH_NAMESPACE_RUNNER, | ||
| DISPATCH_RPC_REQUEST_TYPE, | ||
| RSTEST_CONFIG_MESSAGE_TYPE, | ||
| } from '../protocol'; | ||
| createRunnerLifecycleRequest, | ||
| sendRunnerLifecycle, | ||
| } from './dispatchTransport'; | ||
| import { BrowserSnapshotEnvironment } from './snapshot'; | ||
@@ -49,10 +50,2 @@ import { | ||
| type RunnerLifecycleMethod = | ||
| | 'file-ready' | ||
| | 'suite-start' | ||
| | 'suite-result' | ||
| | 'case-start'; | ||
| let runnerDispatchRequestId = 0; | ||
| /** | ||
@@ -69,3 +62,3 @@ * Debug logger for browser client. | ||
| type RuntimeEnvStore = Record<string, string | undefined>; | ||
| const RSTEST_ENV_SYMBOL = Symbol.for('rstest.env'); | ||
| const RSTEST_ENV_SYMBOL = Symbol.for(RSTEST_ENV_SYMBOL_KEY); | ||
@@ -77,16 +70,2 @@ type GlobalWithRuntimeEnv = typeof globalThis & | ||
| const REGEXP_FLAG_PREFIX = 'RSTEST_REGEXP:'; | ||
| const unwrapRegex = (value: string): string | RegExp => { | ||
| if (value.startsWith(REGEXP_FLAG_PREFIX)) { | ||
| const raw = value.slice(REGEXP_FLAG_PREFIX.length); | ||
| const match = raw.match(/^\/(.+)\/([gimuy]*)$/); | ||
| if (match) { | ||
| const [, pattern, flags] = match; | ||
| return new RegExp(pattern!, flags); | ||
| } | ||
| } | ||
| return value; | ||
| }; | ||
| const restoreRuntimeConfig = ( | ||
@@ -238,3 +217,3 @@ config: BrowserProjectRuntime['runtimeConfig'], | ||
| // Note: This binding may not exist if not using Playwright | ||
| window.__rstest_dispatch__?.(message); | ||
| window[DISPATCH_MESSAGE_TYPE]?.(message); | ||
| }; | ||
@@ -246,31 +225,19 @@ | ||
| ): void => { | ||
| const request: BrowserDispatchRequest = { | ||
| requestId: `runner-lifecycle-${++runnerDispatchRequestId}`, | ||
| namespace: DISPATCH_NAMESPACE_RUNNER, | ||
| method, | ||
| args: payload, | ||
| }; | ||
| if (window.parent === window) { | ||
| const dispatchBridge = window.__rstest_dispatch_rpc__; | ||
| if (!dispatchBridge) { | ||
| debugLog( | ||
| '[Runner] Missing dispatch bridge for lifecycle method:', | ||
| method, | ||
| ); | ||
| return; | ||
| } | ||
| void Promise.resolve(dispatchBridge(request)).catch((error: unknown) => { | ||
| sendRunnerLifecycle( | ||
| createRunnerLifecycleRequest(method, payload), | ||
| (error: unknown) => { | ||
| debugLog('[Runner] Failed to dispatch lifecycle method:', method, error); | ||
| }); | ||
| return; | ||
| } | ||
| send({ | ||
| type: DISPATCH_RPC_REQUEST_TYPE, | ||
| payload: request, | ||
| }); | ||
| }, | ||
| ); | ||
| }; | ||
| /** Timeout for waiting for browser config from container (30 seconds) */ | ||
| /** | ||
| * Timeout for waiting for browser config from container (30 seconds). | ||
| * | ||
| * Coincidentally equal to the RPC default (client/dispatchTransport.ts) and the | ||
| * host's RUNNER_FRAMES_READY_TIMEOUT_MS (hostController.ts), but semantically | ||
| * distinct and in a different runtime, so deliberately not shared. Implicit | ||
| * invariant: this must not exceed the host's frames-ready timeout, or the host | ||
| * declares the runner un-ready before it can even receive its config. | ||
| */ | ||
| const CONFIG_WAIT_TIMEOUT_MS = 30_000; | ||
@@ -277,0 +244,0 @@ |
| /** | ||
| * Re-export runtime API from @rstest/core/browser-runtime for browser use. | ||
| * Re-export runtime API from @rstest/core/internal/browser-runtime for browser use. | ||
| * This file is used as an alias target for '@rstest/core' in browser mode. | ||
| * | ||
| * Uses @rstest/core/browser-runtime which only exports the test APIs | ||
| * Uses @rstest/core/internal/browser-runtime which only exports the test APIs | ||
| * (describe, it, expect, etc.) without any Node.js dependencies. | ||
@@ -12,2 +12,2 @@ */ | ||
| // Re-export all public test APIs | ||
| export * from '@rstest/core/browser-runtime'; | ||
| export * from '@rstest/core/internal/browser-runtime'; |
+24
-22
@@ -1,2 +0,2 @@ | ||
| import type { BrowserDispatchRequest, SnapshotRpcRequest } from '../protocol'; | ||
| import type { BrowserDispatchRequest, SnapshotRpcCall } from '../protocol'; | ||
| import { DISPATCH_NAMESPACE_SNAPSHOT } from '../protocol'; | ||
@@ -14,4 +14,3 @@ import { | ||
| requestId: string, | ||
| method: SnapshotRpcRequest['method'], | ||
| args: SnapshotRpcRequest['args'], | ||
| call: SnapshotRpcCall, | ||
| ): BrowserDispatchRequest => { | ||
@@ -23,4 +22,4 @@ // Snapshot is just one namespace on the shared dispatch RPC channel. | ||
| namespace: DISPATCH_NAMESPACE_SNAPSHOT, | ||
| method, | ||
| args, | ||
| method: call.method, | ||
| args: call.args, | ||
| }; | ||
@@ -32,14 +31,10 @@ }; | ||
| * The container will forward it to the host via WebSocket RPC. | ||
| * | ||
| * `call` is a {@link SnapshotRpcCall}, so `method` and `args` are validated as a | ||
| * pair — a mismatched argument shape fails to compile at the call site. | ||
| */ | ||
| const sendRpcRequest = <T>( | ||
| method: SnapshotRpcRequest['method'], | ||
| args: SnapshotRpcRequest['args'], | ||
| ): Promise<T> => { | ||
| const sendRpcRequest = <T>(call: SnapshotRpcCall): Promise<T> => { | ||
| const requestId = createRequestId('snapshot-rpc'); | ||
| const rpcTimeout = getRpcTimeout(); | ||
| const dispatchRequest = createSnapshotDispatchRequest( | ||
| requestId, | ||
| method, | ||
| args, | ||
| ); | ||
| const dispatchRequest = createSnapshotDispatchRequest(requestId, call); | ||
@@ -51,3 +46,3 @@ return dispatchRpc<T>({ | ||
| staleMessage: 'Stale snapshot RPC request ignored.', | ||
| timeoutMessage: `Snapshot RPC timeout after ${rpcTimeout / 1000}s: ${method}`, | ||
| timeoutMessage: `Snapshot RPC timeout after ${rpcTimeout / 1000}s: ${call.method}`, | ||
| }); | ||
@@ -74,4 +69,5 @@ }; | ||
| async resolvePath(filepath: string): Promise<string> { | ||
| return sendRpcRequest<string>('resolveSnapshotPath', { | ||
| testPath: filepath, | ||
| return sendRpcRequest<string>({ | ||
| method: 'resolveSnapshotPath', | ||
| args: { testPath: filepath }, | ||
| }); | ||
@@ -85,5 +81,5 @@ } | ||
| async saveSnapshotFile(filepath: string, snapshot: string): Promise<void> { | ||
| await sendRpcRequest<void>('saveSnapshotFile', { | ||
| filepath, | ||
| content: snapshot, | ||
| await sendRpcRequest<void>({ | ||
| method: 'saveSnapshotFile', | ||
| args: { filepath, content: snapshot }, | ||
| }); | ||
@@ -93,7 +89,13 @@ } | ||
| async readSnapshotFile(filepath: string): Promise<string | null> { | ||
| return sendRpcRequest<string | null>('readSnapshotFile', { filepath }); | ||
| return sendRpcRequest<string | null>({ | ||
| method: 'readSnapshotFile', | ||
| args: { filepath }, | ||
| }); | ||
| } | ||
| async removeSnapshotFile(filepath: string): Promise<void> { | ||
| await sendRpcRequest<void>('removeSnapshotFile', { filepath }); | ||
| await sendRpcRequest<void>({ | ||
| method: 'removeSnapshotFile', | ||
| args: { filepath }, | ||
| }); | ||
| } | ||
@@ -100,0 +102,0 @@ |
@@ -105,3 +105,3 @@ import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'; | ||
| * This is essential for inline snapshot support because the snapshot code | ||
| * runs in runner.js (which contains @rstest/core/browser-runtime). | ||
| * runs in runner.js (which contains @rstest/core/internal/browser-runtime). | ||
| * Without this, stack traces from inline snapshots cannot be mapped back | ||
@@ -108,0 +108,0 @@ * to the original source files. |
+6
-20
@@ -1,4 +0,8 @@ | ||
| import os from 'node:os'; | ||
| import type { Rstest } from '@rstest/core/browser'; | ||
| import { getNumCpus, parseWorkers } from '@rstest/core/internal/browser'; | ||
| import type { Rstest } from '@rstest/core/internal/browser'; | ||
| // Re-export the shared worker primitives so existing consumers (and unit tests | ||
| // importing from `../src/concurrency`) keep a stable import surface. | ||
| export { getNumCpus, parseWorkers }; | ||
| // Shared headless concurrency policy. | ||
@@ -16,20 +20,2 @@ // Keep this in one place so executors reuse the same worker semantics. | ||
| const getNumCpus = (): number => { | ||
| return os.availableParallelism?.() ?? os.cpus().length; | ||
| }; | ||
| export const parseWorkers = ( | ||
| maxWorkers: string | number, | ||
| numCpus = getNumCpus(), | ||
| ): number => { | ||
| const parsed = Number.parseInt(maxWorkers.toString(), 10); | ||
| if (typeof maxWorkers === 'string' && maxWorkers.trim().endsWith('%')) { | ||
| const workers = Math.floor((parsed / 100) * numCpus); | ||
| return Math.max(workers, 1); | ||
| } | ||
| return parsed > 0 ? parsed : 1; | ||
| }; | ||
| export const resolveDefaultHeadlessWorkers = ( | ||
@@ -36,0 +22,0 @@ command: HeadlessConcurrencyContext['command'], |
@@ -1,2 +0,2 @@ | ||
| import type { Rstest } from '@rstest/core/browser'; | ||
| import type { RstestContext } from '@rstest/core/internal/browser'; | ||
| import { resolveBrowserViewportPreset } from './viewportPresets'; | ||
@@ -45,3 +45,3 @@ | ||
| export const validateBrowserConfig = (context: Rstest): void => { | ||
| export const validateBrowserConfig = (context: RstestContext): void => { | ||
| for (const project of context.projects) { | ||
@@ -48,0 +48,0 @@ const { browser, output } = project.normalizedConfig; |
+102
-83
@@ -1,3 +0,7 @@ | ||
| import type { Reporter } from '@rstest/core/browser'; | ||
| import type { Reporter } from '@rstest/core/internal/browser'; | ||
| import { HostDispatchRouter } from './dispatchRouter'; | ||
| import { | ||
| DISPATCH_NAMESPACE_RUNNER, | ||
| DISPATCH_NAMESPACE_SNAPSHOT, | ||
| } from './protocol'; | ||
| import type { | ||
@@ -7,2 +11,5 @@ BrowserClientMessage, | ||
| BrowserDispatchRequest, | ||
| RunnerDispatchMethod, | ||
| SnapshotRpcMethod, | ||
| SnapshotRpcMethodArgs, | ||
| SnapshotRpcRequest, | ||
@@ -56,33 +63,46 @@ } from './protocol'; | ||
| /** | ||
| * Builds a typed {@link SnapshotRpcRequest} from the untrusted wire envelope, | ||
| * one builder per method. Keyed by {@link SnapshotRpcMethod}, so adding or | ||
| * renaming a method in the union forces a matching entry here — a stale name | ||
| * becomes a compile error instead of silently falling through to `null`. The | ||
| * `args` casts are the unavoidable trust boundary for inbound wire data. | ||
| */ | ||
| const snapshotRequestBuilders: { | ||
| [M in SnapshotRpcMethod]: ( | ||
| id: string, | ||
| args: BrowserDispatchRequest['args'], | ||
| ) => Extract<SnapshotRpcRequest, { method: M }>; | ||
| } = { | ||
| resolveSnapshotPath: (id, args) => ({ | ||
| id, | ||
| method: 'resolveSnapshotPath', | ||
| args: args as SnapshotRpcMethodArgs['resolveSnapshotPath'], | ||
| }), | ||
| readSnapshotFile: (id, args) => ({ | ||
| id, | ||
| method: 'readSnapshotFile', | ||
| args: args as SnapshotRpcMethodArgs['readSnapshotFile'], | ||
| }), | ||
| saveSnapshotFile: (id, args) => ({ | ||
| id, | ||
| method: 'saveSnapshotFile', | ||
| args: args as SnapshotRpcMethodArgs['saveSnapshotFile'], | ||
| }), | ||
| removeSnapshotFile: (id, args) => ({ | ||
| id, | ||
| method: 'removeSnapshotFile', | ||
| args: args as SnapshotRpcMethodArgs['removeSnapshotFile'], | ||
| }), | ||
| }; | ||
| const toSnapshotRpcRequest = ( | ||
| request: BrowserDispatchRequest, | ||
| ): SnapshotRpcRequest | null => { | ||
| switch (request.method) { | ||
| case 'resolveSnapshotPath': | ||
| return { | ||
| id: request.requestId, | ||
| method: 'resolveSnapshotPath', | ||
| args: request.args as { testPath: string }, | ||
| }; | ||
| case 'readSnapshotFile': | ||
| return { | ||
| id: request.requestId, | ||
| method: 'readSnapshotFile', | ||
| args: request.args as { filepath: string }, | ||
| }; | ||
| case 'saveSnapshotFile': | ||
| return { | ||
| id: request.requestId, | ||
| method: 'saveSnapshotFile', | ||
| args: request.args as { filepath: string; content: string }, | ||
| }; | ||
| case 'removeSnapshotFile': | ||
| return { | ||
| id: request.requestId, | ||
| method: 'removeSnapshotFile', | ||
| args: request.args as { filepath: string }, | ||
| }; | ||
| default: | ||
| return null; | ||
| } | ||
| const builder = snapshotRequestBuilders[ | ||
| request.method as SnapshotRpcMethod | ||
| ] as | ||
| | ((id: string, args: BrowserDispatchRequest['args']) => SnapshotRpcRequest) | ||
| | undefined; | ||
| return builder ? builder(request.requestId, request.args) : null; | ||
| }; | ||
@@ -99,58 +119,57 @@ | ||
| router.register('runner', async (request: BrowserDispatchRequest) => { | ||
| switch (request.method) { | ||
| case 'file-start': | ||
| await runnerCallbacks.onTestFileStart( | ||
| request.args as RunnerPayload<'file-start'>, | ||
| ); | ||
| break; | ||
| case 'file-ready': | ||
| await runnerCallbacks.onTestFileReady( | ||
| request.args as RunnerDispatchFileReadyPayload, | ||
| ); | ||
| break; | ||
| case 'suite-start': | ||
| await runnerCallbacks.onTestSuiteStart( | ||
| request.args as RunnerDispatchSuiteStartPayload, | ||
| ); | ||
| break; | ||
| case 'suite-result': | ||
| await runnerCallbacks.onTestSuiteResult( | ||
| request.args as RunnerDispatchSuiteResultPayload, | ||
| ); | ||
| break; | ||
| case 'case-start': | ||
| await runnerCallbacks.onTestCaseStart( | ||
| request.args as RunnerDispatchCaseStartPayload, | ||
| ); | ||
| break; | ||
| case 'case-result': | ||
| await runnerCallbacks.onTestCaseResult( | ||
| request.args as RunnerPayload<'case-result'>, | ||
| ); | ||
| break; | ||
| case 'file-complete': | ||
| await runnerCallbacks.onTestFileComplete( | ||
| request.args as RunnerPayload<'file-complete'>, | ||
| ); | ||
| break; | ||
| case 'log': | ||
| await runnerCallbacks.onLog(request.args as RunnerPayload<'log'>); | ||
| break; | ||
| case 'fatal': | ||
| await runnerCallbacks.onFatal(request.args as RunnerPayload<'fatal'>); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| }); | ||
| // Keyed by RunnerDispatchMethod so adding a runner method to that union forces | ||
| // a handler entry here (a missing key is a compile error) — the previous | ||
| // `switch` with `default: break` silently dropped unhandled methods. The | ||
| // `args` casts are checked against each callback's parameter type, so a wrong | ||
| // payload type also fails to compile. | ||
| const runnerMethodHandlers: { | ||
| [M in RunnerDispatchMethod]: ( | ||
| args: BrowserDispatchRequest['args'], | ||
| ) => Promise<void>; | ||
| } = { | ||
| 'file-start': (args) => | ||
| runnerCallbacks.onTestFileStart(args as RunnerPayload<'file-start'>), | ||
| 'file-ready': (args) => | ||
| runnerCallbacks.onTestFileReady(args as RunnerDispatchFileReadyPayload), | ||
| 'suite-start': (args) => | ||
| runnerCallbacks.onTestSuiteStart(args as RunnerDispatchSuiteStartPayload), | ||
| 'suite-result': (args) => | ||
| runnerCallbacks.onTestSuiteResult( | ||
| args as RunnerDispatchSuiteResultPayload, | ||
| ), | ||
| 'case-start': (args) => | ||
| runnerCallbacks.onTestCaseStart(args as RunnerDispatchCaseStartPayload), | ||
| 'case-result': (args) => | ||
| runnerCallbacks.onTestCaseResult(args as RunnerPayload<'case-result'>), | ||
| 'file-complete': (args) => | ||
| runnerCallbacks.onTestFileComplete( | ||
| args as RunnerPayload<'file-complete'>, | ||
| ), | ||
| log: (args) => runnerCallbacks.onLog(args as RunnerPayload<'log'>), | ||
| fatal: (args) => runnerCallbacks.onFatal(args as RunnerPayload<'fatal'>), | ||
| }; | ||
| router.register('snapshot', async (request: BrowserDispatchRequest) => { | ||
| const snapshotRequest = toSnapshotRpcRequest(request); | ||
| if (!snapshotRequest) { | ||
| return undefined; | ||
| } | ||
| return runSnapshotRpc(snapshotRequest); | ||
| }); | ||
| router.register( | ||
| DISPATCH_NAMESPACE_RUNNER, | ||
| async (request: BrowserDispatchRequest) => { | ||
| // `request.method` is untrusted wire data, so the lookup may miss. Unknown | ||
| // methods are ignored for forward-compatibility with newer runners, | ||
| // matching the previous `default: break`. | ||
| await runnerMethodHandlers[request.method as RunnerDispatchMethod]?.( | ||
| request.args, | ||
| ); | ||
| }, | ||
| ); | ||
| router.register( | ||
| DISPATCH_NAMESPACE_SNAPSHOT, | ||
| async (request: BrowserDispatchRequest) => { | ||
| const snapshotRequest = toSnapshotRpcRequest(request); | ||
| if (!snapshotRequest) { | ||
| return undefined; | ||
| } | ||
| return runSnapshotRpc(snapshotRequest); | ||
| }, | ||
| ); | ||
| for (const [namespace, handler] of extensionHandlers ?? []) { | ||
@@ -157,0 +176,0 @@ if (router.has(namespace)) { |
+6
-2
@@ -0,1 +1,2 @@ | ||
| import { DISPATCH_MESSAGE_TYPE, DISPATCH_RPC_BRIDGE_NAME } from './protocol'; | ||
| import type { | ||
@@ -41,4 +42,7 @@ BrowserClientMessage, | ||
| __RSTEST_BROWSER_OPTIONS__?: BrowserHostConfig; | ||
| __rstest_dispatch__?: (message: BrowserClientMessage) => void; | ||
| __rstest_dispatch_rpc__?: ( | ||
| // Keyed by the sentinel constants so the declared global name and the | ||
| // constant are the same source — renaming a constant moves this key with it, | ||
| // and every `window[CONST]` access site stays in lockstep automatically. | ||
| [DISPATCH_MESSAGE_TYPE]?: (message: BrowserClientMessage) => void; | ||
| [DISPATCH_RPC_BRIDGE_NAME]?: ( | ||
| request: BrowserDispatchRequest, | ||
@@ -45,0 +49,0 @@ ) => Promise<unknown>; |
+22
-11
| import type { | ||
| BrowserHostModule, | ||
| BrowserTestRunOptions, | ||
| BrowserTestRunResult, | ||
| Rstest, | ||
| } from '@rstest/core/browser'; | ||
| RstestContext, | ||
| } from '@rstest/core/internal/browser'; | ||
| import { validateBrowserConfig } from './configValidation'; | ||
| import { | ||
@@ -12,11 +14,6 @@ type ListBrowserTestsResult, | ||
| export { validateBrowserConfig } from './configValidation'; | ||
| export { | ||
| BROWSER_VIEWPORT_PRESET_DIMENSIONS, | ||
| BROWSER_VIEWPORT_PRESET_IDS, | ||
| resolveBrowserViewportPreset, | ||
| } from './viewportPresets'; | ||
| export { validateBrowserConfig }; | ||
| export async function runBrowserTests( | ||
| context: Rstest, | ||
| context: RstestContext, | ||
| options?: BrowserTestRunOptions, | ||
@@ -28,7 +25,21 @@ ): Promise<BrowserTestRunResult | void> { | ||
| export async function listBrowserTests( | ||
| context: Rstest, | ||
| context: RstestContext, | ||
| options?: Pick<BrowserTestRunOptions, 'shardedEntries'>, | ||
| ): Promise<ListBrowserTestsResult> { | ||
| return listBrowserTestsImpl(context); | ||
| // Forward `options` (e.g. `shardedEntries`) so `rstest list --shard` lists | ||
| // only the current shard's browser test files, matching the run path. | ||
| return listBrowserTestsImpl(context, options); | ||
| } | ||
| /** | ||
| * Compile-time guard: ensure the public host exports satisfy the core-owned | ||
| * {@link BrowserHostModule} contract. This catches drift such as a dropped | ||
| * `options` argument at the load boundary. No runtime side effect. | ||
| */ | ||
| void ({ | ||
| validateBrowserConfig, | ||
| runBrowserTests, | ||
| listBrowserTests, | ||
| } satisfies BrowserHostModule); | ||
| export type { | ||
@@ -35,0 +46,0 @@ BrowserTestRunOptions, |
+66
-22
@@ -1,2 +0,2 @@ | ||
| import type { DevicePreset } from '@rstest/core/browser'; | ||
| import type { BrowserViewport } from '@rstest/core/internal/browser'; | ||
| import type { | ||
@@ -7,3 +7,3 @@ RuntimeConfig, | ||
| TestResult, | ||
| } from '@rstest/core/browser-runtime'; | ||
| } from '@rstest/core/internal/browser-runtime'; | ||
| import type { SnapshotUpdateState } from '@vitest/snapshot'; | ||
@@ -14,2 +14,5 @@ | ||
| BrowserRpcRequest, | ||
| SnapshotRpcCall, | ||
| SnapshotRpcMethod, | ||
| SnapshotRpcMethodArgs, | ||
| SnapshotRpcRequest, | ||
@@ -32,8 +35,6 @@ } from './rpcProtocol'; | ||
| export type BrowserViewport = | ||
| | { | ||
| width: number; | ||
| height: number; | ||
| } | ||
| | DevicePreset; | ||
| // `BrowserViewport` is a core config type (`@rstest/core` owns the canonical | ||
| // definition used by `NormalizedBrowserModeConfig`). Re-export it so the host | ||
| // assigns the SAME type across the seam instead of a hand-copied duplicate. | ||
| export type { BrowserViewport }; | ||
@@ -64,2 +65,21 @@ export type BrowserProjectRuntime = { | ||
| /** | ||
| * Wire shape of a `log` client message payload. The host receives this and maps | ||
| * it onto core's {@link UserConsoleLog} (notably `level` → `name`); that mapper | ||
| * (`hostController.ts` `handleLog`) annotates its result as `UserConsoleLog`, so | ||
| * the map→core direction is compiler-checked. Owning the wire shape here as one | ||
| * named type keeps the host's input type from drifting away from the producer. | ||
| */ | ||
| export type BrowserLogPayload = { | ||
| level: 'log' | 'warn' | 'error' | 'info' | 'debug'; | ||
| content: string; | ||
| taskId?: string; | ||
| taskName?: string; | ||
| taskParentNames?: string[]; | ||
| taskType?: 'file' | 'suite' | 'case'; | ||
| testPath: string; | ||
| type: 'stdout' | 'stderr'; | ||
| trace?: string; | ||
| }; | ||
| export type BrowserHostConfig = { | ||
@@ -108,17 +128,4 @@ rootPath: string; | ||
| | { type: 'file-complete'; payload: TestFileResult } | ||
| | { type: 'log'; payload: BrowserLogPayload } | ||
| | { | ||
| type: 'log'; | ||
| payload: { | ||
| level: 'log' | 'warn' | 'error' | 'info' | 'debug'; | ||
| content: string; | ||
| taskId?: string; | ||
| taskName?: string; | ||
| taskParentNames?: string[]; | ||
| taskType?: 'file' | 'suite' | 'case'; | ||
| testPath: string; | ||
| type: 'stdout' | 'stderr'; | ||
| trace?: string; | ||
| }; | ||
| } | ||
| | { | ||
| type: 'fatal'; | ||
@@ -143,2 +150,34 @@ payload: { message: string; stack?: string }; | ||
| /** | ||
| * Lifecycle methods the runner emits via `dispatchRunnerLifecycle()` as | ||
| * dispatch-rpc-requests on the `runner` namespace (as opposed to the | ||
| * {@link BrowserClientMessage} types it `send()`s). The runner client imports | ||
| * this instead of redeclaring the list, so the emit site cannot drift from the | ||
| * host router. | ||
| */ | ||
| export type RunnerLifecycleMethod = | ||
| | 'file-ready' | ||
| | 'suite-start' | ||
| | 'suite-result' | ||
| | 'case-start'; | ||
| /** | ||
| * {@link BrowserClientMessage} types that are forwarded to the `runner` | ||
| * namespace (by message `type`) rather than handled at the transport layer. | ||
| * `Extract` keeps this a checked subset of the message union — renaming a | ||
| * message type drops it here, surfacing as a missing handler downstream. | ||
| */ | ||
| type RunnerMessageMethod = Extract< | ||
| BrowserClientMessage['type'], | ||
| 'file-start' | 'case-result' | 'file-complete' | 'log' | 'fatal' | ||
| >; | ||
| /** | ||
| * Single source of truth for every method handled by the `runner` dispatch | ||
| * namespace. The host handler table is keyed by this union (a missing key is a | ||
| * compile error), so adding a runner method here forces a matching handler and | ||
| * cannot silently no-op at runtime. | ||
| */ | ||
| export type RunnerDispatchMethod = RunnerLifecycleMethod | RunnerMessageMethod; | ||
| /** | ||
| * Transport-agnostic envelope used by host routing. | ||
@@ -156,2 +195,7 @@ * `namespace + method + args + target` describes an operation independent of | ||
| args?: unknown; | ||
| // Routing reads `namespace`, `method`, `runToken`, and `target.sessionId` | ||
| // (see dispatchRouter.ts / dispatchBrowserRpcRequest). `target.testFile` and | ||
| // `target.projectName` are carried for diagnostics / forward-compatibility and | ||
| // are NOT consulted for routing today — adding a routing-relevant field here | ||
| // means wiring a reader on the host side, which structural typing won't force. | ||
| target?: { | ||
@@ -158,0 +202,0 @@ testFile?: string; |
@@ -0,1 +1,2 @@ | ||
| import type { BrowserProvider } from '@rstest/core/internal/browser'; | ||
| import type { BrowserRpcRequest } from '../rpcProtocol'; | ||
@@ -17,4 +18,10 @@ import { playwrightProviderImplementation } from './playwright'; | ||
| * - prefer direct passthrough to provider APIs over provider-specific translation | ||
| * | ||
| * The provider-name union is owned by `@rstest/core` (see `BROWSER_PROVIDERS`) | ||
| * because core's CLI `init` templates need it but cannot import this package | ||
| * (peer-dependency direction). `providerImplementations` below is keyed by the | ||
| * re-exported union, so a provider added in core without an implementation here | ||
| * is a compile error. | ||
| */ | ||
| export type BrowserProvider = 'playwright'; | ||
| export type { BrowserProvider }; | ||
@@ -21,0 +28,0 @@ /** Minimal console shape needed by host logging bridge. */ |
+34
-21
@@ -182,25 +182,38 @@ export type BrowserLocatorText = | ||
| /** | ||
| * Single source of truth for snapshot RPC methods and their argument shapes. | ||
| * The request union, the client sender, both host-side mappers, and every | ||
| * exhaustiveness check derive from this map — adding or renaming a method here | ||
| * forces a compile error at every site that has not been updated. | ||
| */ | ||
| export type SnapshotRpcMethodArgs = { | ||
| resolveSnapshotPath: { testPath: string }; | ||
| readSnapshotFile: { filepath: string }; | ||
| saveSnapshotFile: { filepath: string; content: string }; | ||
| removeSnapshotFile: { filepath: string }; | ||
| }; | ||
| export type SnapshotRpcMethod = keyof SnapshotRpcMethodArgs; | ||
| /** | ||
| * Snapshot RPC request from runner iframe. | ||
| * The container will forward these to the host via WebSocket RPC. | ||
| */ | ||
| export type SnapshotRpcRequest = | ||
| | { | ||
| id: string; | ||
| method: 'resolveSnapshotPath'; | ||
| args: { testPath: string }; | ||
| } | ||
| | { | ||
| id: string; | ||
| method: 'readSnapshotFile'; | ||
| args: { filepath: string }; | ||
| } | ||
| | { | ||
| id: string; | ||
| method: 'saveSnapshotFile'; | ||
| args: { filepath: string; content: string }; | ||
| } | ||
| | { | ||
| id: string; | ||
| method: 'removeSnapshotFile'; | ||
| args: { filepath: string }; | ||
| }; | ||
| export type SnapshotRpcRequest = { | ||
| [M in SnapshotRpcMethod]: { | ||
| id: string; | ||
| method: M; | ||
| args: SnapshotRpcMethodArgs[M]; | ||
| }; | ||
| }[SnapshotRpcMethod]; | ||
| /** | ||
| * Client-side snapshot RPC call (request without the transport `id`). | ||
| * Distributive over {@link SnapshotRpcMethod} so `method` and `args` stay paired | ||
| * at the call site — a mismatched pair fails to compile. | ||
| */ | ||
| export type SnapshotRpcCall = { | ||
| [M in SnapshotRpcMethod]: { | ||
| method: M; | ||
| args: SnapshotRpcMethodArgs[M]; | ||
| }; | ||
| }[SnapshotRpcMethod]; |
@@ -7,2 +7,7 @@ /** | ||
| * autocomplete and runtime validation stay consistent. | ||
| * | ||
| * Upstream source of truth for ids/dimensions is the Chrome DevTools emulated | ||
| * device list — mirror each device's `screen.vertical` `{ width, height }`: | ||
| * https://github.com/ChromeDevTools/devtools-frontend/blob/main/front_end/models/emulation/EmulatedDevices.ts | ||
| * Last verified in sync (all 17): 2026-04-22, devtools-frontend commit abaac05e. | ||
| */ | ||
@@ -9,0 +14,0 @@ export const BROWSER_VIEWPORT_PRESET_IDS = [ |
@@ -1,7 +0,5 @@ | ||
| import { color, logger } from '@rstest/core/browser'; | ||
| import { color, isTTY, logger } from '@rstest/core/internal/browser'; | ||
| const isTTY = (): boolean => Boolean(process.stdin.isTTY && !process.env.CI); | ||
| export const isBrowserWatchCliShortcutsEnabled = (): boolean => isTTY('stdin'); | ||
| export const isBrowserWatchCliShortcutsEnabled = (): boolean => isTTY(); | ||
| export const getBrowserWatchCliShortcutsHintMessage = (): string => { | ||
@@ -8,0 +6,0 @@ return ` ${color.dim('press')} ${color.bold('q')} ${color.dim('to quit')}\n`; |
Sorry, the diff of this file is too big to display
| /*! LICENSE: 27.099feaf1da.js.LICENSE.txt */ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| /*! LICENSE: lib-react.5ae9b90ee5.js.LICENSE.txt */ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
2416222
0.47%53539
0.28%8
-20%