@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.
- package/dist/browser-container/container-static/js/683.4f821ce8f1.js +194 -0
- package/dist/browser-container/container-static/js/index.568aa7cb35.js +1 -0
- package/dist/browser-container/container-static/js/lib-react.e24a1d366b.js +2 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browserExecutor.d.ts +7 -5
- package/dist/browserRsbuild.d.ts +119 -0
- package/dist/containerRpc.d.ts +52 -0
- package/dist/dispatchCapabilities.d.ts +1 -2
- package/dist/headedScheduler.d.ts +58 -0
- package/dist/headlessScheduler.d.ts +37 -0
- package/dist/hostController.d.ts +12 -47
- package/dist/hostPayloads.d.ts +30 -0
- package/dist/index.js +1738 -1793
- package/dist/protocol.d.ts +5 -0
- package/dist/schedulerSeam.d.ts +37 -0
- package/dist/watchRerunPlanner.d.ts +5 -0
- package/dist/watchRuntime.d.ts +21 -0
- package/dist/watchSignals.d.ts +22 -0
- package/package.json +5 -5
- package/src/browserExecutor.ts +90 -8
- package/src/browserRsbuild.ts +1927 -0
- package/src/client/entry.ts +3 -0
- package/src/containerRpc.ts +206 -0
- package/src/dispatchCapabilities.ts +1 -6
- package/src/headedScheduler.ts +664 -0
- package/src/headlessScheduler.ts +566 -0
- package/src/hostController.ts +855 -4163
- package/src/hostPayloads.ts +62 -0
- package/src/protocol.ts +6 -0
- package/src/schedulerSeam.ts +49 -0
- package/src/watchRerunPlanner.ts +18 -7
- package/src/watchRuntime.ts +83 -0
- package/src/watchSignals.ts +93 -0
- package/dist/browser-container/container-static/js/243.a8eed2b9e7.js +0 -27406
- package/dist/browser-container/container-static/js/243.a8eed2b9e7.js.LICENSE.txt +0 -1
- package/dist/browser-container/container-static/js/index.84aaafaf21.js +0 -3058
- package/dist/browser-container/container-static/js/lib-react.62b27a21db.js +0 -8454
- package/dist/browser-container/container-static/js/lib-react.62b27a21db.js.LICENSE.txt +0 -1
- package/dist/headlessLatestRerunScheduler.d.ts +0 -18
- package/src/headlessLatestRerunScheduler.ts +0 -76
package/src/client/entry.ts
CHANGED
|
@@ -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'>;
|