@remix-run/test 0.1.0 → 0.3.0
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/README.md +161 -50
- package/dist/app/client/entry.d.ts +2 -0
- package/dist/app/client/entry.d.ts.map +1 -0
- package/dist/app/client/entry.js +328 -0
- package/dist/app/client/iframe.d.ts +2 -0
- package/dist/app/client/iframe.d.ts.map +1 -0
- package/dist/app/client/iframe.js +22 -0
- package/dist/app/server.d.ts +6 -0
- package/dist/app/server.d.ts.map +1 -0
- package/dist/app/server.js +303 -0
- package/dist/cli-entry.d.ts +3 -0
- package/dist/cli-entry.d.ts.map +1 -0
- package/dist/cli-entry.js +14 -0
- package/dist/cli.d.ts +7 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +319 -140
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/colors.d.ts +2 -0
- package/dist/lib/colors.d.ts.map +1 -0
- package/dist/lib/colors.js +2 -0
- package/dist/lib/config.d.ts +59 -14
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +181 -38
- package/dist/lib/context.d.ts +37 -13
- package/dist/lib/context.d.ts.map +1 -1
- package/dist/lib/context.js +19 -3
- package/dist/lib/coverage-loader.d.ts +16 -0
- package/dist/lib/coverage-loader.d.ts.map +1 -0
- package/dist/lib/coverage-loader.js +20 -0
- package/dist/lib/coverage.d.ts +28 -0
- package/dist/lib/coverage.d.ts.map +1 -0
- package/dist/lib/coverage.js +212 -0
- package/dist/lib/executor.d.ts +3 -26
- package/dist/lib/executor.d.ts.map +1 -1
- package/dist/lib/executor.js +11 -6
- package/dist/lib/fake-timers.d.ts +13 -0
- package/dist/lib/fake-timers.d.ts.map +1 -0
- package/dist/lib/fake-timers.js +64 -0
- package/dist/lib/import-module.d.ts +2 -0
- package/dist/lib/import-module.d.ts.map +1 -0
- package/dist/lib/import-module.js +38 -0
- package/dist/lib/normalize.d.ts +2 -0
- package/dist/lib/normalize.d.ts.map +1 -0
- package/dist/lib/{utils.js → normalize.js} +0 -9
- package/dist/lib/playwright.d.ts +1 -1
- package/dist/lib/playwright.d.ts.map +1 -1
- package/dist/lib/playwright.js +5 -8
- package/dist/lib/reporters/dot.d.ts +1 -2
- package/dist/lib/reporters/dot.d.ts.map +1 -1
- package/dist/lib/reporters/dot.js +12 -1
- package/dist/lib/reporters/files.d.ts +1 -2
- package/dist/lib/reporters/files.d.ts.map +1 -1
- package/dist/lib/reporters/files.js +12 -1
- package/dist/lib/reporters/index.d.ts +4 -5
- package/dist/lib/reporters/index.d.ts.map +1 -1
- package/dist/lib/reporters/index.js +3 -3
- package/dist/lib/reporters/results.d.ts +30 -0
- package/dist/lib/reporters/results.d.ts.map +1 -0
- package/dist/lib/reporters/results.js +1 -0
- package/dist/lib/reporters/spec.d.ts +1 -2
- package/dist/lib/reporters/spec.d.ts.map +1 -1
- package/dist/lib/reporters/spec.js +12 -1
- package/dist/lib/reporters/tap.d.ts +1 -2
- package/dist/lib/reporters/tap.d.ts.map +1 -1
- package/dist/lib/reporters/tap.js +11 -1
- package/dist/lib/runner-browser.d.ts +21 -0
- package/dist/lib/runner-browser.d.ts.map +1 -0
- package/dist/lib/runner-browser.js +123 -0
- package/dist/lib/runner.d.ts +24 -2
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +216 -38
- package/dist/lib/runtime.d.ts +2 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +2 -0
- package/dist/lib/ts-transform.d.ts +4 -0
- package/dist/lib/ts-transform.d.ts.map +1 -0
- package/dist/lib/ts-transform.js +29 -0
- package/dist/lib/worker-e2e-file.d.ts +11 -0
- package/dist/lib/worker-e2e-file.d.ts.map +1 -0
- package/dist/lib/worker-e2e-file.js +69 -0
- package/dist/lib/worker-e2e.js +11 -46
- package/dist/lib/worker-process.d.ts +2 -0
- package/dist/lib/worker-process.d.ts.map +1 -0
- package/dist/lib/worker-process.js +55 -0
- package/dist/lib/worker-results.d.ts +3 -0
- package/dist/lib/worker-results.d.ts.map +1 -0
- package/dist/lib/worker-results.js +20 -0
- package/dist/lib/worker-server.d.ts +10 -0
- package/dist/lib/worker-server.d.ts.map +1 -0
- package/dist/lib/worker-server.js +113 -0
- package/dist/lib/worker.js +7 -28
- package/dist/test/coverage/fixture.d.ts +5 -0
- package/dist/test/coverage/fixture.d.ts.map +1 -0
- package/dist/test/coverage/fixture.js +32 -0
- package/dist/test/coverage/test-browser.d.ts +2 -0
- package/dist/test/coverage/test-browser.d.ts.map +1 -0
- package/dist/test/coverage/test-browser.js +24 -0
- package/dist/test/coverage/test-e2e.d.ts +2 -0
- package/dist/test/coverage/test-e2e.d.ts.map +1 -0
- package/dist/test/coverage/test-e2e.js +60 -0
- package/dist/test/coverage/test-unit.d.ts +2 -0
- package/dist/test/coverage/test-unit.d.ts.map +1 -0
- package/dist/test/coverage/test-unit.js +27 -0
- package/dist/test/framework.test.browser.d.ts +2 -0
- package/dist/test/framework.test.browser.d.ts.map +1 -0
- package/dist/test/framework.test.browser.js +107 -0
- package/dist/test/framework.test.e2e.d.ts.map +1 -0
- package/dist/test/framework.test.e2e.js +34 -0
- package/package.json +30 -9
- package/src/app/client/entry.ts +357 -0
- package/src/app/client/iframe.ts +18 -0
- package/src/app/server.ts +336 -0
- package/src/cli-entry.ts +15 -0
- package/src/cli.ts +382 -145
- package/src/index.ts +2 -1
- package/src/lib/colors.ts +3 -0
- package/src/lib/config.ts +266 -54
- package/src/lib/context.ts +59 -17
- package/src/lib/coverage-loader.ts +31 -0
- package/src/lib/coverage.ts +320 -0
- package/src/lib/executor.ts +18 -35
- package/src/lib/fake-timers.ts +89 -0
- package/src/lib/import-module.ts +39 -0
- package/src/lib/{utils.ts → normalize.ts} +0 -18
- package/src/lib/playwright.ts +5 -7
- package/src/lib/reporters/dot.ts +12 -2
- package/src/lib/reporters/files.ts +12 -2
- package/src/lib/reporters/index.ts +4 -5
- package/src/lib/reporters/results.ts +29 -0
- package/src/lib/reporters/spec.ts +12 -2
- package/src/lib/reporters/tap.ts +11 -2
- package/src/lib/runner-browser.ts +171 -0
- package/src/lib/runner.ts +308 -53
- package/src/lib/runtime.ts +2 -0
- package/src/lib/ts-transform.ts +36 -0
- package/src/lib/worker-e2e-file.ts +98 -0
- package/src/lib/worker-e2e.ts +14 -49
- package/src/lib/worker-process.ts +69 -0
- package/src/lib/worker-results.ts +22 -0
- package/src/lib/worker-server.ts +123 -0
- package/src/lib/worker.ts +8 -28
- package/src/test/coverage/fixture.ts +34 -0
- package/src/test/coverage/test-browser.ts +29 -0
- package/src/test/coverage/test-e2e.ts +70 -0
- package/src/test/coverage/test-unit.ts +32 -0
- package/tsconfig.json +3 -1
- package/dist/lib/e2e-server.d.ts +0 -11
- package/dist/lib/e2e-server.d.ts.map +0 -1
- package/dist/lib/e2e-server.js +0 -15
- package/dist/lib/framework.test.d.ts +0 -2
- package/dist/lib/framework.test.d.ts.map +0 -1
- package/dist/lib/framework.test.e2e.d.ts.map +0 -1
- package/dist/lib/framework.test.e2e.js +0 -29
- package/dist/lib/framework.test.js +0 -283
- package/dist/lib/utils.d.ts +0 -16
- package/dist/lib/utils.d.ts.map +0 -1
- package/src/lib/e2e-server.ts +0 -28
- /package/dist/{lib → test}/framework.test.e2e.d.ts +0 -0
package/dist/lib/config.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
+
import * as fsp from 'node:fs/promises';
|
|
1
2
|
import * as os from 'node:os';
|
|
2
3
|
import * as path from 'node:path';
|
|
3
|
-
import
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import * as util from 'node:util';
|
|
5
|
-
import {
|
|
6
|
+
import { importModule } from "./import-module.js";
|
|
7
|
+
export const IS_RUNNING_FROM_SRC = path.extname(new URL(import.meta.url).pathname) === '.ts';
|
|
8
|
+
/*
|
|
9
|
+
* The root directory for the test code. Coverage URLs are emitted as
|
|
10
|
+
* `/scripts/<rel-from-rootDir>` and resolved back via the same anchor.
|
|
11
|
+
*
|
|
12
|
+
* - In a published install: `process.cwd()`, since deps and user source all
|
|
13
|
+
* live under it.
|
|
14
|
+
* - In monorepo src mode: the monorepo root, computed by walking back from
|
|
15
|
+
* the resolved `@remix-run/test` source path. `process.cwd()` doesn't work
|
|
16
|
+
* here because workspace deps and node_modules live above the per-package
|
|
17
|
+
* cwd.
|
|
18
|
+
*/
|
|
19
|
+
export function getBrowserTestRootDir() {
|
|
20
|
+
return IS_RUNNING_FROM_SRC
|
|
21
|
+
? // Resolve to packages/test/src/index.ts and the pop 3 directories off to the repo root
|
|
22
|
+
path
|
|
23
|
+
.dirname(fileURLToPath(import.meta.resolve('@remix-run/test')))
|
|
24
|
+
.split(path.sep)
|
|
25
|
+
.slice(0, -3)
|
|
26
|
+
.join(path.sep)
|
|
27
|
+
: process.cwd();
|
|
28
|
+
}
|
|
6
29
|
// prettier-ignore
|
|
7
30
|
// Note: `description` is not a field used by parseArgs(), it's an additional field
|
|
8
31
|
// we use for `--help`
|
|
@@ -15,13 +38,25 @@ const cliOptions = {
|
|
|
15
38
|
type: 'boolean',
|
|
16
39
|
description: 'Open browser window and keep open after tests finish',
|
|
17
40
|
},
|
|
41
|
+
'glob.browser': {
|
|
42
|
+
type: 'string',
|
|
43
|
+
multiple: true,
|
|
44
|
+
description: 'Glob pattern(s) for browser test files',
|
|
45
|
+
},
|
|
18
46
|
'glob.e2e': {
|
|
19
47
|
type: 'string',
|
|
20
|
-
|
|
48
|
+
multiple: true,
|
|
49
|
+
description: 'Glob pattern(s) for E2E test files',
|
|
50
|
+
},
|
|
51
|
+
'glob.exclude': {
|
|
52
|
+
type: 'string',
|
|
53
|
+
multiple: true,
|
|
54
|
+
description: 'Glob pattern(s) for paths to exclude from discovery',
|
|
21
55
|
},
|
|
22
56
|
'glob.test': {
|
|
23
57
|
type: 'string',
|
|
24
|
-
|
|
58
|
+
multiple: true,
|
|
59
|
+
description: 'Glob pattern(s) for all test files',
|
|
25
60
|
},
|
|
26
61
|
concurrency: {
|
|
27
62
|
type: 'string',
|
|
@@ -32,6 +67,40 @@ const cliOptions = {
|
|
|
32
67
|
type: 'string',
|
|
33
68
|
description: 'Path to config file (default: remix-test.config.ts)',
|
|
34
69
|
},
|
|
70
|
+
coverage: {
|
|
71
|
+
type: 'boolean',
|
|
72
|
+
description: 'Enable or disable coverage collection (default: false)',
|
|
73
|
+
},
|
|
74
|
+
'coverage.dir': {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: 'Directory to output coverage reports (default: .coverage)',
|
|
77
|
+
},
|
|
78
|
+
'coverage.include': {
|
|
79
|
+
type: 'string',
|
|
80
|
+
multiple: true,
|
|
81
|
+
description: 'Glob pattern(s) for files to include in coverage',
|
|
82
|
+
},
|
|
83
|
+
'coverage.exclude': {
|
|
84
|
+
type: 'string',
|
|
85
|
+
multiple: true,
|
|
86
|
+
description: 'Glob pattern(s) for files to exclude from coverage',
|
|
87
|
+
},
|
|
88
|
+
'coverage.branches': {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Branches coverage threshold percentage',
|
|
91
|
+
},
|
|
92
|
+
'coverage.functions': {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'Functions coverage threshold percentage',
|
|
95
|
+
},
|
|
96
|
+
'coverage.lines': {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'Lines coverage threshold percentage',
|
|
99
|
+
},
|
|
100
|
+
'coverage.statements': {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'Statements coverage threshold percentage',
|
|
103
|
+
},
|
|
35
104
|
setup: {
|
|
36
105
|
type: 'string',
|
|
37
106
|
short: 's',
|
|
@@ -44,7 +113,12 @@ const cliOptions = {
|
|
|
44
113
|
project: {
|
|
45
114
|
type: 'string',
|
|
46
115
|
short: 'p',
|
|
47
|
-
|
|
116
|
+
multiple: true,
|
|
117
|
+
description: 'Filter to specific Playwright project(s)',
|
|
118
|
+
},
|
|
119
|
+
pool: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
description: 'Pool used to run server and E2E test files: forks, threads (default: forks)',
|
|
48
122
|
},
|
|
49
123
|
reporter: {
|
|
50
124
|
type: 'string',
|
|
@@ -54,7 +128,8 @@ const cliOptions = {
|
|
|
54
128
|
type: {
|
|
55
129
|
type: 'string',
|
|
56
130
|
short: 't',
|
|
57
|
-
|
|
131
|
+
multiple: true,
|
|
132
|
+
description: 'Test types to run (default: server, browser, e2e)',
|
|
58
133
|
},
|
|
59
134
|
watch: {
|
|
60
135
|
type: 'boolean',
|
|
@@ -68,33 +143,41 @@ const defaultValues = {
|
|
|
68
143
|
open: false,
|
|
69
144
|
},
|
|
70
145
|
concurrency: os.availableParallelism(),
|
|
146
|
+
coverage: {
|
|
147
|
+
dir: '.coverage',
|
|
148
|
+
include: undefined,
|
|
149
|
+
exclude: undefined,
|
|
150
|
+
statements: undefined,
|
|
151
|
+
lines: undefined,
|
|
152
|
+
branches: undefined,
|
|
153
|
+
functions: undefined,
|
|
154
|
+
},
|
|
71
155
|
glob: {
|
|
72
|
-
test: '**/*.test
|
|
73
|
-
|
|
156
|
+
test: ['**/*.test{,.e2e,.browser}.{ts,tsx}'],
|
|
157
|
+
browser: ['**/*.test.browser.{ts,tsx}'],
|
|
158
|
+
e2e: ['**/*.test.e2e.{ts,tsx}'],
|
|
159
|
+
exclude: ['node_modules/**'],
|
|
74
160
|
},
|
|
75
|
-
|
|
76
|
-
type: 'server,e2e',
|
|
77
|
-
setup: undefined,
|
|
161
|
+
pool: 'forks',
|
|
78
162
|
playwrightConfig: undefined,
|
|
79
163
|
project: undefined,
|
|
164
|
+
reporter: process.env.CI === 'true' ? 'files' : 'spec',
|
|
165
|
+
setup: undefined,
|
|
166
|
+
type: ['server', 'browser', 'e2e'],
|
|
80
167
|
watch: false,
|
|
81
168
|
};
|
|
82
|
-
export async function loadConfig() {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
let parsed = parseCliArgs();
|
|
88
|
-
let fileConfig = await loadConfigFile(parsed.values.config);
|
|
169
|
+
export async function loadConfig(args = process.argv.slice(2), cwd = process.cwd()) {
|
|
170
|
+
let parsed = parseCliArgs(args);
|
|
171
|
+
let fileConfig = await loadConfigFile(parsed.values.config, cwd);
|
|
89
172
|
let config = resolveConfig(fileConfig, parsed);
|
|
90
173
|
return config;
|
|
91
174
|
}
|
|
92
|
-
function
|
|
175
|
+
export function getRemixTestHelpText(_target = process.stdout) {
|
|
93
176
|
let lines = [
|
|
94
|
-
'Usage: remix-test [glob] [options]',
|
|
177
|
+
'Usage: remix-test [glob...] [options]',
|
|
95
178
|
'',
|
|
96
179
|
'Arguments:',
|
|
97
|
-
` glob Glob pattern for test files (default: "${defaultValues.glob.test}")`,
|
|
180
|
+
` glob Glob pattern(s) for test files (default: "${defaultValues.glob.test.join(', ')}")`,
|
|
98
181
|
'',
|
|
99
182
|
'Options:',
|
|
100
183
|
];
|
|
@@ -106,47 +189,107 @@ function generateHelp() {
|
|
|
106
189
|
lines.push(` ${'-h, --help'.padEnd(30)} Show this help message`);
|
|
107
190
|
return lines.join('\n');
|
|
108
191
|
}
|
|
109
|
-
function parseCliArgs(args
|
|
192
|
+
function parseCliArgs(args) {
|
|
110
193
|
return util.parseArgs({ args, options: cliOptions, allowPositionals: true });
|
|
111
194
|
}
|
|
195
|
+
function toArray(value) {
|
|
196
|
+
return Array.isArray(value) ? [...value] : [value];
|
|
197
|
+
}
|
|
198
|
+
function toCommaSeparatedArray(value) {
|
|
199
|
+
return toArray(value).flatMap((item) => item
|
|
200
|
+
.split(',')
|
|
201
|
+
.map((part) => part.trim())
|
|
202
|
+
.filter(Boolean));
|
|
203
|
+
}
|
|
112
204
|
function resolveConfig(fileConfig, { values: cliValues, positionals }) {
|
|
205
|
+
let fileCoverage = typeof fileConfig.coverage === 'boolean' ? {} : fileConfig.coverage || {};
|
|
113
206
|
return {
|
|
114
207
|
glob: {
|
|
115
|
-
test: positionals
|
|
116
|
-
|
|
117
|
-
fileConfig.glob?.test ??
|
|
118
|
-
|
|
119
|
-
e2e: cliValues['glob.e2e'] ?? fileConfig.glob?.e2e ?? defaultValues.glob.e2e,
|
|
208
|
+
test: toArray(positionals.length > 0
|
|
209
|
+
? positionals
|
|
210
|
+
: (cliValues['glob.test'] ?? fileConfig.glob?.test ?? defaultValues.glob.test)),
|
|
211
|
+
browser: toArray(cliValues['glob.browser'] ?? fileConfig.glob?.browser ?? defaultValues.glob.browser),
|
|
212
|
+
e2e: toArray(cliValues['glob.e2e'] ?? fileConfig.glob?.e2e ?? defaultValues.glob.e2e),
|
|
213
|
+
exclude: toArray(cliValues['glob.exclude'] ?? fileConfig.glob?.exclude ?? defaultValues.glob.exclude),
|
|
120
214
|
},
|
|
121
215
|
browser: {
|
|
122
216
|
echo: cliValues['browser.echo'] ?? fileConfig.browser?.echo ?? defaultValues.browser.echo,
|
|
123
217
|
open: cliValues['browser.open'] ?? fileConfig.browser?.open ?? defaultValues.browser.open,
|
|
124
218
|
},
|
|
125
219
|
concurrency: Number(cliValues.concurrency ?? fileConfig.concurrency ?? defaultValues.concurrency),
|
|
220
|
+
coverage: cliValues.coverage === true || !!fileConfig.coverage
|
|
221
|
+
? {
|
|
222
|
+
dir: cliValues['coverage.dir'] ?? fileCoverage.dir ?? defaultValues.coverage.dir,
|
|
223
|
+
include: (() => {
|
|
224
|
+
let raw = cliValues['coverage.include'] ??
|
|
225
|
+
fileCoverage.include ??
|
|
226
|
+
defaultValues.coverage.include;
|
|
227
|
+
return raw === undefined ? undefined : toArray(raw);
|
|
228
|
+
})(),
|
|
229
|
+
exclude: (() => {
|
|
230
|
+
let raw = cliValues['coverage.exclude'] ??
|
|
231
|
+
fileCoverage.exclude ??
|
|
232
|
+
defaultValues.coverage.exclude;
|
|
233
|
+
return raw === undefined ? undefined : toArray(raw);
|
|
234
|
+
})(),
|
|
235
|
+
statements: cliValues['coverage.statements'] !== undefined
|
|
236
|
+
? Number(cliValues['coverage.statements'])
|
|
237
|
+
: fileCoverage.statements !== undefined
|
|
238
|
+
? Number(fileCoverage.statements)
|
|
239
|
+
: undefined,
|
|
240
|
+
lines: cliValues['coverage.lines'] !== undefined
|
|
241
|
+
? Number(cliValues['coverage.lines'])
|
|
242
|
+
: fileCoverage.lines !== undefined
|
|
243
|
+
? Number(fileCoverage.lines)
|
|
244
|
+
: undefined,
|
|
245
|
+
branches: cliValues['coverage.branches'] !== undefined
|
|
246
|
+
? Number(cliValues['coverage.branches'])
|
|
247
|
+
: fileCoverage.branches !== undefined
|
|
248
|
+
? Number(fileCoverage.branches)
|
|
249
|
+
: undefined,
|
|
250
|
+
functions: cliValues['coverage.functions'] !== undefined
|
|
251
|
+
? Number(cliValues['coverage.functions'])
|
|
252
|
+
: fileCoverage.functions !== undefined
|
|
253
|
+
? Number(fileCoverage.functions)
|
|
254
|
+
: undefined,
|
|
255
|
+
}
|
|
256
|
+
: undefined,
|
|
126
257
|
setup: cliValues.setup ?? fileConfig.setup ?? defaultValues.setup,
|
|
127
258
|
playwrightConfig: cliValues.playwrightConfig ?? fileConfig.playwrightConfig ?? defaultValues.playwrightConfig,
|
|
128
|
-
|
|
259
|
+
pool: resolvePool(cliValues.pool ?? fileConfig.pool ?? defaultValues.pool),
|
|
260
|
+
project: (() => {
|
|
261
|
+
let raw = cliValues.project ?? fileConfig.project ?? defaultValues.project;
|
|
262
|
+
return raw === undefined ? undefined : toCommaSeparatedArray(raw);
|
|
263
|
+
})(),
|
|
129
264
|
reporter: cliValues.reporter ?? fileConfig.reporter ?? defaultValues.reporter,
|
|
130
|
-
type: cliValues.type ?? fileConfig.type ?? defaultValues.type,
|
|
265
|
+
type: toCommaSeparatedArray(cliValues.type ?? fileConfig.type ?? defaultValues.type),
|
|
131
266
|
watch: cliValues.watch ?? fileConfig.watch ?? defaultValues.watch,
|
|
132
267
|
};
|
|
133
268
|
}
|
|
134
|
-
|
|
269
|
+
function resolvePool(value) {
|
|
270
|
+
if (value === 'forks' || value === 'threads') {
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
throw new Error(`Unsupported test pool "${value}". Supported pools are: forks, threads`);
|
|
274
|
+
}
|
|
275
|
+
async function loadConfigFile(configPath, cwd) {
|
|
135
276
|
let candidates = configPath
|
|
136
|
-
? [path.resolve(
|
|
137
|
-
: [
|
|
138
|
-
path.join(process.cwd(), 'remix-test.config.ts'),
|
|
139
|
-
path.join(process.cwd(), 'remix-test.config.js'),
|
|
140
|
-
];
|
|
277
|
+
? [path.resolve(cwd, configPath)]
|
|
278
|
+
: [path.join(cwd, 'remix-test.config.ts'), path.join(cwd, 'remix-test.config.js')];
|
|
141
279
|
for (let candidate of candidates) {
|
|
142
280
|
try {
|
|
143
281
|
await fsp.access(candidate);
|
|
144
|
-
let mod = await tsImport(candidate, { parentURL: import.meta.url });
|
|
145
|
-
return mod.default ?? mod;
|
|
146
282
|
}
|
|
147
283
|
catch {
|
|
148
|
-
// not found
|
|
284
|
+
// not found — try the next candidate
|
|
285
|
+
continue;
|
|
149
286
|
}
|
|
287
|
+
// The file exists; let import errors propagate rather than silently
|
|
288
|
+
// falling through to defaults — that masking is what hid "Windows
|
|
289
|
+
// absolute paths aren't valid ESM specifiers" by classifying every
|
|
290
|
+
// browser test as a server test.
|
|
291
|
+
let mod = await importModule(candidate, import.meta);
|
|
292
|
+
return mod.default ?? mod;
|
|
150
293
|
}
|
|
151
294
|
return {};
|
|
152
295
|
}
|
package/dist/lib/context.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { Browser, Page } from 'playwright';
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
2
|
+
import type { V8CoverageEntry } from './coverage.ts';
|
|
3
|
+
import { type FakeTimers } from './fake-timers.ts';
|
|
4
|
+
import { type MockCall, type MockContext, type MockFunction } from './mock.ts';
|
|
4
5
|
import type { getPlaywrightPageOptions } from './playwright.ts';
|
|
6
|
+
/**
|
|
7
|
+
* The shape `t.serve()` consumes. Matches the result of `createTestServer`
|
|
8
|
+
* from `@remix-run/node-fetch-server/test`, but any object with a `baseUrl`
|
|
9
|
+
* and async `close()` works.
|
|
10
|
+
*/
|
|
11
|
+
export interface TestServer {
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Test Context providing utilities for testing via remix-test. The context is
|
|
7
17
|
* passed as the first argument to the {@link test}/{@link it} functions.
|
|
@@ -49,21 +59,35 @@ export interface TestContext {
|
|
|
49
59
|
method<T extends object, K extends keyof T>(obj: T, methodName: K, impl?: Function): MockFunction;
|
|
50
60
|
};
|
|
51
61
|
/**
|
|
52
|
-
*
|
|
62
|
+
* Activates fake timers for testing time-dependent code.
|
|
53
63
|
*
|
|
54
|
-
* @
|
|
55
|
-
* @returns {Promise<Page>} A promise resolving to a page instance for the server
|
|
64
|
+
* @returns {FakeTimers} A fake timers instance for controlling time
|
|
56
65
|
*/
|
|
57
|
-
|
|
66
|
+
useFakeTimers(): FakeTimers;
|
|
67
|
+
/**
|
|
68
|
+
* Wires a running test server up to a Playwright page so the test can drive
|
|
69
|
+
* it. The server is closed automatically when the test ends. Pair with
|
|
70
|
+
* `createTestServer` from `@remix-run/node-fetch-server/test` (or any other
|
|
71
|
+
* source of a `{ baseUrl, close }` handle) to spin up the server first.
|
|
72
|
+
*
|
|
73
|
+
* @param server - The running server the page should target
|
|
74
|
+
* @returns A `Page` whose `baseURL` is set to `server.baseUrl`.
|
|
75
|
+
*/
|
|
76
|
+
serve(server: TestServer): Promise<Page>;
|
|
77
|
+
}
|
|
78
|
+
export interface CreateTestContextOptions {
|
|
79
|
+
addE2ECoverageEntries: (value: {
|
|
80
|
+
entries: V8CoverageEntry[];
|
|
81
|
+
baseUrl: string;
|
|
82
|
+
}) => void;
|
|
83
|
+
browser: Browser;
|
|
84
|
+
coverage: boolean;
|
|
85
|
+
open: boolean;
|
|
86
|
+
playwrightPageOptions: ReturnType<typeof getPlaywrightPageOptions>;
|
|
58
87
|
}
|
|
59
|
-
export declare function createTestContext(options: {
|
|
60
|
-
createServer?: CreateServerFunction;
|
|
61
|
-
browser?: Browser;
|
|
62
|
-
open?: boolean;
|
|
63
|
-
playwrightPageOptions?: ReturnType<typeof getPlaywrightPageOptions>;
|
|
64
|
-
}): {
|
|
88
|
+
export declare function createTestContext(options?: CreateTestContextOptions): {
|
|
65
89
|
testContext: TestContext;
|
|
66
90
|
cleanup(): Promise<void>;
|
|
67
91
|
};
|
|
68
|
-
export type {
|
|
92
|
+
export type { MockCall, MockContext, MockFunction };
|
|
69
93
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACpE,OAAO,EAAQ,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AACpF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE/D;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;IAE3B;;;OAGG;IACH,IAAI,EAAE;QACJ;;;;;;WAMG;QACH,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;QAEhE;;;;;;;;;;WAUG;QACH,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EACxC,GAAG,EAAE,CAAC,EACN,UAAU,EAAE,CAAC,EACb,IAAI,CAAC,EAAE,QAAQ,GACd,YAAY,CAAA;KAChB,CAAA;IAED;;;;OAIG;IACH,aAAa,IAAI,UAAU,CAAA;IAE3B;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,eAAe,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACvF,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;IACb,qBAAqB,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;CACnE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG;IACrE,WAAW,EAAE,WAAW,CAAA;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB,CAkEA;AAED,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,CAAA"}
|
package/dist/lib/context.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createFakeTimers } from "./fake-timers.js";
|
|
1
2
|
import { mock } from "./mock.js";
|
|
2
3
|
export function createTestContext(options) {
|
|
3
4
|
let cleanups = [];
|
|
@@ -14,11 +15,15 @@ export function createTestContext(options) {
|
|
|
14
15
|
after(fn) {
|
|
15
16
|
cleanups.push(fn);
|
|
16
17
|
},
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
useFakeTimers() {
|
|
19
|
+
let timers = createFakeTimers();
|
|
20
|
+
cleanups.push(timers.restore);
|
|
21
|
+
return timers;
|
|
22
|
+
},
|
|
23
|
+
async serve(server) {
|
|
24
|
+
if (!options || !options.browser) {
|
|
19
25
|
throw new Error('t.serve() is only available in E2E test suites');
|
|
20
26
|
}
|
|
21
|
-
let server = await options.createServer(handler);
|
|
22
27
|
let page = await options.browser.newPage({
|
|
23
28
|
...options.playwrightPageOptions,
|
|
24
29
|
baseURL: server.baseUrl,
|
|
@@ -29,6 +34,17 @@ export function createTestContext(options) {
|
|
|
29
34
|
if (options.playwrightPageOptions?.actionTimeout != null) {
|
|
30
35
|
page.setDefaultTimeout(options.playwrightPageOptions.actionTimeout);
|
|
31
36
|
}
|
|
37
|
+
let coverageEnabled = options.coverage && options.browser.browserType().name() === 'chromium';
|
|
38
|
+
if (coverageEnabled) {
|
|
39
|
+
await page.coverage.startJSCoverage({ resetOnNavigation: false });
|
|
40
|
+
cleanups.push(async () => {
|
|
41
|
+
let entries = await page.coverage.stopJSCoverage();
|
|
42
|
+
options.addE2ECoverageEntries?.({
|
|
43
|
+
entries: entries,
|
|
44
|
+
baseUrl: server.baseUrl,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
32
48
|
cleanups.push(async () => {
|
|
33
49
|
if (!options.open) {
|
|
34
50
|
await page.close();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function load(url: string, context: {
|
|
2
|
+
format?: string;
|
|
3
|
+
}, nextLoad: (url: string, context: {
|
|
4
|
+
format?: string;
|
|
5
|
+
}) => Promise<{
|
|
6
|
+
format: string;
|
|
7
|
+
source: string;
|
|
8
|
+
}>): Promise<{
|
|
9
|
+
format: string;
|
|
10
|
+
source: string;
|
|
11
|
+
} | {
|
|
12
|
+
format: string;
|
|
13
|
+
source: string;
|
|
14
|
+
shortCircuit: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=coverage-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-loader.d.ts","sourceRoot":"","sources":["../../src/lib/coverage-loader.ts"],"names":[],"mappings":"AAYA,wBAAsB,IAAI,CACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5B,QAAQ,EAAE,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KACzB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;;;;;;;GAYjD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { transformTypeScript } from "./ts-transform.js";
|
|
4
|
+
// Custom ESM loader hook for TypeScript files.
|
|
5
|
+
//
|
|
6
|
+
// Replaces tsx's minified transformation with an un-minified esbuild transform
|
|
7
|
+
// that preserves line structure. This ensures V8 coverage byte offsets map
|
|
8
|
+
// cleanly to TypeScript source lines via the inline source map, giving
|
|
9
|
+
// accurate per-line coverage rather than collapsing multiple statements onto
|
|
10
|
+
// a single minified line.
|
|
11
|
+
export async function load(url, context, nextLoad) {
|
|
12
|
+
let cleanUrl = url.includes('?') ? url.slice(0, url.indexOf('?')) : url;
|
|
13
|
+
if (!cleanUrl.endsWith('.ts') && !cleanUrl.endsWith('.tsx')) {
|
|
14
|
+
return nextLoad(url, context);
|
|
15
|
+
}
|
|
16
|
+
let filePath = fileURLToPath(cleanUrl);
|
|
17
|
+
let source = await readFile(filePath, 'utf-8');
|
|
18
|
+
let { code } = await transformTypeScript(source, filePath);
|
|
19
|
+
return { format: 'module', source: code, shortCircuit: true };
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { createCoverageMap as CreateCoverageMap } from 'istanbul-lib-coverage';
|
|
2
|
+
export interface CoverageConfig {
|
|
3
|
+
dir: string;
|
|
4
|
+
include?: string[];
|
|
5
|
+
exclude?: string[];
|
|
6
|
+
statements?: number;
|
|
7
|
+
lines?: number;
|
|
8
|
+
branches?: number;
|
|
9
|
+
functions?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface V8CoverageEntry {
|
|
12
|
+
url: string;
|
|
13
|
+
source?: string;
|
|
14
|
+
functions: Array<{
|
|
15
|
+
functionName: string;
|
|
16
|
+
isBlockCoverage: boolean;
|
|
17
|
+
ranges: Array<{
|
|
18
|
+
startOffset: number;
|
|
19
|
+
endOffset: number;
|
|
20
|
+
count: number;
|
|
21
|
+
}>;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export type CoverageMap = ReturnType<typeof CreateCoverageMap>;
|
|
25
|
+
export declare function collectServerCoverageMap(coverageDataDir: string, cwd: string, testFiles: Set<string>): Promise<CoverageMap | null>;
|
|
26
|
+
export declare function collectCoverageMapFromPlaywright(entries: V8CoverageEntry[], rootDir: string, testFiles: Set<string>, resolveRelativePath: (url: string) => Promise<string | null>): Promise<CoverageMap | null>;
|
|
27
|
+
export declare function generateCombinedCoverageReport(maps: (CoverageMap | null | undefined)[], cwd: string, config: CoverageConfig): Promise<boolean>;
|
|
28
|
+
//# sourceMappingURL=coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/lib/coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAsCnF,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,KAAK,CAAC;QACf,YAAY,EAAE,MAAM,CAAA;QACpB,eAAe,EAAE,OAAO,CAAA;QACxB,MAAM,EAAE,KAAK,CAAC;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACzE,CAAC,CAAA;CACH;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAmI9D,wBAAsB,wBAAwB,CAC5C,eAAe,EAAE,MAAM,EACvB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAuD7B;AAED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,eAAe,EAAE,EAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAC3D,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA2C7B;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,CAAC,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,CAAC,CAelB"}
|