@remix-run/test 0.0.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/LICENSE +21 -0
- package/README.md +430 -2
- 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 +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +305 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- 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 +91 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +255 -0
- package/dist/lib/context.d.ts +93 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/context.js +65 -0
- 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 +4 -0
- package/dist/lib/executor.d.ts.map +1 -0
- package/dist/lib/executor.js +128 -0
- 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/framework.d.ts +107 -0
- package/dist/lib/framework.d.ts.map +1 -0
- package/dist/lib/framework.js +198 -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/mock.d.ts +52 -0
- package/dist/lib/mock.d.ts.map +1 -0
- package/dist/lib/mock.js +61 -0
- package/dist/lib/normalize.d.ts +2 -0
- package/dist/lib/normalize.d.ts.map +1 -0
- package/dist/lib/normalize.js +18 -0
- package/dist/lib/playwright.d.ts +15 -0
- package/dist/lib/playwright.d.ts.map +1 -0
- package/dist/lib/playwright.js +81 -0
- package/dist/lib/reporters/dot.d.ts +9 -0
- package/dist/lib/reporters/dot.d.ts.map +1 -0
- package/dist/lib/reporters/dot.js +56 -0
- package/dist/lib/reporters/files.d.ts +9 -0
- package/dist/lib/reporters/files.d.ts.map +1 -0
- package/dist/lib/reporters/files.js +71 -0
- package/dist/lib/reporters/index.d.ts +13 -0
- package/dist/lib/reporters/index.d.ts.map +1 -0
- package/dist/lib/reporters/index.js +18 -0
- 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 +9 -0
- package/dist/lib/reporters/spec.d.ts.map +1 -0
- package/dist/lib/reporters/spec.js +153 -0
- package/dist/lib/reporters/tap.d.ts +9 -0
- package/dist/lib/reporters/tap.d.ts.map +1 -0
- package/dist/lib/reporters/tap.js +54 -0
- 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 +14 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +118 -0
- 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/watcher.d.ts +5 -0
- package/dist/lib/watcher.d.ts.map +1 -0
- package/dist/lib/watcher.js +39 -0
- package/dist/lib/worker-e2e.d.ts +2 -0
- package/dist/lib/worker-e2e.d.ts.map +1 -0
- package/dist/lib/worker-e2e.js +49 -0
- package/dist/lib/worker.d.ts +2 -0
- package/dist/lib/worker.d.ts.map +1 -0
- package/dist/lib/worker.js +57 -0
- 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 +2 -0
- package/dist/test/framework.test.e2e.d.ts.map +1 -0
- package/dist/test/framework.test.e2e.js +34 -0
- package/package.json +79 -5
- 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 +384 -0
- package/src/index.ts +16 -0
- package/src/lib/colors.ts +3 -0
- package/src/lib/config.ts +377 -0
- package/src/lib/context.ts +168 -0
- package/src/lib/coverage-loader.ts +31 -0
- package/src/lib/coverage.ts +320 -0
- package/src/lib/executor.ts +145 -0
- package/src/lib/fake-timers.ts +64 -0
- package/src/lib/framework.ts +251 -0
- package/src/lib/import-module.ts +29 -0
- package/src/lib/mock.ts +89 -0
- package/src/lib/normalize.ts +22 -0
- package/src/lib/playwright.ts +100 -0
- package/src/lib/reporters/dot.ts +58 -0
- package/src/lib/reporters/files.ts +77 -0
- package/src/lib/reporters/index.ts +27 -0
- package/src/lib/reporters/results.ts +29 -0
- package/src/lib/reporters/spec.ts +174 -0
- package/src/lib/reporters/tap.ts +58 -0
- package/src/lib/runner-browser.ts +165 -0
- package/src/lib/runner.ts +189 -0
- package/src/lib/runtime.ts +2 -0
- package/src/lib/ts-transform.ts +36 -0
- package/src/lib/watcher.ts +46 -0
- package/src/lib/worker-e2e.ts +54 -0
- package/src/lib/worker.ts +50 -0
- 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 +16 -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
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import * as fsp from 'node:fs/promises'
|
|
2
|
+
import type * as http from 'node:http'
|
|
3
|
+
import * as path from 'node:path'
|
|
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'
|
|
12
|
+
import { createReporter } from './lib/reporters/index.ts'
|
|
13
|
+
import { runBrowserTests } from './lib/runner-browser.ts'
|
|
14
|
+
import { runServerTests } from './lib/runner.ts'
|
|
15
|
+
import { createWatcher } from './lib/watcher.ts'
|
|
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'
|
|
20
|
+
|
|
21
|
+
export { getRemixTestHelpText }
|
|
22
|
+
|
|
23
|
+
export interface RunRemixTestOptions {
|
|
24
|
+
argv?: string[]
|
|
25
|
+
cwd?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface DiscoveredTests {
|
|
29
|
+
files: string[]
|
|
30
|
+
serverFiles: string[]
|
|
31
|
+
browserFiles: string[]
|
|
32
|
+
e2eFiles: string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
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()
|
|
39
|
+
|
|
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)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
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
|
+
}
|
|
57
|
+
|
|
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
|
+
}
|
|
84
|
+
|
|
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
|
+
}
|
|
97
|
+
|
|
98
|
+
let queueRerun = (reason: string) => {
|
|
99
|
+
if (!config.watch || hasExited) return
|
|
100
|
+
|
|
101
|
+
clearTimeout(rerunTimer)
|
|
102
|
+
|
|
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
|
+
}
|
|
113
|
+
|
|
114
|
+
let executeRun = async () => {
|
|
115
|
+
if (hasExited) return
|
|
116
|
+
|
|
117
|
+
running = true
|
|
118
|
+
|
|
119
|
+
let globalTeardown: (() => Promise<void> | void) | undefined
|
|
120
|
+
|
|
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
|
+
}
|
|
128
|
+
|
|
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)
|
|
193
|
+
}
|
|
194
|
+
|
|
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}"`)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
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
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
reporter.onSummary(counts, performance.now() - startTime)
|
|
274
|
+
|
|
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
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
process.on('SIGINT', handleInterrupt)
|
|
300
|
+
process.on('SIGTERM', handleInterrupt)
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
await executeRun()
|
|
304
|
+
|
|
305
|
+
if (config.watch && !hasExited) {
|
|
306
|
+
console.log('Watching for changes. Press Ctrl+C to stop.')
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
cleanupAndExit(1)
|
|
310
|
+
}
|
|
311
|
+
|
|
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)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
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)
|
|
328
|
+
|
|
329
|
+
if (files.length === 0) {
|
|
330
|
+
console.log(`No test files found matching pattern: ${config.glob.test}`)
|
|
331
|
+
return null
|
|
332
|
+
}
|
|
333
|
+
|
|
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))
|
|
336
|
+
let types = new Set(config.type.split(','))
|
|
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
|
|
343
|
+
|
|
344
|
+
if (totalFiles === 0) {
|
|
345
|
+
console.log(`No test files remain after filtering for type ${config.type}`)
|
|
346
|
+
return null
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
console.log(
|
|
350
|
+
`Found ${totalFiles} test file(s) (${serverFiles.length} server, ${browserFiles.length} browser, ${e2eFiles.length} e2e)`,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
files,
|
|
355
|
+
serverFiles,
|
|
356
|
+
browserFiles,
|
|
357
|
+
e2eFiles,
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
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
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return files
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for await (let file of fsp.glob(pattern, { cwd, exclude: [excludePattern] })) {
|
|
381
|
+
files.push(path.resolve(cwd, file))
|
|
382
|
+
}
|
|
383
|
+
return files
|
|
384
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { RemixTestConfig } from './lib/config.ts'
|
|
2
|
+
export {
|
|
3
|
+
describe,
|
|
4
|
+
it,
|
|
5
|
+
suite,
|
|
6
|
+
test,
|
|
7
|
+
before,
|
|
8
|
+
after,
|
|
9
|
+
beforeEach,
|
|
10
|
+
afterEach,
|
|
11
|
+
beforeAll,
|
|
12
|
+
afterAll,
|
|
13
|
+
} from './lib/framework.ts'
|
|
14
|
+
export { mock } from './lib/mock.ts'
|
|
15
|
+
export type { TestContext } from './lib/context.ts'
|
|
16
|
+
export type { FakeTimers } from './lib/fake-timers.ts'
|