@rstest/browser 0.11.0 → 0.11.2

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.
@@ -1,6 +1,98 @@
1
- import type { RstestContext } from '@rstest/core/internal/browser';
1
+ import {
2
+ browserIgnoredRuntimeConfigKeys,
3
+ color,
4
+ logger,
5
+ type RstestContext,
6
+ type RuntimeConfig,
7
+ } from '@rstest/core/internal/browser';
2
8
  import { resolveBrowserViewportPreset } from './viewportPresets';
3
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
+ detectAsyncLeaks: {
49
+ isNonDefault: (config) => config.detectAsyncLeaks === true,
50
+ message: () =>
51
+ 'Ignoring detectAsyncLeaks in browser mode: it relies on node ' +
52
+ 'async_hooks.',
53
+ },
54
+ logHeapUsage: {
55
+ isNonDefault: (config) => config.logHeapUsage === true,
56
+ message: () => 'Ignoring logHeapUsage in browser mode.',
57
+ },
58
+ };
59
+
60
+ /**
61
+ * Browser-ignored keys handled outside the generic warn loop: `coverage` is
62
+ * 'stripped' but gets the dedicated provider check in
63
+ * {@link reportUnsupportedBrowserOptions} (a v8 hard error on browser-only
64
+ * runs, a warning in mixed runs), not a plain "set → warn".
65
+ */
66
+ const speciallyHandledIgnoredKeys: (keyof RuntimeConfig)[] = ['coverage'];
67
+
68
+ /**
69
+ * Every browser-ignored RuntimeConfig key this module checks — the union of the
70
+ * warn descriptors and the specially-handled keys. Exported for the lockstep
71
+ * test that asserts it covers {@link browserIgnoredRuntimeConfigKeys}.
72
+ */
73
+ export const browserValidatedIgnoredKeys: (keyof RuntimeConfig)[] = [
74
+ ...(Object.keys(ignoredKeyWarnings) as (keyof RuntimeConfig)[]),
75
+ ...speciallyHandledIgnoredKeys,
76
+ ];
77
+
78
+ // Anti-#1389 lockstep (runs at module load): fail loudly if the
79
+ // `executorCapabilities` table gains an 'ignored-warn' / 'stripped' browser key
80
+ // that this validation does not cover, instead of it becoming a silent no-op.
81
+ const assertIgnoredKeysCovered = (): void => {
82
+ const covered = new Set<string>(browserValidatedIgnoredKeys);
83
+ const uncovered = browserIgnoredRuntimeConfigKeys.filter(
84
+ (key) => !covered.has(key),
85
+ );
86
+ if (uncovered.length > 0) {
87
+ throw new Error(
88
+ 'Browser config validation is out of sync with executorCapabilities: ' +
89
+ `no check for ignored RuntimeConfig field(s): ${uncovered.join(', ')}. ` +
90
+ 'Add a descriptor to `ignoredKeyWarnings` or `speciallyHandledIgnoredKeys`.',
91
+ );
92
+ }
93
+ };
94
+ assertIgnoredKeysCovered();
95
+
4
96
  const SUPPORTED_PROVIDERS = ['playwright'] as const;
5
97
 
6
98
  const isPlainObject = (value: unknown): value is Record<string, unknown> => {
@@ -42,6 +134,93 @@ const validateViewport = (viewport: unknown): void => {
42
134
  );
43
135
  };
44
136
 
137
+ /**
138
+ * Warn (or hard-error) on node-only config that is silently ignored under
139
+ * `browser.enabled`. Prevents the #1389 class of silent no-ops. The per-key
140
+ * warnings are driven by the `executorCapabilities` table: the loop iterates
141
+ * {@link browserIgnoredRuntimeConfigKeys} and looks each key up in
142
+ * {@link ignoredKeyWarnings}; `coverage` (and `pool`, which is not a
143
+ * RuntimeConfig field) keep bespoke handling below.
144
+ *
145
+ * Scoping rules: `coverage` and `isolate` are global-only config copied onto
146
+ * every project, so a v8 hard-error / isolate warning is gated to browser-only
147
+ * runs — a mixed repo legitimately sets them for its node projects and must not
148
+ * get unsilenceable noise on a correct configuration.
149
+ */
150
+ const reportUnsupportedBrowserOptions = (context: RstestContext): void => {
151
+ const browserProjects = context.projects.filter(
152
+ (project) => project.normalizedConfig.browser.enabled,
153
+ );
154
+ if (browserProjects.length === 0) {
155
+ return;
156
+ }
157
+ const isBrowserOnlyRun = browserProjects.length === context.projects.length;
158
+
159
+ // `coverage`, `pool` and `isolate` are global-only config (not settable per
160
+ // project), read from the root normalized config.
161
+ const globalConfig = context.normalizedConfig;
162
+
163
+ const { coverage } = globalConfig;
164
+ // `list` never collects coverage, so the v8 guard must not block listing.
165
+ if (
166
+ context.command !== 'list' &&
167
+ coverage.enabled &&
168
+ coverage.provider === 'v8'
169
+ ) {
170
+ if (isBrowserOnlyRun) {
171
+ throw new Error(
172
+ "Coverage provider 'v8' is not supported in browser mode: browser " +
173
+ 'projects produce no v8 coverage. Use the default istanbul provider ' +
174
+ "(coverage.provider: 'istanbul') for browser coverage.",
175
+ );
176
+ }
177
+ logger.warn(
178
+ color.yellow(
179
+ "Coverage provider 'v8' produces no coverage for browser project " +
180
+ "files; use the 'istanbul' provider to collect browser coverage. " +
181
+ 'Node projects will still use v8.',
182
+ ),
183
+ );
184
+ }
185
+
186
+ // Node-only options silently ignored under browser.enabled. Collected into a
187
+ // Set so each distinct warning is emitted once, not per project.
188
+ const warnings = new Set<string>();
189
+
190
+ if (globalConfig.pool.type !== 'forks') {
191
+ warnings.add(
192
+ `Ignoring pool.type '${globalConfig.pool.type}' in browser mode.`,
193
+ );
194
+ }
195
+ if (globalConfig.pool.execArgv && globalConfig.pool.execArgv.length > 0) {
196
+ warnings.add('Ignoring pool.execArgv in browser mode.');
197
+ }
198
+ // Table-driven warnings: for every browser-ignored RuntimeConfig key, warn
199
+ // when a browser project sets it to a non-default value. `isolate` (and any
200
+ // other global-only key) is copied onto every project, so reading it per
201
+ // project matches the old global read; the Set dedupes the repeats.
202
+ for (const project of browserProjects) {
203
+ const config = project.normalizedConfig;
204
+ for (const key of browserIgnoredRuntimeConfigKeys) {
205
+ const descriptor = ignoredKeyWarnings[key];
206
+ if (!descriptor) {
207
+ // Specially handled elsewhere (e.g. `coverage`).
208
+ continue;
209
+ }
210
+ if (descriptor.browserOnly && !isBrowserOnlyRun) {
211
+ continue;
212
+ }
213
+ if (descriptor.isNonDefault(config)) {
214
+ warnings.add(descriptor.message(config));
215
+ }
216
+ }
217
+ }
218
+
219
+ for (const message of warnings) {
220
+ logger.warn(color.yellow(message));
221
+ }
222
+ };
223
+
45
224
  export const validateBrowserConfig = (context: RstestContext): void => {
46
225
  for (const project of context.projects) {
47
226
  const { browser, output } = project.normalizedConfig;
@@ -73,4 +252,6 @@ export const validateBrowserConfig = (context: RstestContext): void => {
73
252
  );
74
253
  }
75
254
  }
255
+
256
+ reportUnsupportedBrowserOptions(context);
76
257
  };