@rstest/browser 0.11.4 → 0.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/browser-container/container-static/js/683.4f821ce8f1.js +194 -0
  2. package/dist/browser-container/container-static/js/index.568aa7cb35.js +1 -0
  3. package/dist/browser-container/container-static/js/lib-react.e24a1d366b.js +2 -0
  4. package/dist/browser-container/index.html +1 -1
  5. package/dist/browserExecutor.d.ts +7 -5
  6. package/dist/browserRsbuild.d.ts +119 -0
  7. package/dist/containerRpc.d.ts +52 -0
  8. package/dist/dispatchCapabilities.d.ts +1 -2
  9. package/dist/headedScheduler.d.ts +58 -0
  10. package/dist/headlessScheduler.d.ts +37 -0
  11. package/dist/hostController.d.ts +12 -47
  12. package/dist/hostPayloads.d.ts +30 -0
  13. package/dist/index.js +1738 -1793
  14. package/dist/protocol.d.ts +5 -0
  15. package/dist/schedulerSeam.d.ts +37 -0
  16. package/dist/watchRerunPlanner.d.ts +5 -0
  17. package/dist/watchRuntime.d.ts +21 -0
  18. package/dist/watchSignals.d.ts +22 -0
  19. package/package.json +5 -5
  20. package/src/browserExecutor.ts +90 -8
  21. package/src/browserRsbuild.ts +1927 -0
  22. package/src/client/entry.ts +3 -0
  23. package/src/containerRpc.ts +206 -0
  24. package/src/dispatchCapabilities.ts +1 -6
  25. package/src/headedScheduler.ts +664 -0
  26. package/src/headlessScheduler.ts +566 -0
  27. package/src/hostController.ts +855 -4163
  28. package/src/hostPayloads.ts +62 -0
  29. package/src/protocol.ts +6 -0
  30. package/src/schedulerSeam.ts +49 -0
  31. package/src/watchRerunPlanner.ts +18 -7
  32. package/src/watchRuntime.ts +83 -0
  33. package/src/watchSignals.ts +93 -0
  34. package/dist/browser-container/container-static/js/243.a8eed2b9e7.js +0 -27406
  35. package/dist/browser-container/container-static/js/243.a8eed2b9e7.js.LICENSE.txt +0 -1
  36. package/dist/browser-container/container-static/js/index.84aaafaf21.js +0 -3058
  37. package/dist/browser-container/container-static/js/lib-react.62b27a21db.js +0 -8454
  38. package/dist/browser-container/container-static/js/lib-react.62b27a21db.js.LICENSE.txt +0 -1
  39. package/dist/headlessLatestRerunScheduler.d.ts +0 -18
  40. package/src/headlessLatestRerunScheduler.ts +0 -76
@@ -142,6 +142,7 @@ const getFileTaskId = (testPath: string): string => {
142
142
  * Returns a restore function to revert console to original.
143
143
  */
144
144
  const interceptConsole = (
145
+ projectName: string,
145
146
  getCurrentTask: () => CurrentTaskInfo | undefined,
146
147
  printConsoleTrace: boolean,
147
148
  ): (() => void) => {
@@ -176,6 +177,7 @@ const interceptConsole = (
176
177
  payload: {
177
178
  level,
178
179
  content,
180
+ projectName,
179
181
  taskId: currentTask?.taskId,
180
182
  taskName: currentTask?.taskName,
181
183
  taskParentNames: currentTask?.taskParentNames,
@@ -605,6 +607,7 @@ const run = async () => {
605
607
  // Intercept console methods to forward logs to host
606
608
  const restoreConsole = shouldInterceptConsole
607
609
  ? interceptConsole(
610
+ projectRuntime.name,
608
611
  () => taskContext.getCurrent() ?? taskStack[taskStack.length - 1],
609
612
  runtimeConfig.disableConsoleIntercept
610
613
  ? false
@@ -0,0 +1,206 @@
1
+ import { logger, type TestResult } from '@rstest/core/internal/browser';
2
+ import { type BirpcReturn, createBirpc } from 'birpc';
3
+ import { type WebSocket, WebSocketServer } from 'ws';
4
+ import type {
5
+ BrowserDispatchRequest,
6
+ BrowserDispatchResponse,
7
+ BrowserHostConfig,
8
+ TestFileInfo,
9
+ } from './protocol';
10
+ import type {
11
+ FatalPayload,
12
+ HeadedTestFileCompletePayload,
13
+ LogPayload,
14
+ ReloadTestFileAck,
15
+ TestFileStartPayload,
16
+ } from './hostPayloads';
17
+
18
+ /** RPC methods exposed by the host (server) to the container (client) */
19
+ export type HostRpcMethods = {
20
+ rerunTest: (testFile: string, testNamePattern?: string) => Promise<void>;
21
+ getTestFiles: () => Promise<TestFileInfo[]>;
22
+ onRunnerFramesReady: (testFiles: string[]) => Promise<void>;
23
+ // Test result callbacks from container
24
+ onTestFileStart: (payload: TestFileStartPayload) => Promise<void>;
25
+ onTestCaseResult: (payload: TestResult) => Promise<void>;
26
+ onTestFileComplete: (payload: HeadedTestFileCompletePayload) => Promise<void>;
27
+ onLog: (payload: LogPayload) => Promise<void>;
28
+ onFatal: (payload: FatalPayload) => Promise<void>;
29
+ // Generic dispatch endpoint used by runner RPC requests.
30
+ dispatch: (
31
+ request: BrowserDispatchRequest,
32
+ ) => Promise<BrowserDispatchResponse>;
33
+ };
34
+
35
+ /** RPC methods exposed by the container (client) to the host (server) */
36
+ export type ContainerRpcMethods = {
37
+ onTestFileUpdate: (testFiles: TestFileInfo[]) => Promise<void>;
38
+ reloadTestFile: (
39
+ testFile: string,
40
+ testNamePattern?: string,
41
+ ) => Promise<ReloadTestFileAck>;
42
+ /**
43
+ * Replace the container's copy of the host config so runner iframes loaded
44
+ * from now on receive fresh values (e.g. the 'u' shortcut flipping
45
+ * `snapshot.updateSnapshot` between watch reruns).
46
+ */
47
+ onHostConfigUpdate: (config: BrowserHostConfig) => Promise<void>;
48
+ };
49
+
50
+ export type ContainerRpc = BirpcReturn<ContainerRpcMethods, HostRpcMethods>;
51
+
52
+ // ============================================================================
53
+ // RPC Manager - Encapsulates WebSocket and birpc management
54
+ // ============================================================================
55
+
56
+ /**
57
+ * Manages the WebSocket connection and birpc communication with the container UI.
58
+ * Provides a clean interface for sending RPC calls and handling connections.
59
+ */
60
+ export class ContainerRpcManager {
61
+ private wss: WebSocketServer;
62
+ private ws: WebSocket | null = null;
63
+ private rpc: ContainerRpc | null = null;
64
+ private methods: HostRpcMethods;
65
+ private onDisconnect?: (error: Error) => void;
66
+ private detachActiveSocketListeners: (() => void) | null = null;
67
+
68
+ constructor(
69
+ wss: WebSocketServer,
70
+ methods: HostRpcMethods,
71
+ onDisconnect?: (error: Error) => void,
72
+ ) {
73
+ this.wss = wss;
74
+ this.methods = methods;
75
+ this.onDisconnect = onDisconnect;
76
+ this.setupConnectionHandler();
77
+ }
78
+
79
+ /** Update the RPC methods (used when starting a new test run) */
80
+ updateMethods(
81
+ methods: HostRpcMethods,
82
+ onDisconnect?: (error: Error) => void,
83
+ ): void {
84
+ this.methods = methods;
85
+ this.onDisconnect = onDisconnect;
86
+ // Re-create birpc with new methods if already connected
87
+ if (this.ws && this.ws.readyState === this.ws.OPEN) {
88
+ this.attachWebSocket(this.ws);
89
+ }
90
+ }
91
+
92
+ private setupConnectionHandler(): void {
93
+ this.wss.on('connection', (ws: WebSocket) => {
94
+ logger.debug('[Browser UI] Container WebSocket connected');
95
+ logger.debug(
96
+ `[Browser UI] Current ws: ${this.ws ? 'exists' : 'null'}, new ws: ${ws ? 'exists' : 'null'}`,
97
+ );
98
+ this.attachWebSocket(ws);
99
+ });
100
+ }
101
+
102
+ private attachWebSocket(ws: WebSocket): void {
103
+ this.detachActiveSocketListeners?.();
104
+ if (this.rpc && !this.rpc.$closed) {
105
+ this.rpc.$close(new Error('Container RPC transport reattached'));
106
+ }
107
+ this.ws = ws;
108
+ const messageHandlers = new WeakMap<
109
+ (data: any) => void,
110
+ (message: any) => void
111
+ >();
112
+
113
+ this.rpc = createBirpc<ContainerRpcMethods, HostRpcMethods>(this.methods, {
114
+ timeout: -1,
115
+ post: (data) => {
116
+ if (ws.readyState === ws.OPEN) {
117
+ ws.send(JSON.stringify(data));
118
+ }
119
+ },
120
+ on: (fn) => {
121
+ const handler = (message: any) => {
122
+ try {
123
+ const data = JSON.parse(message.toString());
124
+ fn(data);
125
+ } catch {
126
+ // ignore invalid messages
127
+ }
128
+ };
129
+ messageHandlers.set(fn, handler);
130
+ ws.on('message', handler);
131
+ },
132
+ off: (fn) => {
133
+ const handler = messageHandlers.get(fn);
134
+ if (!handler) {
135
+ return;
136
+ }
137
+ ws.off('message', handler);
138
+ messageHandlers.delete(fn);
139
+ },
140
+ });
141
+
142
+ const handleClose = () => {
143
+ // Only clear if this is still the active connection
144
+ // This prevents a race condition when a new connection is established
145
+ // before the old one's close event fires
146
+ if (this.ws === ws) {
147
+ this.ws = null;
148
+ }
149
+ this.detachActiveSocketListeners?.();
150
+ this.detachActiveSocketListeners = null;
151
+ if (this.rpc && !this.rpc.$closed) {
152
+ const disconnectError = new Error(
153
+ 'Browser UI WebSocket disconnected before reload completed',
154
+ );
155
+ this.rpc.$close(disconnectError);
156
+ this.onDisconnect?.(disconnectError);
157
+ }
158
+ this.rpc = null;
159
+ };
160
+
161
+ ws.on('close', handleClose);
162
+ this.detachActiveSocketListeners = () => {
163
+ ws.off('close', handleClose);
164
+ };
165
+ }
166
+
167
+ /** Check if a container is currently connected */
168
+ get isConnected(): boolean {
169
+ return this.ws !== null && this.ws.readyState === this.ws.OPEN;
170
+ }
171
+
172
+ /** Get the current WebSocket instance (for reuse in watch mode) */
173
+ get currentWebSocket(): WebSocket | null {
174
+ return this.ws;
175
+ }
176
+
177
+ /** Reattach an existing WebSocket (for watch mode reuse) */
178
+ reattach(ws: WebSocket): void {
179
+ this.attachWebSocket(ws);
180
+ }
181
+
182
+ /** Notify container of test file changes */
183
+ async notifyTestFileUpdate(files: TestFileInfo[]): Promise<void> {
184
+ await this.rpc?.onTestFileUpdate(files);
185
+ }
186
+
187
+ /** Push a refreshed host config to the container (watch reruns) */
188
+ async updateHostConfig(config: BrowserHostConfig): Promise<void> {
189
+ await this.rpc?.onHostConfigUpdate(config);
190
+ }
191
+
192
+ /** Request container to reload a specific test file */
193
+ async reloadTestFile(
194
+ testFile: string,
195
+ testNamePattern?: string,
196
+ ): Promise<ReloadTestFileAck> {
197
+ logger.debug(
198
+ `[Browser UI] reloadTestFile called, rpc: ${this.rpc ? 'exists' : 'null'}, ws: ${this.ws ? 'exists' : 'null'}`,
199
+ );
200
+ if (!this.rpc) {
201
+ throw new Error('Browser UI RPC not available for reloadTestFile');
202
+ }
203
+ logger.debug(`[Browser UI] Calling reloadTestFile: ${testFile}`);
204
+ return this.rpc.reloadTestFile(testFile, testNamePattern);
205
+ }
206
+ }
@@ -1,5 +1,5 @@
1
- import type { Reporter } from '@rstest/core/internal/browser';
2
1
  import { HostDispatchRouter } from './dispatchRouter';
2
+ import type { ReporterHookArg } from './hostPayloads';
3
3
  import {
4
4
  DISPATCH_NAMESPACE_RUNNER,
5
5
  DISPATCH_NAMESPACE_SNAPSHOT,
@@ -25,11 +25,6 @@ type RunnerPayload<TType extends BrowserClientMessage['type']> =
25
25
  ? TPayload
26
26
  : never;
27
27
 
28
- type ReporterHookArg<THook extends keyof Reporter> =
29
- NonNullable<Reporter[THook]> extends (...args: infer TArgs) => unknown
30
- ? TArgs[0]
31
- : never;
32
-
33
28
  type RunnerDispatchFileReadyPayload = ReporterHookArg<'onTestFileReady'>;
34
29
  type RunnerDispatchSuiteStartPayload = ReporterHookArg<'onTestSuiteStart'>;
35
30
  type RunnerDispatchSuiteResultPayload = ReporterHookArg<'onTestSuiteResult'>;