@remix-run/test 0.1.0 → 0.2.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 +140 -35
- 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 +324 -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 +273 -139
- package/dist/index.d.ts +1 -0
- 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 +32 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +125 -22
- 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 +6 -0
- package/dist/lib/fake-timers.d.ts.map +1 -0
- package/dist/lib/fake-timers.js +45 -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 +29 -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 +2 -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 +2 -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 +2 -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 +1 -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 +117 -0
- package/dist/lib/runner.d.ts +7 -2
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +33 -4
- 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.js +5 -4
- package/dist/lib/worker.js +31 -3
- 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 +353 -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 +322 -148
- package/src/index.ts +1 -0
- package/src/lib/colors.ts +3 -0
- package/src/lib/config.ts +169 -23
- 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 +64 -0
- package/src/lib/import-module.ts +29 -0
- package/src/lib/{utils.ts → normalize.ts} +0 -18
- package/src/lib/playwright.ts +5 -7
- package/src/lib/reporters/dot.ts +3 -2
- package/src/lib/reporters/files.ts +3 -2
- package/src/lib/reporters/index.ts +4 -5
- package/src/lib/reporters/results.ts +29 -0
- package/src/lib/reporters/spec.ts +3 -2
- package/src/lib/reporters/tap.ts +2 -2
- package/src/lib/runner-browser.ts +165 -0
- package/src/lib/runner.ts +62 -10
- package/src/lib/runtime.ts +2 -0
- package/src/lib/ts-transform.ts +36 -0
- package/src/lib/worker-e2e.ts +7 -5
- package/src/lib/worker.ts +24 -4
- 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/src/cli-entry.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import { runRemixTest } from './cli.ts'
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
let exitCode = await runRemixTest({
|
|
8
|
+
argv: process.argv.slice(2),
|
|
9
|
+
cwd: process.cwd(),
|
|
10
|
+
})
|
|
11
|
+
process.exit(exitCode)
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error('Error running tests:', error)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,210 +1,384 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import * as fsp from 'node:fs/promises'
|
|
2
|
+
import type * as http from 'node:http'
|
|
3
3
|
import * as path from 'node:path'
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
getRemixTestHelpText,
|
|
6
|
+
IS_RUNNING_FROM_SRC,
|
|
7
|
+
loadConfig,
|
|
8
|
+
type ResolvedRemixTestConfig,
|
|
9
|
+
} from './lib/config.ts'
|
|
10
|
+
import { generateCombinedCoverageReport } from './lib/coverage.ts'
|
|
11
|
+
import { loadPlaywrightConfig, resolveProjects } from './lib/playwright.ts'
|
|
6
12
|
import { createReporter } from './lib/reporters/index.ts'
|
|
13
|
+
import { runBrowserTests } from './lib/runner-browser.ts'
|
|
14
|
+
import { runServerTests } from './lib/runner.ts'
|
|
7
15
|
import { createWatcher } from './lib/watcher.ts'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
16
|
+
import { importModule } from './lib/import-module.ts'
|
|
17
|
+
import type { Counts } from './lib/reporters/results.ts'
|
|
18
|
+
import { IS_BUN } from './lib/runtime.ts'
|
|
19
|
+
import { isMainThread } from 'node:worker_threads'
|
|
11
20
|
|
|
12
|
-
|
|
21
|
+
export { getRemixTestHelpText }
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let queued = false
|
|
19
|
-
let rerunTimer: NodeJS.Timeout | undefined
|
|
23
|
+
export interface RunRemixTestOptions {
|
|
24
|
+
argv?: string[]
|
|
25
|
+
cwd?: string
|
|
26
|
+
}
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
interface DiscoveredTests {
|
|
29
|
+
files: string[]
|
|
30
|
+
serverFiles: string[]
|
|
31
|
+
browserFiles: string[]
|
|
32
|
+
e2eFiles: string[]
|
|
33
|
+
}
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
export async function runRemixTest(options: RunRemixTestOptions = {}): Promise<number> {
|
|
36
|
+
let argv = options.argv ?? process.argv.slice(2)
|
|
37
|
+
let cwd = await resolveCwd(options.cwd ?? process.cwd())
|
|
38
|
+
let previousCwd = process.cwd()
|
|
26
39
|
|
|
27
|
-
if (
|
|
28
|
-
|
|
40
|
+
if (!isMainThread) {
|
|
41
|
+
return await runRemixTestInCwd(argv, cwd)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
process.chdir(cwd)
|
|
46
|
+
return await runRemixTestInCwd(argv, cwd)
|
|
47
|
+
} finally {
|
|
48
|
+
process.chdir(previousCwd)
|
|
29
49
|
}
|
|
30
|
-
} catch {
|
|
31
|
-
cleanupAndExit(1)
|
|
32
50
|
}
|
|
33
51
|
|
|
34
|
-
async function
|
|
35
|
-
if (
|
|
52
|
+
async function runRemixTestInCwd(argv: string[], cwd: string): Promise<number> {
|
|
53
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
54
|
+
console.log(getRemixTestHelpText())
|
|
55
|
+
return 0
|
|
56
|
+
}
|
|
36
57
|
|
|
37
|
-
|
|
58
|
+
let config = await loadConfig(argv, cwd)
|
|
59
|
+
let hasExited = false
|
|
60
|
+
let latestExitCode = 0
|
|
61
|
+
let watcher: ReturnType<typeof createWatcher> | undefined
|
|
62
|
+
let running = false
|
|
63
|
+
let queued = false
|
|
64
|
+
let rerunTimer: NodeJS.Timeout | undefined
|
|
65
|
+
let browserServer: http.Server | undefined
|
|
66
|
+
let browserServerFilesKey: string | undefined
|
|
67
|
+
let browserPort: number | undefined
|
|
68
|
+
let resolveRun: ((exitCode: number) => void) | undefined
|
|
69
|
+
|
|
70
|
+
let runPromise = new Promise<number>((resolve) => {
|
|
71
|
+
resolveRun = resolve
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
let cleanupAndExit = (code: number) => {
|
|
75
|
+
if (hasExited) return
|
|
76
|
+
hasExited = true
|
|
77
|
+
watcher?.close()
|
|
78
|
+
browserServer?.close()
|
|
79
|
+
clearTimeout(rerunTimer)
|
|
80
|
+
process.off('SIGINT', handleInterrupt)
|
|
81
|
+
process.off('SIGTERM', handleInterrupt)
|
|
82
|
+
resolveRun?.(code)
|
|
83
|
+
}
|
|
38
84
|
|
|
39
|
-
let
|
|
85
|
+
let handleInterrupt = () => cleanupAndExit(latestExitCode)
|
|
86
|
+
|
|
87
|
+
let closeBrowserServer = async () => {
|
|
88
|
+
if (!browserServer) return
|
|
89
|
+
let server = browserServer
|
|
90
|
+
await new Promise<void>((resolve, reject) =>
|
|
91
|
+
server.close((error) => (error ? reject(error) : resolve())),
|
|
92
|
+
)
|
|
93
|
+
browserServer = undefined
|
|
94
|
+
browserServerFilesKey = undefined
|
|
95
|
+
browserPort = undefined
|
|
96
|
+
}
|
|
40
97
|
|
|
41
|
-
|
|
42
|
-
if (config.
|
|
43
|
-
let mod = await tsImport(path.resolve(process.cwd(), config.setup), {
|
|
44
|
-
parentURL: import.meta.url,
|
|
45
|
-
})
|
|
46
|
-
let globalSetup: (() => Promise<void> | void) | undefined = mod.globalSetup
|
|
47
|
-
globalTeardown = mod.globalTeardown
|
|
48
|
-
await globalSetup?.()
|
|
49
|
-
}
|
|
98
|
+
let queueRerun = (reason: string) => {
|
|
99
|
+
if (!config.watch || hasExited) return
|
|
50
100
|
|
|
51
|
-
|
|
101
|
+
clearTimeout(rerunTimer)
|
|
52
102
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
103
|
+
rerunTimer = setTimeout(() => {
|
|
104
|
+
rerunTimer = undefined
|
|
105
|
+
if (running) {
|
|
106
|
+
queued = true
|
|
107
|
+
} else {
|
|
108
|
+
console.log(`\n↻ Change detected (${reason}), re-running tests...\n`)
|
|
109
|
+
void executeRun()
|
|
110
|
+
}
|
|
111
|
+
}, 100)
|
|
112
|
+
}
|
|
57
113
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
? await loadPlaywrightConfig(config.playwrightConfig)
|
|
61
|
-
: config.playwrightConfig
|
|
114
|
+
let executeRun = async () => {
|
|
115
|
+
if (hasExited) return
|
|
62
116
|
|
|
63
|
-
|
|
64
|
-
let startTime = performance.now()
|
|
117
|
+
running = true
|
|
65
118
|
|
|
66
|
-
let
|
|
67
|
-
passed: 0,
|
|
68
|
-
failed: 0,
|
|
69
|
-
skipped: 0,
|
|
70
|
-
todo: 0,
|
|
71
|
-
}
|
|
119
|
+
let globalTeardown: (() => Promise<void> | void) | undefined
|
|
72
120
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
counts.todo += serverResult.todo
|
|
81
|
-
}
|
|
121
|
+
try {
|
|
122
|
+
if (config.setup) {
|
|
123
|
+
let mod = await importModule(path.resolve(cwd, config.setup), import.meta)
|
|
124
|
+
let globalSetup: (() => Promise<void> | void) | undefined = mod.globalSetup
|
|
125
|
+
globalTeardown = mod.globalTeardown
|
|
126
|
+
await globalSetup?.()
|
|
127
|
+
}
|
|
82
128
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
let discoveredTests = await discoverTests(config, cwd)
|
|
130
|
+
if (discoveredTests == null) {
|
|
131
|
+
latestExitCode = 1
|
|
132
|
+
cleanupAndExit(latestExitCode)
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let { files, serverFiles, browserFiles, e2eFiles } = discoveredTests
|
|
137
|
+
|
|
138
|
+
if (config.watch) {
|
|
139
|
+
watcher ??= createWatcher((file) => queueRerun(file))
|
|
140
|
+
watcher.update(files)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let browserFilesKey = browserFiles.join('\0')
|
|
144
|
+
if (browserServer && browserFiles.length === 0) {
|
|
145
|
+
await closeBrowserServer()
|
|
146
|
+
} else if (
|
|
147
|
+
browserFiles.length > 0 &&
|
|
148
|
+
(!browserServer || browserServerFilesKey !== browserFilesKey)
|
|
149
|
+
) {
|
|
150
|
+
await closeBrowserServer()
|
|
151
|
+
let { startServer } = IS_RUNNING_FROM_SRC
|
|
152
|
+
? await importModule('./app/server.ts', import.meta)
|
|
153
|
+
: await import(`./app/server.js`)
|
|
154
|
+
let result = await startServer(browserFiles)
|
|
155
|
+
browserServer = result.server
|
|
156
|
+
browserServerFilesKey = browserFilesKey
|
|
157
|
+
browserPort = result.port
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let playwrightConfig =
|
|
161
|
+
config.playwrightConfig == null || typeof config.playwrightConfig === 'string'
|
|
162
|
+
? await loadPlaywrightConfig(config.playwrightConfig, cwd)
|
|
163
|
+
: config.playwrightConfig
|
|
164
|
+
|
|
165
|
+
let reporter = createReporter(config.reporter)
|
|
166
|
+
let startTime = performance.now()
|
|
167
|
+
|
|
168
|
+
let counts: Counts = {
|
|
169
|
+
passed: 0,
|
|
170
|
+
failed: 0,
|
|
171
|
+
skipped: 0,
|
|
172
|
+
todo: 0,
|
|
173
|
+
}
|
|
174
|
+
let allCoverageMaps: Array<ReturnType<typeof Object.values>[number] | null | undefined> = []
|
|
175
|
+
|
|
176
|
+
if (serverFiles.length > 0) {
|
|
177
|
+
reporter.onSectionStart('\nRunning server tests:')
|
|
178
|
+
let serverResult = await runServerTests(
|
|
179
|
+
serverFiles,
|
|
180
|
+
reporter,
|
|
181
|
+
config.concurrency,
|
|
182
|
+
'server',
|
|
183
|
+
{
|
|
184
|
+
coverage: config.coverage,
|
|
185
|
+
cwd,
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
counts.failed += serverResult.failed
|
|
189
|
+
counts.passed += serverResult.passed
|
|
190
|
+
counts.skipped += serverResult.skipped
|
|
191
|
+
counts.todo += serverResult.todo
|
|
192
|
+
allCoverageMaps.push(serverResult.coverageMap)
|
|
92
193
|
}
|
|
93
194
|
|
|
94
|
-
for
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (config.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
project.playwrightUseOpts = { ...project.playwrightUseOpts, headless: false }
|
|
195
|
+
// Run browser/e2e tests for all browsers configured by the user
|
|
196
|
+
if (browserFiles.length > 0 || e2eFiles.length > 0) {
|
|
197
|
+
let projects = resolveProjects(playwrightConfig)
|
|
198
|
+
if (config.project) {
|
|
199
|
+
let projectNames = config.project.split(',').map((project) => project.trim())
|
|
200
|
+
projects = projects.filter(
|
|
201
|
+
(project) => project.name && projectNames.includes(project.name),
|
|
202
|
+
)
|
|
203
|
+
if (projects.length === 0) {
|
|
204
|
+
throw new Error(`No playwright projects found with name(s) "${config.project}"`)
|
|
105
205
|
}
|
|
106
206
|
}
|
|
107
207
|
|
|
108
|
-
let
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
208
|
+
let lastBrowserResult: Awaited<ReturnType<typeof runBrowserTests>> | null = null
|
|
209
|
+
|
|
210
|
+
for (let project of projects) {
|
|
211
|
+
reporter.onSectionStart(`\nRunning tests for project \`${project.name}\`:`)
|
|
212
|
+
|
|
213
|
+
if (config.browser?.open) {
|
|
214
|
+
if (project.playwrightUseOpts?.headless === true) {
|
|
215
|
+
let label = project.name ? ` (project "${project.name}")` : ''
|
|
216
|
+
console.warn(
|
|
217
|
+
`Warning: browser.open is set but playwright headless is explicitly true${label} — ignoring browser.open`,
|
|
218
|
+
)
|
|
219
|
+
} else {
|
|
220
|
+
project.playwrightUseOpts = { ...project.playwrightUseOpts, headless: false }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let [browserResult, e2eResult] = await Promise.all([
|
|
225
|
+
browserFiles.length > 0
|
|
226
|
+
? runBrowserTests({
|
|
227
|
+
baseUrl: `http://localhost:${browserPort}`,
|
|
228
|
+
console: config.browser?.echo,
|
|
229
|
+
coverage: !!config.coverage,
|
|
230
|
+
open: config.browser?.open,
|
|
231
|
+
playwrightUseOpts: project.playwrightUseOpts,
|
|
232
|
+
projectName: project.name,
|
|
233
|
+
reporter,
|
|
234
|
+
testFiles: browserFiles,
|
|
235
|
+
})
|
|
236
|
+
: null,
|
|
237
|
+
e2eFiles.length > 0
|
|
238
|
+
? runServerTests(e2eFiles, reporter, config.concurrency, 'e2e', {
|
|
239
|
+
open: config.browser?.open,
|
|
240
|
+
playwrightUseOpts: project.playwrightUseOpts,
|
|
241
|
+
projectName: project.name,
|
|
242
|
+
coverage: config.coverage,
|
|
243
|
+
cwd,
|
|
244
|
+
})
|
|
245
|
+
: null,
|
|
246
|
+
])
|
|
247
|
+
|
|
248
|
+
counts.passed += (browserResult?.results.passed ?? 0) + (e2eResult?.passed ?? 0)
|
|
249
|
+
counts.failed += (browserResult?.results.failed ?? 0) + (e2eResult?.failed ?? 0)
|
|
250
|
+
counts.skipped += (browserResult?.results.skipped ?? 0) + (e2eResult?.skipped ?? 0)
|
|
251
|
+
counts.todo += (browserResult?.results.todo ?? 0) + (e2eResult?.todo ?? 0)
|
|
252
|
+
allCoverageMaps.push(browserResult?.coverageMap)
|
|
253
|
+
allCoverageMaps.push(e2eResult?.coverageMap)
|
|
254
|
+
|
|
255
|
+
if (browserResult) {
|
|
256
|
+
lastBrowserResult = browserResult
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (config.browser?.open && lastBrowserResult) {
|
|
261
|
+
console.log('\nBrowser is open. Press Ctrl+C to close.')
|
|
262
|
+
await Promise.race([
|
|
263
|
+
lastBrowserResult.disconnected,
|
|
264
|
+
new Promise<void>((resolve) => {
|
|
265
|
+
process.once('SIGINT', resolve)
|
|
266
|
+
process.once('SIGTERM', resolve)
|
|
267
|
+
}),
|
|
268
|
+
])
|
|
269
|
+
await lastBrowserResult.close()
|
|
270
|
+
}
|
|
121
271
|
}
|
|
122
|
-
}
|
|
123
272
|
|
|
124
|
-
|
|
273
|
+
reporter.onSummary(counts, performance.now() - startTime)
|
|
125
274
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
275
|
+
let thresholdsPassed = true
|
|
276
|
+
if (config.coverage) {
|
|
277
|
+
thresholdsPassed = await generateCombinedCoverageReport(
|
|
278
|
+
allCoverageMaps,
|
|
279
|
+
cwd,
|
|
280
|
+
config.coverage,
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
latestExitCode = counts.failed > 0 || !thresholdsPassed ? 1 : 0
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('Error running tests:', error)
|
|
286
|
+
latestExitCode = 1
|
|
287
|
+
} finally {
|
|
288
|
+
await globalTeardown?.()
|
|
289
|
+
running = false
|
|
290
|
+
if (queued) {
|
|
291
|
+
queued = false
|
|
292
|
+
queueRerun('queued change')
|
|
293
|
+
} else if (!config.watch) {
|
|
294
|
+
cleanupAndExit(latestExitCode)
|
|
295
|
+
}
|
|
138
296
|
}
|
|
139
297
|
}
|
|
140
|
-
}
|
|
141
298
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
serverFiles: string[]
|
|
145
|
-
e2eFiles: string[]
|
|
146
|
-
}> {
|
|
147
|
-
async function findFiles(pattern: string) {
|
|
148
|
-
let files: string[] = []
|
|
149
|
-
let exclude = ['node_modules/**', '.git/**']
|
|
299
|
+
process.on('SIGINT', handleInterrupt)
|
|
300
|
+
process.on('SIGTERM', handleInterrupt)
|
|
150
301
|
|
|
151
|
-
|
|
152
|
-
|
|
302
|
+
try {
|
|
303
|
+
await executeRun()
|
|
304
|
+
|
|
305
|
+
if (config.watch && !hasExited) {
|
|
306
|
+
console.log('Watching for changes. Press Ctrl+C to stop.')
|
|
153
307
|
}
|
|
308
|
+
} catch {
|
|
309
|
+
cleanupAndExit(1)
|
|
310
|
+
}
|
|
154
311
|
|
|
155
|
-
|
|
312
|
+
return await runPromise
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function resolveCwd(cwd: string): Promise<string> {
|
|
316
|
+
try {
|
|
317
|
+
return await fsp.realpath(cwd)
|
|
318
|
+
} catch {
|
|
319
|
+
return path.resolve(cwd)
|
|
156
320
|
}
|
|
321
|
+
}
|
|
157
322
|
|
|
158
|
-
|
|
323
|
+
async function discoverTests(
|
|
324
|
+
config: ResolvedRemixTestConfig,
|
|
325
|
+
cwd: string,
|
|
326
|
+
): Promise<DiscoveredTests | null> {
|
|
327
|
+
let files = await findFiles(config.glob.test, config.glob.exclude, cwd)
|
|
159
328
|
|
|
160
329
|
if (files.length === 0) {
|
|
161
330
|
console.log(`No test files found matching pattern: ${config.glob.test}`)
|
|
162
|
-
|
|
331
|
+
return null
|
|
163
332
|
}
|
|
164
333
|
|
|
165
|
-
let
|
|
166
|
-
|
|
334
|
+
let browserSet = new Set(await findFiles(config.glob.browser, config.glob.exclude, cwd))
|
|
335
|
+
let e2eSet = new Set(await findFiles(config.glob.e2e, config.glob.exclude, cwd))
|
|
167
336
|
let types = new Set(config.type.split(','))
|
|
168
|
-
let
|
|
169
|
-
let
|
|
170
|
-
|
|
171
|
-
|
|
337
|
+
let browserFiles = types.has('browser') ? files.filter((f) => browserSet.has(f)) : []
|
|
338
|
+
let e2eFiles = types.has('e2e') ? files.filter((file) => e2eSet.has(file)) : []
|
|
339
|
+
let serverFiles = types.has('server')
|
|
340
|
+
? files.filter((file) => !browserSet.has(file) && !e2eSet.has(file))
|
|
341
|
+
: []
|
|
342
|
+
let totalFiles = browserFiles.length + serverFiles.length + e2eFiles.length
|
|
172
343
|
|
|
173
344
|
if (totalFiles === 0) {
|
|
174
345
|
console.log(`No test files remain after filtering for type ${config.type}`)
|
|
175
|
-
|
|
346
|
+
return null
|
|
176
347
|
}
|
|
177
348
|
|
|
178
349
|
console.log(
|
|
179
|
-
`Found ${totalFiles} test file(s) (${serverFiles.length} server, ${e2eFiles.length} e2e)`,
|
|
350
|
+
`Found ${totalFiles} test file(s) (${serverFiles.length} server, ${browserFiles.length} browser, ${e2eFiles.length} e2e)`,
|
|
180
351
|
)
|
|
181
352
|
|
|
182
353
|
return {
|
|
183
354
|
files,
|
|
184
355
|
serverFiles,
|
|
356
|
+
browserFiles,
|
|
185
357
|
e2eFiles,
|
|
186
358
|
}
|
|
187
359
|
}
|
|
188
360
|
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
361
|
+
async function findFiles(pattern: string, excludePattern: string, cwd: string): Promise<string[]> {
|
|
362
|
+
let files: string[] = []
|
|
363
|
+
|
|
364
|
+
if (IS_BUN) {
|
|
365
|
+
// Bun's `fs.promises.glob` follows symlinks and doesn't prune traversal
|
|
366
|
+
// via `exclude`, so it enters pnpm symlink cycles in `node_modules`.
|
|
367
|
+
// Use Bun's native Glob, which defaults to `followSymlinks: false`.
|
|
368
|
+
// @ts-expect-error — bun module is only resolvable under the Bun runtime
|
|
369
|
+
let { Glob } = await import('bun')
|
|
370
|
+
let glob = new Glob(pattern)
|
|
371
|
+
let excludeGlob = new Glob(excludePattern)
|
|
372
|
+
for await (let file of glob.scan({ cwd, absolute: true })) {
|
|
373
|
+
if (!excludeGlob.match(path.relative(cwd, file))) {
|
|
374
|
+
files.push(file)
|
|
375
|
+
}
|
|
201
376
|
}
|
|
202
|
-
|
|
203
|
-
}
|
|
377
|
+
return files
|
|
378
|
+
}
|
|
204
379
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
process.exit(code)
|
|
380
|
+
for await (let file of fsp.glob(pattern, { cwd, exclude: [excludePattern] })) {
|
|
381
|
+
files.push(path.resolve(cwd, file))
|
|
382
|
+
}
|
|
383
|
+
return files
|
|
210
384
|
}
|
package/src/index.ts
CHANGED