@rstest/browser 0.11.5 → 0.11.7
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/css/index.43daaf6c93.css +1 -0
- package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
- package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browserExecutor.d.ts +7 -5
- package/dist/browserRsbuild.d.ts +139 -0
- package/dist/browserV8Coverage.d.ts +15 -0
- package/dist/client/240~1.js +150 -0
- package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
- package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
- package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
- package/dist/client/client/index.d.ts +2 -0
- package/dist/client/client/runIdentity.d.ts +12 -0
- package/dist/client/client/runner.d.ts +4 -0
- package/dist/client/client/snapshot.d.ts +27 -0
- package/dist/client/client/sourceMapSupport.d.ts +36 -0
- package/dist/{browser.js → client/index.js} +3 -102
- package/dist/client/protocol.d.ts +225 -0
- package/dist/client/rpcProtocol.d.ts +138 -0
- package/dist/client/runner.js +675 -0
- package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
- package/dist/containerRpc.d.ts +60 -0
- package/dist/dispatchCapabilities.d.ts +1 -2
- package/dist/headedRunRegistry.d.ts +51 -0
- package/dist/headedScheduler.d.ts +58 -0
- package/dist/headlessScheduler.d.ts +38 -0
- package/dist/headlessTransport.d.ts +4 -2
- package/dist/hostController.d.ts +12 -47
- package/dist/hostPayloads.d.ts +24 -0
- package/dist/index.js +2019 -1516
- package/dist/protocol.d.ts +48 -8
- package/dist/providers/index.d.ts +21 -0
- package/dist/providers/playwright/v8Coverage.d.ts +2 -0
- package/dist/rpcProtocol.d.ts +1 -2
- package/dist/schedulerSeam.d.ts +42 -0
- package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
- package/dist/watchRerunPlanner.d.ts +20 -4
- package/dist/watchRuntime.d.ts +21 -0
- package/dist/watchSignals.d.ts +29 -0
- package/package.json +12 -12
- package/dist/33.js +0 -7
- package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
- package/dist/browser-container/container-static/js/506.b2f920fa67.js +0 -194
- package/dist/browser-container/container-static/js/index.d27fd63f5e.js +0 -1
- package/dist/browser.d.ts +0 -2
- package/dist/headlessLatestRerunScheduler.d.ts +0 -18
- package/src/augmentExpect.ts +0 -62
- package/src/browser.ts +0 -3
- package/src/browserExecutor.ts +0 -140
- package/src/browserRpcRegistry.ts +0 -57
- package/src/client/api.ts +0 -213
- package/src/client/browserRpc.ts +0 -86
- package/src/client/dispatchTransport.ts +0 -240
- package/src/client/entry.ts +0 -807
- package/src/client/locator.ts +0 -448
- package/src/client/snapshot.ts +0 -113
- package/src/client/sourceMapSupport.ts +0 -159
- package/src/concurrency.ts +0 -65
- package/src/configValidation.ts +0 -263
- package/src/dispatchCapabilities.ts +0 -182
- package/src/dispatchRouter.ts +0 -82
- package/src/env.d.ts +0 -54
- package/src/headedSerialTaskQueue.ts +0 -23
- package/src/headlessLatestRerunScheduler.ts +0 -76
- package/src/headlessTransport.ts +0 -28
- package/src/hostController.ts +0 -4633
- package/src/index.ts +0 -51
- package/src/manifest.d.ts +0 -41
- package/src/protocol.ts +0 -225
- package/src/providers/index.ts +0 -126
- package/src/providers/playwright/compileLocator.ts +0 -130
- package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
- package/src/providers/playwright/expectUtils.ts +0 -57
- package/src/providers/playwright/implementation.ts +0 -35
- package/src/providers/playwright/index.ts +0 -1
- package/src/providers/playwright/runtime.ts +0 -159
- package/src/providers/playwright/textMatcher.ts +0 -10
- package/src/rpcProtocol.ts +0 -218
- package/src/runSession.ts +0 -110
- package/src/sessionRegistry.ts +0 -89
- package/src/sourceMap/sourceMapLoader.ts +0 -96
- package/src/viewportPresets.ts +0 -69
- package/src/watchRerunPlanner.ts +0 -77
- /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
- /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
- /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
package/src/concurrency.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getNumCpus,
|
|
3
|
-
parseWorkers,
|
|
4
|
-
resolveWorkerCount,
|
|
5
|
-
} from '@rstest/core/internal/browser';
|
|
6
|
-
import type { Rstest } from '@rstest/core/internal/browser';
|
|
7
|
-
|
|
8
|
-
// Re-export the shared worker primitives so existing consumers (and unit tests
|
|
9
|
-
// importing from `../src/concurrency`) keep a stable import surface.
|
|
10
|
-
export { getNumCpus, parseWorkers };
|
|
11
|
-
|
|
12
|
-
// The browser headless path caps CPU-derived workers at 12 and halves that
|
|
13
|
-
// capped base in watch; the shared `resolveWorkerCount` helper owns the
|
|
14
|
-
// `maxWorkers` override and workload clamp.
|
|
15
|
-
const DEFAULT_MAX_HEADLESS_WORKERS = 12;
|
|
16
|
-
|
|
17
|
-
type HeadlessConcurrencyContext = Pick<Rstest, 'command'> & {
|
|
18
|
-
normalizedConfig: {
|
|
19
|
-
pool: {
|
|
20
|
-
maxWorkers?: string | number;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const resolveHeadlessWorkerCount = ({
|
|
26
|
-
command,
|
|
27
|
-
maxWorkers,
|
|
28
|
-
totalTasks,
|
|
29
|
-
numCpus = getNumCpus(),
|
|
30
|
-
}: {
|
|
31
|
-
command: HeadlessConcurrencyContext['command'];
|
|
32
|
-
maxWorkers?: string | number;
|
|
33
|
-
totalTasks: number;
|
|
34
|
-
numCpus?: number;
|
|
35
|
-
}): number => {
|
|
36
|
-
const base = Math.max(Math.min(DEFAULT_MAX_HEADLESS_WORKERS, numCpus - 1), 1);
|
|
37
|
-
return resolveWorkerCount({
|
|
38
|
-
command,
|
|
39
|
-
maxWorkers,
|
|
40
|
-
totalTasks,
|
|
41
|
-
recommended: base,
|
|
42
|
-
watchRecommended: Math.max(Math.floor(base / 2), 1),
|
|
43
|
-
numCpus,
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const resolveDefaultHeadlessWorkers = (
|
|
48
|
-
command: HeadlessConcurrencyContext['command'],
|
|
49
|
-
numCpus: number = getNumCpus(),
|
|
50
|
-
): number =>
|
|
51
|
-
resolveHeadlessWorkerCount({
|
|
52
|
-
command,
|
|
53
|
-
totalTasks: Number.POSITIVE_INFINITY,
|
|
54
|
-
numCpus,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
export const getHeadlessConcurrency = (
|
|
58
|
-
context: HeadlessConcurrencyContext,
|
|
59
|
-
totalTests: number,
|
|
60
|
-
): number =>
|
|
61
|
-
resolveHeadlessWorkerCount({
|
|
62
|
-
command: context.command,
|
|
63
|
-
maxWorkers: context.normalizedConfig.pool.maxWorkers,
|
|
64
|
-
totalTasks: totalTests,
|
|
65
|
-
});
|
package/src/configValidation.ts
DELETED
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
browserIgnoredRuntimeConfigKeys,
|
|
3
|
-
color,
|
|
4
|
-
logger,
|
|
5
|
-
type RstestContext,
|
|
6
|
-
type RuntimeConfig,
|
|
7
|
-
} from '@rstest/core/internal/browser';
|
|
8
|
-
import { resolveBrowserViewportPreset } from './viewportPresets';
|
|
9
|
-
|
|
10
|
-
type BrowserProjectConfig =
|
|
11
|
-
RstestContext['projects'][number]['normalizedConfig'];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Per-key warning for a browser-ignored RuntimeConfig field, keyed by the exact
|
|
15
|
-
* RuntimeConfig keys the `executorCapabilities` table marks 'ignored-warn' /
|
|
16
|
-
* 'stripped'. {@link reportUnsupportedBrowserOptions} iterates the table's
|
|
17
|
-
* {@link browserIgnoredRuntimeConfigKeys} and looks each key up here, so the
|
|
18
|
-
* warnings are genuinely table-driven.
|
|
19
|
-
*/
|
|
20
|
-
const ignoredKeyWarnings: Partial<
|
|
21
|
-
Record<
|
|
22
|
-
keyof RuntimeConfig,
|
|
23
|
-
{
|
|
24
|
-
/** True when the project's value is non-default (worth warning about). */
|
|
25
|
-
isNonDefault: (config: BrowserProjectConfig) => boolean;
|
|
26
|
-
message: (config: BrowserProjectConfig) => string;
|
|
27
|
-
/**
|
|
28
|
-
* Warn only on browser-only runs. Global-only config (copied onto every
|
|
29
|
-
* project) that a mixed repo may legitimately set for its node projects.
|
|
30
|
-
*/
|
|
31
|
-
browserOnly?: boolean;
|
|
32
|
-
}
|
|
33
|
-
>
|
|
34
|
-
> = {
|
|
35
|
-
testEnvironment: {
|
|
36
|
-
isNonDefault: (config) => config.testEnvironment.name !== 'node',
|
|
37
|
-
message: (config) =>
|
|
38
|
-
`Ignoring testEnvironment '${config.testEnvironment.name}' in browser ` +
|
|
39
|
-
'mode: the browser itself is the test environment.',
|
|
40
|
-
},
|
|
41
|
-
isolate: {
|
|
42
|
-
isNonDefault: (config) => config.isolate === false,
|
|
43
|
-
message: () =>
|
|
44
|
-
'Ignoring isolate: false in browser mode: each test file still runs in ' +
|
|
45
|
-
'a fresh context.',
|
|
46
|
-
browserOnly: true,
|
|
47
|
-
},
|
|
48
|
-
federation: {
|
|
49
|
-
isNonDefault: (config) => config.federation === true,
|
|
50
|
-
message: () =>
|
|
51
|
-
'Ignoring federation in browser mode: it only applies to the Node runner.',
|
|
52
|
-
browserOnly: true,
|
|
53
|
-
},
|
|
54
|
-
detectAsyncLeaks: {
|
|
55
|
-
isNonDefault: (config) => config.detectAsyncLeaks === true,
|
|
56
|
-
message: () =>
|
|
57
|
-
'Ignoring detectAsyncLeaks in browser mode: it relies on node ' +
|
|
58
|
-
'async_hooks.',
|
|
59
|
-
},
|
|
60
|
-
logHeapUsage: {
|
|
61
|
-
isNonDefault: (config) => config.logHeapUsage === true,
|
|
62
|
-
message: () => 'Ignoring logHeapUsage in browser mode.',
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Browser-ignored keys handled outside the generic warn loop: `coverage` is
|
|
68
|
-
* 'stripped' but gets the dedicated provider check in
|
|
69
|
-
* {@link reportUnsupportedBrowserOptions} (a v8 hard error on browser-only
|
|
70
|
-
* runs, a warning in mixed runs), not a plain "set → warn".
|
|
71
|
-
*/
|
|
72
|
-
const speciallyHandledIgnoredKeys: (keyof RuntimeConfig)[] = ['coverage'];
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Every browser-ignored RuntimeConfig key this module checks — the union of the
|
|
76
|
-
* warn descriptors and the specially-handled keys. Exported for the lockstep
|
|
77
|
-
* test that asserts it covers {@link browserIgnoredRuntimeConfigKeys}.
|
|
78
|
-
*/
|
|
79
|
-
export const browserValidatedIgnoredKeys: (keyof RuntimeConfig)[] = [
|
|
80
|
-
...(Object.keys(ignoredKeyWarnings) as (keyof RuntimeConfig)[]),
|
|
81
|
-
...speciallyHandledIgnoredKeys,
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
// Anti-#1389 lockstep (runs at module load): fail loudly if the
|
|
85
|
-
// `executorCapabilities` table gains an 'ignored-warn' / 'stripped' browser key
|
|
86
|
-
// that this validation does not cover, instead of it becoming a silent no-op.
|
|
87
|
-
const assertIgnoredKeysCovered = (): void => {
|
|
88
|
-
const covered = new Set<string>(browserValidatedIgnoredKeys);
|
|
89
|
-
const uncovered = browserIgnoredRuntimeConfigKeys.filter(
|
|
90
|
-
(key) => !covered.has(key),
|
|
91
|
-
);
|
|
92
|
-
if (uncovered.length > 0) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
'Browser config validation is out of sync with executorCapabilities: ' +
|
|
95
|
-
`no check for ignored RuntimeConfig field(s): ${uncovered.join(', ')}. ` +
|
|
96
|
-
'Add a descriptor to `ignoredKeyWarnings` or `speciallyHandledIgnoredKeys`.',
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
assertIgnoredKeysCovered();
|
|
101
|
-
|
|
102
|
-
const SUPPORTED_PROVIDERS = ['playwright'] as const;
|
|
103
|
-
|
|
104
|
-
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
|
105
|
-
return Object.prototype.toString.call(value) === '[object Object]';
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const validateViewport = (viewport: unknown): void => {
|
|
109
|
-
if (viewport == null) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (typeof viewport === 'string') {
|
|
114
|
-
const presetId = viewport.trim();
|
|
115
|
-
if (!presetId) {
|
|
116
|
-
throw new Error('browser.viewport must be a non-empty preset id.');
|
|
117
|
-
}
|
|
118
|
-
if (!resolveBrowserViewportPreset(presetId)) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
`browser.viewport must be a valid preset id. Received: ${viewport}`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (isPlainObject(viewport)) {
|
|
127
|
-
const width = (viewport as any).width;
|
|
128
|
-
const height = (viewport as any).height;
|
|
129
|
-
if (!Number.isFinite(width) || width <= 0) {
|
|
130
|
-
throw new Error('browser.viewport.width must be a positive number.');
|
|
131
|
-
}
|
|
132
|
-
if (!Number.isFinite(height) || height <= 0) {
|
|
133
|
-
throw new Error('browser.viewport.height must be a positive number.');
|
|
134
|
-
}
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
throw new Error(
|
|
139
|
-
'browser.viewport must be either a preset id or { width, height }.',
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Warn (or hard-error) on node-only config that is silently ignored under
|
|
145
|
-
* `browser.enabled`. Prevents the #1389 class of silent no-ops. The per-key
|
|
146
|
-
* warnings are driven by the `executorCapabilities` table: the loop iterates
|
|
147
|
-
* {@link browserIgnoredRuntimeConfigKeys} and looks each key up in
|
|
148
|
-
* {@link ignoredKeyWarnings}; `coverage` (and `pool`, which is not a
|
|
149
|
-
* RuntimeConfig field) keep bespoke handling below.
|
|
150
|
-
*
|
|
151
|
-
* Scoping rules: `coverage` and `isolate` are global-only config copied onto
|
|
152
|
-
* every project, so a v8 hard-error / isolate warning is gated to browser-only
|
|
153
|
-
* runs — a mixed repo legitimately sets them for its node projects and must not
|
|
154
|
-
* get unsilenceable noise on a correct configuration.
|
|
155
|
-
*/
|
|
156
|
-
const reportUnsupportedBrowserOptions = (context: RstestContext): void => {
|
|
157
|
-
const browserProjects = context.projects.filter(
|
|
158
|
-
(project) => project.normalizedConfig.browser.enabled,
|
|
159
|
-
);
|
|
160
|
-
if (browserProjects.length === 0) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const isBrowserOnlyRun = browserProjects.length === context.projects.length;
|
|
164
|
-
|
|
165
|
-
// `coverage`, `pool` and `isolate` are global-only config (not settable per
|
|
166
|
-
// project), read from the root normalized config.
|
|
167
|
-
const globalConfig = context.normalizedConfig;
|
|
168
|
-
|
|
169
|
-
const { coverage } = globalConfig;
|
|
170
|
-
// `list` never collects coverage, so the v8 guard must not block listing.
|
|
171
|
-
if (
|
|
172
|
-
context.command !== 'list' &&
|
|
173
|
-
coverage.enabled &&
|
|
174
|
-
coverage.provider === 'v8'
|
|
175
|
-
) {
|
|
176
|
-
if (isBrowserOnlyRun) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
"Coverage provider 'v8' is not supported in browser mode: browser " +
|
|
179
|
-
'projects produce no v8 coverage. Use the default istanbul provider ' +
|
|
180
|
-
"(coverage.provider: 'istanbul') for browser coverage.",
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
logger.warn(
|
|
184
|
-
color.yellow(
|
|
185
|
-
"Coverage provider 'v8' produces no coverage for browser project " +
|
|
186
|
-
"files; use the 'istanbul' provider to collect browser coverage. " +
|
|
187
|
-
'Node projects will still use v8.',
|
|
188
|
-
),
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Node-only options silently ignored under browser.enabled. Collected into a
|
|
193
|
-
// Set so each distinct warning is emitted once, not per project.
|
|
194
|
-
const warnings = new Set<string>();
|
|
195
|
-
|
|
196
|
-
if (globalConfig.pool.type !== 'forks') {
|
|
197
|
-
warnings.add(
|
|
198
|
-
`Ignoring pool.type '${globalConfig.pool.type}' in browser mode.`,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
if (globalConfig.pool.execArgv && globalConfig.pool.execArgv.length > 0) {
|
|
202
|
-
warnings.add('Ignoring pool.execArgv in browser mode.');
|
|
203
|
-
}
|
|
204
|
-
// Table-driven warnings: for every browser-ignored RuntimeConfig key, warn
|
|
205
|
-
// when a browser project sets it to a non-default value. `isolate` (and any
|
|
206
|
-
// other global-only key) is copied onto every project, so reading it per
|
|
207
|
-
// project matches the old global read; the Set dedupes the repeats.
|
|
208
|
-
for (const project of browserProjects) {
|
|
209
|
-
const config = project.normalizedConfig;
|
|
210
|
-
for (const key of browserIgnoredRuntimeConfigKeys) {
|
|
211
|
-
const descriptor = ignoredKeyWarnings[key];
|
|
212
|
-
if (!descriptor) {
|
|
213
|
-
// Specially handled elsewhere (e.g. `coverage`).
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (descriptor.browserOnly && !isBrowserOnlyRun) {
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (descriptor.isNonDefault(config)) {
|
|
220
|
-
warnings.add(descriptor.message(config));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
for (const message of warnings) {
|
|
226
|
-
logger.warn(color.yellow(message));
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
export const validateBrowserConfig = (context: RstestContext): void => {
|
|
231
|
-
for (const project of context.projects) {
|
|
232
|
-
const { browser, output } = project.normalizedConfig;
|
|
233
|
-
if (!browser.enabled) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (!browser.provider) {
|
|
238
|
-
throw new Error(
|
|
239
|
-
'browser.provider is required when browser.enabled is true.',
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (!SUPPORTED_PROVIDERS.includes(browser.provider)) {
|
|
244
|
-
throw new Error(
|
|
245
|
-
`browser.provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}.`,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
validateViewport(browser.viewport);
|
|
250
|
-
|
|
251
|
-
if (!isPlainObject(browser.providerOptions)) {
|
|
252
|
-
throw new Error('browser.providerOptions must be a plain object.');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (output?.bundleDependencies === false) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
'output.bundleDependencies false is not supported in browser mode.',
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
reportUnsupportedBrowserOptions(context);
|
|
263
|
-
};
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import type { Reporter } from '@rstest/core/internal/browser';
|
|
2
|
-
import { HostDispatchRouter } from './dispatchRouter';
|
|
3
|
-
import {
|
|
4
|
-
DISPATCH_NAMESPACE_RUNNER,
|
|
5
|
-
DISPATCH_NAMESPACE_SNAPSHOT,
|
|
6
|
-
} from './protocol';
|
|
7
|
-
import type {
|
|
8
|
-
BrowserClientMessage,
|
|
9
|
-
BrowserDispatchHandler,
|
|
10
|
-
BrowserDispatchRequest,
|
|
11
|
-
RunnerDispatchMethod,
|
|
12
|
-
SnapshotRpcMethod,
|
|
13
|
-
SnapshotRpcMethodArgs,
|
|
14
|
-
SnapshotRpcRequest,
|
|
15
|
-
} from './protocol';
|
|
16
|
-
|
|
17
|
-
export type HostDispatchRouterOptions = ConstructorParameters<
|
|
18
|
-
typeof HostDispatchRouter
|
|
19
|
-
>[0];
|
|
20
|
-
|
|
21
|
-
type RunnerPayload<TType extends BrowserClientMessage['type']> =
|
|
22
|
-
Extract<BrowserClientMessage, { type: TType }> extends {
|
|
23
|
-
payload: infer TPayload;
|
|
24
|
-
}
|
|
25
|
-
? TPayload
|
|
26
|
-
: never;
|
|
27
|
-
|
|
28
|
-
type ReporterHookArg<THook extends keyof Reporter> =
|
|
29
|
-
NonNullable<Reporter[THook]> extends (...args: infer TArgs) => unknown
|
|
30
|
-
? TArgs[0]
|
|
31
|
-
: never;
|
|
32
|
-
|
|
33
|
-
type RunnerDispatchFileReadyPayload = ReporterHookArg<'onTestFileReady'>;
|
|
34
|
-
type RunnerDispatchSuiteStartPayload = ReporterHookArg<'onTestSuiteStart'>;
|
|
35
|
-
type RunnerDispatchSuiteResultPayload = ReporterHookArg<'onTestSuiteResult'>;
|
|
36
|
-
type RunnerDispatchCaseStartPayload = ReporterHookArg<'onTestCaseStart'>;
|
|
37
|
-
|
|
38
|
-
type RunnerDispatchCallbacks = {
|
|
39
|
-
onTestFileStart: (payload: RunnerPayload<'file-start'>) => Promise<void>;
|
|
40
|
-
onTestFileReady: (payload: RunnerDispatchFileReadyPayload) => Promise<void>;
|
|
41
|
-
onTestSuiteStart: (payload: RunnerDispatchSuiteStartPayload) => Promise<void>;
|
|
42
|
-
onTestSuiteResult: (
|
|
43
|
-
payload: RunnerDispatchSuiteResultPayload,
|
|
44
|
-
) => Promise<void>;
|
|
45
|
-
onTestCaseStart: (payload: RunnerDispatchCaseStartPayload) => Promise<void>;
|
|
46
|
-
onTestCaseResult: (payload: RunnerPayload<'case-result'>) => Promise<void>;
|
|
47
|
-
onTestFileComplete: (
|
|
48
|
-
payload: RunnerPayload<'file-complete'>,
|
|
49
|
-
) => Promise<void>;
|
|
50
|
-
onLog: (payload: RunnerPayload<'log'>) => Promise<void>;
|
|
51
|
-
onFatal: (payload: RunnerPayload<'fatal'>) => Promise<void>;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
type CreateHostDispatchRouterOptions = {
|
|
55
|
-
routerOptions?: HostDispatchRouterOptions;
|
|
56
|
-
runnerCallbacks: RunnerDispatchCallbacks;
|
|
57
|
-
runSnapshotRpc: (request: SnapshotRpcRequest) => Promise<unknown>;
|
|
58
|
-
extensionHandlers?: Map<string, BrowserDispatchHandler>;
|
|
59
|
-
onDuplicateNamespace?: (namespace: string) => void;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Builds a typed {@link SnapshotRpcRequest} from the untrusted wire envelope,
|
|
64
|
-
* one builder per method. Keyed by {@link SnapshotRpcMethod}, so adding or
|
|
65
|
-
* renaming a method in the union forces a matching entry here — a stale name
|
|
66
|
-
* becomes a compile error instead of silently falling through to `null`. The
|
|
67
|
-
* `args` casts are the unavoidable trust boundary for inbound wire data.
|
|
68
|
-
*/
|
|
69
|
-
const snapshotRequestBuilders: {
|
|
70
|
-
[M in SnapshotRpcMethod]: (
|
|
71
|
-
id: string,
|
|
72
|
-
args: BrowserDispatchRequest['args'],
|
|
73
|
-
) => Extract<SnapshotRpcRequest, { method: M }>;
|
|
74
|
-
} = {
|
|
75
|
-
resolveSnapshotPath: (id, args) => ({
|
|
76
|
-
id,
|
|
77
|
-
method: 'resolveSnapshotPath',
|
|
78
|
-
args: args as SnapshotRpcMethodArgs['resolveSnapshotPath'],
|
|
79
|
-
}),
|
|
80
|
-
readSnapshotFile: (id, args) => ({
|
|
81
|
-
id,
|
|
82
|
-
method: 'readSnapshotFile',
|
|
83
|
-
args: args as SnapshotRpcMethodArgs['readSnapshotFile'],
|
|
84
|
-
}),
|
|
85
|
-
saveSnapshotFile: (id, args) => ({
|
|
86
|
-
id,
|
|
87
|
-
method: 'saveSnapshotFile',
|
|
88
|
-
args: args as SnapshotRpcMethodArgs['saveSnapshotFile'],
|
|
89
|
-
}),
|
|
90
|
-
removeSnapshotFile: (id, args) => ({
|
|
91
|
-
id,
|
|
92
|
-
method: 'removeSnapshotFile',
|
|
93
|
-
args: args as SnapshotRpcMethodArgs['removeSnapshotFile'],
|
|
94
|
-
}),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const toSnapshotRpcRequest = (
|
|
98
|
-
request: BrowserDispatchRequest,
|
|
99
|
-
): SnapshotRpcRequest | null => {
|
|
100
|
-
const builder = snapshotRequestBuilders[
|
|
101
|
-
request.method as SnapshotRpcMethod
|
|
102
|
-
] as
|
|
103
|
-
| ((id: string, args: BrowserDispatchRequest['args']) => SnapshotRpcRequest)
|
|
104
|
-
| undefined;
|
|
105
|
-
return builder ? builder(request.requestId, request.args) : null;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const createHostDispatchRouter = ({
|
|
109
|
-
routerOptions,
|
|
110
|
-
runnerCallbacks,
|
|
111
|
-
runSnapshotRpc,
|
|
112
|
-
extensionHandlers,
|
|
113
|
-
onDuplicateNamespace,
|
|
114
|
-
}: CreateHostDispatchRouterOptions): HostDispatchRouter => {
|
|
115
|
-
const router = new HostDispatchRouter(routerOptions);
|
|
116
|
-
|
|
117
|
-
// Keyed by RunnerDispatchMethod so adding a runner method to that union forces
|
|
118
|
-
// a handler entry here (a missing key is a compile error) — the previous
|
|
119
|
-
// `switch` with `default: break` silently dropped unhandled methods. The
|
|
120
|
-
// `args` casts are checked against each callback's parameter type, so a wrong
|
|
121
|
-
// payload type also fails to compile.
|
|
122
|
-
const runnerMethodHandlers: {
|
|
123
|
-
[M in RunnerDispatchMethod]: (
|
|
124
|
-
args: BrowserDispatchRequest['args'],
|
|
125
|
-
) => Promise<void>;
|
|
126
|
-
} = {
|
|
127
|
-
'file-start': (args) =>
|
|
128
|
-
runnerCallbacks.onTestFileStart(args as RunnerPayload<'file-start'>),
|
|
129
|
-
'file-ready': (args) =>
|
|
130
|
-
runnerCallbacks.onTestFileReady(args as RunnerDispatchFileReadyPayload),
|
|
131
|
-
'suite-start': (args) =>
|
|
132
|
-
runnerCallbacks.onTestSuiteStart(args as RunnerDispatchSuiteStartPayload),
|
|
133
|
-
'suite-result': (args) =>
|
|
134
|
-
runnerCallbacks.onTestSuiteResult(
|
|
135
|
-
args as RunnerDispatchSuiteResultPayload,
|
|
136
|
-
),
|
|
137
|
-
'case-start': (args) =>
|
|
138
|
-
runnerCallbacks.onTestCaseStart(args as RunnerDispatchCaseStartPayload),
|
|
139
|
-
'case-result': (args) =>
|
|
140
|
-
runnerCallbacks.onTestCaseResult(args as RunnerPayload<'case-result'>),
|
|
141
|
-
'file-complete': (args) =>
|
|
142
|
-
runnerCallbacks.onTestFileComplete(
|
|
143
|
-
args as RunnerPayload<'file-complete'>,
|
|
144
|
-
),
|
|
145
|
-
log: (args) => runnerCallbacks.onLog(args as RunnerPayload<'log'>),
|
|
146
|
-
fatal: (args) => runnerCallbacks.onFatal(args as RunnerPayload<'fatal'>),
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
router.register(
|
|
150
|
-
DISPATCH_NAMESPACE_RUNNER,
|
|
151
|
-
async (request: BrowserDispatchRequest) => {
|
|
152
|
-
// `request.method` is untrusted wire data, so the lookup may miss. Unknown
|
|
153
|
-
// methods are ignored for forward-compatibility with newer runners,
|
|
154
|
-
// matching the previous `default: break`.
|
|
155
|
-
await runnerMethodHandlers[request.method as RunnerDispatchMethod]?.(
|
|
156
|
-
request.args,
|
|
157
|
-
);
|
|
158
|
-
},
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
router.register(
|
|
162
|
-
DISPATCH_NAMESPACE_SNAPSHOT,
|
|
163
|
-
async (request: BrowserDispatchRequest) => {
|
|
164
|
-
const snapshotRequest = toSnapshotRpcRequest(request);
|
|
165
|
-
if (!snapshotRequest) {
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
return runSnapshotRpc(snapshotRequest);
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
for (const [namespace, handler] of extensionHandlers ?? []) {
|
|
173
|
-
if (router.has(namespace)) {
|
|
174
|
-
onDuplicateNamespace?.(namespace);
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
router.register(namespace, handler);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return router;
|
|
182
|
-
};
|
package/src/dispatchRouter.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BrowserDispatchRequest,
|
|
3
|
-
BrowserDispatchResponse,
|
|
4
|
-
} from './protocol';
|
|
5
|
-
|
|
6
|
-
type DispatchHandler = (request: BrowserDispatchRequest) => Promise<unknown>;
|
|
7
|
-
|
|
8
|
-
type HostDispatchRouterOptions = {
|
|
9
|
-
isRunTokenStale?: (runToken: number) => boolean;
|
|
10
|
-
onStale?: (request: BrowserDispatchRequest) => void;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const toErrorMessage = (error: unknown): string => {
|
|
14
|
-
return error instanceof Error ? error.message : String(error);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Host-side routing layer for dispatch envelopes.
|
|
19
|
-
* Capabilities are registered by namespace, while routing stays transport-agnostic.
|
|
20
|
-
*/
|
|
21
|
-
export class HostDispatchRouter {
|
|
22
|
-
private handlers = new Map<string, DispatchHandler>();
|
|
23
|
-
private options: HostDispatchRouterOptions;
|
|
24
|
-
|
|
25
|
-
constructor(options?: HostDispatchRouterOptions) {
|
|
26
|
-
this.options = options ?? {};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
register(namespace: string, handler: DispatchHandler): void {
|
|
30
|
-
this.handlers.set(namespace, handler);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
unregister(namespace: string): void {
|
|
34
|
-
this.handlers.delete(namespace);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
has(namespace: string): boolean {
|
|
38
|
-
return this.handlers.has(namespace);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async dispatch(
|
|
42
|
-
request: BrowserDispatchRequest,
|
|
43
|
-
): Promise<BrowserDispatchResponse> {
|
|
44
|
-
const runToken = request.runToken;
|
|
45
|
-
if (
|
|
46
|
-
typeof runToken === 'number' &&
|
|
47
|
-
this.options.isRunTokenStale?.(runToken)
|
|
48
|
-
) {
|
|
49
|
-
// Return a stale marker so callers can drop old-run writes safely.
|
|
50
|
-
this.options.onStale?.(request);
|
|
51
|
-
return {
|
|
52
|
-
requestId: request.requestId,
|
|
53
|
-
runToken,
|
|
54
|
-
stale: true,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const handler = this.handlers.get(request.namespace);
|
|
59
|
-
if (!handler) {
|
|
60
|
-
return {
|
|
61
|
-
requestId: request.requestId,
|
|
62
|
-
runToken,
|
|
63
|
-
error: `No dispatch handler registered for namespace "${request.namespace}"`,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const result = await handler(request);
|
|
69
|
-
return {
|
|
70
|
-
requestId: request.requestId,
|
|
71
|
-
runToken,
|
|
72
|
-
result,
|
|
73
|
-
};
|
|
74
|
-
} catch (error) {
|
|
75
|
-
return {
|
|
76
|
-
requestId: request.requestId,
|
|
77
|
-
runToken,
|
|
78
|
-
error: toErrorMessage(error),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
package/src/env.d.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { DISPATCH_MESSAGE_TYPE, DISPATCH_RPC_BRIDGE_NAME } from './protocol';
|
|
2
|
-
import type {
|
|
3
|
-
BrowserClientMessage,
|
|
4
|
-
BrowserDispatchRequest,
|
|
5
|
-
BrowserHostConfig,
|
|
6
|
-
} from './protocol';
|
|
7
|
-
|
|
8
|
-
declare module '@rstest/browser-manifest' {
|
|
9
|
-
export type ManifestProjectConfig = {
|
|
10
|
-
name: string;
|
|
11
|
-
environmentName: string;
|
|
12
|
-
projectRoot: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type ManifestTestContext = {
|
|
16
|
-
getTestKeys: () => string[];
|
|
17
|
-
loadTest: (key: string) => Promise<unknown>;
|
|
18
|
-
projectRoot: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const projects: ManifestProjectConfig[];
|
|
22
|
-
|
|
23
|
-
export const projectSetupLoaders: Record<
|
|
24
|
-
string,
|
|
25
|
-
Array<() => Promise<unknown>>
|
|
26
|
-
>;
|
|
27
|
-
|
|
28
|
-
export const projectTestContexts: Record<string, ManifestTestContext>;
|
|
29
|
-
|
|
30
|
-
// Backward compatibility exports
|
|
31
|
-
export const projectConfig: ManifestProjectConfig | undefined;
|
|
32
|
-
export const setupLoaders: Array<() => Promise<unknown>>;
|
|
33
|
-
export const getTestKeys: () => string[];
|
|
34
|
-
export const loadTest: (key: string) => Promise<unknown>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
declare global {
|
|
38
|
-
const RSTEST_VERSION: string;
|
|
39
|
-
|
|
40
|
-
interface Window {
|
|
41
|
-
__RSTEST_BROWSER_OPTIONS__?: BrowserHostConfig;
|
|
42
|
-
// Keyed by the sentinel constants so the declared global name and the
|
|
43
|
-
// constant are the same source — renaming a constant moves this key with it,
|
|
44
|
-
// and every `window[CONST]` access site stays in lockstep automatically.
|
|
45
|
-
[DISPATCH_MESSAGE_TYPE]?: (message: BrowserClientMessage) => void;
|
|
46
|
-
[DISPATCH_RPC_BRIDGE_NAME]?: (
|
|
47
|
-
request: BrowserDispatchRequest,
|
|
48
|
-
) => Promise<unknown>;
|
|
49
|
-
__rstest_container_dispatch__?: (data: unknown) => void;
|
|
50
|
-
__rstest_container_on__?: (data: unknown) => void;
|
|
51
|
-
__RSTEST_DONE__?: boolean;
|
|
52
|
-
__RSTEST_TEST_FILES__?: string[];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
type HeadedSerialTask = () => Promise<void>;
|
|
2
|
-
|
|
3
|
-
type HeadedSerialTaskQueue = {
|
|
4
|
-
enqueue: (task: HeadedSerialTask) => Promise<void>;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Serializes headed browser file execution so only one task runs at a time.
|
|
9
|
-
* The queue keeps draining even if an earlier task rejects.
|
|
10
|
-
*/
|
|
11
|
-
export const createHeadedSerialTaskQueue = (): HeadedSerialTaskQueue => {
|
|
12
|
-
let queue: Promise<void> = Promise.resolve();
|
|
13
|
-
|
|
14
|
-
const enqueue = (task: HeadedSerialTask): Promise<void> => {
|
|
15
|
-
const next = queue.then(task);
|
|
16
|
-
queue = next.catch(() => {});
|
|
17
|
-
return next;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
enqueue,
|
|
22
|
-
};
|
|
23
|
-
};
|