@starci/jest-preset 2.2.6 → 2.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/CHANGELOG.md +9 -0
- package/index.cjs +47 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0 - 2026-10-16
|
|
4
|
+
|
|
5
|
+
- Added: a declared lite app (the edition of the `hfs.json` above its `be/`) runs its existing integration and contract specs without a test world: those projects exist only when their spec files do, and neither the world runner nor the world global setup and teardown is attached to a lite run.
|
|
6
|
+
- Changed: a full app without a test world keeps only the unit project (an unfinished full scaffold), as before; the cited ruling is now ruling:R212/case-1 amended by ruling:R212/case-4.
|
|
7
|
+
|
|
8
|
+
## 2.2.7 - 2026-10-13
|
|
9
|
+
|
|
10
|
+
- Fixed: the ESM-only runtime dependency `jose` (the Supabase access-token verifier of every lite back end) is compiled to CommonJS by ts-jest instead of being skipped with node_modules, so a lite app's unit run can load its guard (owner ruling 2026-10-12: lite keeps full's unit run).
|
|
11
|
+
|
|
3
12
|
## 2.2.6 - 2026-10-08
|
|
4
13
|
|
|
5
14
|
- Fixed: the `integration`, `e2e` and `contract` projects match `**/src/tests/<layer>/**/*.<layer>-spec.ts` instead of a pattern prefixed with the root path. On Windows a root that holds a dot-named folder (a checkout under `.claude`) turned the path separator before it into a glob escape, so the contract run found 0 spec files ("No tests found") although they exist.
|
package/index.cjs
CHANGED
|
@@ -53,9 +53,25 @@ const TS_TRANSFORM = String.raw`^.+\.ts$`
|
|
|
53
53
|
* does not type-check.
|
|
54
54
|
*/
|
|
55
55
|
function transform(tsconfig) {
|
|
56
|
-
return { [TS_TRANSFORM]: ["ts-jest", { tsconfig, diagnostics: false }] }
|
|
56
|
+
return { [TS_TRANSFORM]: ["ts-jest", { tsconfig, diagnostics: false }], ...esmDependencyTransform() }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The runtime dependencies of a back end that ship ECMAScript modules only: jest's CommonJS runtime cannot load them, so their
|
|
61
|
+
* files are compiled to CommonJS like the repository's own (ts-jest with `allowJs`) instead of being skipped as node_modules.
|
|
62
|
+
* `jose` is the Supabase access-token verifier of every lite back end (be.integrations.supabase); a lite app keeps full's unit
|
|
63
|
+
* run (ruling:R212/case-1, amended by ruling:R212/case-4), so a spec that imports its guard must load it.
|
|
64
|
+
*/
|
|
65
|
+
const ESM_ONLY_DEPENDENCIES = Object.freeze(["jose"])
|
|
66
|
+
const ESM_DEPENDENCY_TRANSFORM = String.raw`^.+[\\/]node_modules[\\/](?:${ESM_ONLY_DEPENDENCIES.join("|")})[\\/].+\.js$`
|
|
67
|
+
|
|
68
|
+
function esmDependencyTransform() {
|
|
69
|
+
return { [ESM_DEPENDENCY_TRANSFORM]: ["ts-jest", { tsconfig: { allowJs: true, module: "commonjs", target: "es2022", esModuleInterop: true }, diagnostics: false }] }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** node_modules stay untransformed except the ESM-only dependencies above. */
|
|
73
|
+
const TRANSFORM_IGNORE_PATTERNS = Object.freeze([String.raw`[\\/]node_modules[\\/](?!(?:${ESM_ONLY_DEPENDENCIES.join("|")})[\\/])`])
|
|
74
|
+
|
|
59
75
|
/**
|
|
60
76
|
* The compiler options the UNIT project overlays on the repository tsconfig (ts-jest merges an inline `tsconfig` object over the
|
|
61
77
|
* `tsconfig.json` it finds under `rootDir`). They exist so that per-file 100 coverage is reachable for a decorated service:
|
|
@@ -98,7 +114,7 @@ const underTests = (folder) => String.raw`[\\/]src[\\/]tests[\\/]` + folder + St
|
|
|
98
114
|
* test tree compiles against `src/tests/tsconfig.json`, the nearest config of every file under `src/tests/`. The
|
|
99
115
|
* integration, e2e and contract projects share the world's `global-setup.ts`/`global-teardown.ts` (`src/tests/world/`) and
|
|
100
116
|
* run their spec files up to `min(--maxWorkers, slots)` at once, each in a worker process of its own bound to one data slot
|
|
101
|
-
* of the test world (`world-runner.cjs`; a slot's data is reset when a file boots, so no two files ever share a slot)
|
|
117
|
+
* of the test world (`world-runner.cjs`; a slot's data is reset when a file boots, so no two files ever share a slot). A declared lite app instead runs its existing integration and contract specs without a world, on jest's stock runner. The unit project alone has a `setupFilesAfterEnv` (the Outcome matchers).
|
|
102
118
|
* Coverage is collected from the logic roles of the measured roots only (`coverage` option), with a per-file threshold of 100 on lines, branches, functions and
|
|
103
119
|
* statements: the `test` script runs the unit project with `--coverage`, fails below it and writes `coverage/lcov.info` for Sonar. It uses v8: istanbul instruments the helpers TypeScript emits (`__decorate`, `__param`, `__awaiter`, interop wrappers)
|
|
104
120
|
* as thousands of branches no spec can cover, while v8 measures the real source.
|
|
@@ -111,6 +127,16 @@ function hasTestWorld(root) {
|
|
|
111
127
|
return require("node:fs").existsSync(require("node:path").join(root, "src", "tests", "world", "global-setup.ts"))
|
|
112
128
|
}
|
|
113
129
|
|
|
130
|
+
/** The managed config runs in `be/`, one level below the app's edition declaration. */
|
|
131
|
+
function isLiteApp(root) {
|
|
132
|
+
try {
|
|
133
|
+
const file = require("node:path").join(root, "..", "hfs.json")
|
|
134
|
+
return JSON.parse(require("node:fs").readFileSync(file, "utf8")).edition === "lite"
|
|
135
|
+
} catch {
|
|
136
|
+
return false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
114
140
|
/** Every alternative a `{a,b}` pattern spells, braces expanded left to right. */
|
|
115
141
|
function braceVariants(value) {
|
|
116
142
|
const match = /{([^{}]+)}/.exec(value)
|
|
@@ -173,26 +199,36 @@ function coverageOf(options) {
|
|
|
173
199
|
|
|
174
200
|
function starciJestConfig(options) {
|
|
175
201
|
const coverage = coverageOf(options)
|
|
202
|
+
const root = process.cwd()
|
|
203
|
+
const world = hasTestWorld(root)
|
|
176
204
|
const shared = {
|
|
177
205
|
preset: "ts-jest",
|
|
178
206
|
testEnvironment: "node",
|
|
179
207
|
rootDir: ".",
|
|
180
208
|
roots: ["<rootDir>/src", "<rootDir>/apps"],
|
|
181
209
|
moduleNameMapper: { ...MODULE_NAME_MAPPER },
|
|
210
|
+
transformIgnorePatterns: [...TRANSFORM_IGNORE_PATTERNS],
|
|
182
211
|
}
|
|
183
212
|
const suite = (displayName, suffix) => ({
|
|
184
213
|
...shared,
|
|
185
214
|
transform: transform("src/tests/tsconfig.json"),
|
|
186
215
|
displayName,
|
|
187
|
-
// Every spec file in a process of its own (never in band): nothing one file or the globalSetup put on a process global
|
|
188
|
-
// (a framework registry such as @nestjs/graphql's type metadata) reaches another file. See world-runner.cjs.
|
|
189
|
-
runner: WORLD_RUNNER,
|
|
190
216
|
// A glob without the root's absolute path: jest's roots (src, apps) already bound the search, and a root path that holds a dot-named folder (a checkout under `.claude`) turns its backslash separator into a glob escape on Windows, so a path-prefixed pattern would match no file.
|
|
191
217
|
testMatch: [`**/src/tests/${displayName}/**/*.${suffix}.ts`],
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
218
|
+
// Full's world gives every spec file its own process and data slot; lite needs neither its runner nor its hooks.
|
|
219
|
+
...(world ? {
|
|
220
|
+
runner: WORLD_RUNNER,
|
|
221
|
+
globalSetup: "<rootDir>/src/tests/world/global-setup.ts",
|
|
222
|
+
globalTeardown: "<rootDir>/src/tests/world/global-teardown.ts",
|
|
223
|
+
} : {}),
|
|
195
224
|
})
|
|
225
|
+
const testProjects = world ? [suite("integration", "integration-spec"), suite("e2e", "e2e-spec"), suite("contract", "contract-spec")] : []
|
|
226
|
+
if (!world && isLiteApp(root)) {
|
|
227
|
+
for (const name of ["integration", "contract"]) {
|
|
228
|
+
const suffix = `${name}-spec`
|
|
229
|
+
if (hasCoverageSubjects(root, `src/tests/${name}/**/*.${suffix}.ts`)) testProjects.push(suite(name, suffix))
|
|
230
|
+
}
|
|
231
|
+
}
|
|
196
232
|
return {
|
|
197
233
|
testTimeout: 120_000,
|
|
198
234
|
coverageProvider: "v8",
|
|
@@ -213,10 +249,9 @@ function starciJestConfig(options) {
|
|
|
213
249
|
testMatch: ["**/*.spec.ts"],
|
|
214
250
|
testPathIgnorePatterns: ["/node_modules/", ...TEST_KIND_FOLDERS.map(underTests)],
|
|
215
251
|
},
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
...(hasTestWorld(process.cwd()) ? [suite("integration", "integration-spec"), suite("e2e", "e2e-spec"), suite("contract", "contract-spec")] : []),
|
|
252
|
+
// Jest validates every project's world hooks even under --selectProjects unit. An unfinished full scaffold
|
|
253
|
+
// keeps only unit; a lite app's present integration/contract lanes need no test world.
|
|
254
|
+
...testProjects,
|
|
220
255
|
],
|
|
221
256
|
}
|
|
222
257
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starci/jest-preset",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "The StarCi jest config for Nest repositories (unit, integration, e2e and contract projects, ts-jest with diagnostics off, per-file 100 coverage of *.service.ts only) and the one shared unit-test double kit: typed mock<T>(), mockEntityManager(), fakeTransaction(), fakeCache(), fakeLock(), recordingEventBus(), recordingQueueOutbox(), fakeInbox(), FakeClock, fakeIds(), builder() and the Outcome matchers toBeRefused/toSucceedWith.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|