@starci/jest-preset 2.5.3 → 2.6.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 +4 -0
- package/dist/index.js +6 -2
- package/dist/spec-coverage-reporter.d.ts +19 -0
- package/dist/spec-coverage-reporter.js +81 -0
- package/dist/spec-coverage.d.ts +51 -0
- package/dist/spec-coverage.js +96 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.6.0 - 2026-10-31
|
|
4
|
+
|
|
5
|
+
- Added: the per-spec coverage of the app check cache. With `STARCI_SPEC_COVERAGE_DIR=<folder>` the config adds the reporter `spec-coverage-reporter.js` and runs like a shard (no all-files instrumentation, no threshold): each spec file writes the covered and not-covered statements, functions and branch locations of every measured source as flags, never hit counts, and a failed spec writes none. `@starci/jest-preset/spec-coverage` merges the records of every spec (an item is covered when any spec covers it) into the lcov and coverage-final.json an unsharded run writes. Specs: `test/spec-coverage.spec.ts`.
|
|
6
|
+
|
|
3
7
|
## 2.5.3 - 2026-10-30
|
|
4
8
|
|
|
5
9
|
- Changed: the message that names the managed sync command says `starci app sync --execute`.
|
package/dist/index.js
CHANGED
|
@@ -272,9 +272,13 @@ function starciJestConfig(options) {
|
|
|
272
272
|
// A shard of the managed CI (STARCI_COVERAGE_SHARD=1, one of npm test -- --shard=i/N) measures only the files its tests load, without the per-file threshold, and
|
|
273
273
|
// writes istanbul JSON: the coverage job merges the shards by source location (merge.ts) and the coverage proof enforces 100 per file on the merged report. The
|
|
274
274
|
// all-files instrumentation of collectCoverageFrom is off there: it instruments an unloaded file with another transform (other functions and branches).
|
|
275
|
-
|
|
275
|
+
// The check cache's unit run (STARCI_SPEC_COVERAGE_DIR=<folder>, `starci app check --test`) is a partial run too: it runs the specs whose key changed, writes the coverage of
|
|
276
|
+
// each spec file on its own (spec-coverage-reporter.ts) and leaves the threshold to the merged total of every spec's record.
|
|
277
|
+
const specCoverageDir = process.env.STARCI_SPEC_COVERAGE_DIR;
|
|
278
|
+
const shard = process.env.STARCI_COVERAGE_SHARD === "1" || Boolean(specCoverageDir);
|
|
276
279
|
return {
|
|
277
280
|
coverageProvider: "v8",
|
|
281
|
+
...(specCoverageDir ? { reporters: ["default", [require.resolve("./spec-coverage-reporter.js"), { dir: specCoverageDir, include: collectCoverageFrom(coverage) }]] } : {}),
|
|
278
282
|
...(shard ? {} : {
|
|
279
283
|
collectCoverageFrom: collectCoverageFrom(coverage),
|
|
280
284
|
// A glob key is applied to every matching file on its own: each measured file must reach 100, not the average.
|
|
@@ -282,7 +286,7 @@ function starciJestConfig(options) {
|
|
|
282
286
|
}),
|
|
283
287
|
coverageDirectory: "coverage",
|
|
284
288
|
// lcov is what Sonar imports (coverage/lcov.info); text-summary and text are what the developer reads; a shard writes the json the merge reads.
|
|
285
|
-
coverageReporters: shard ? ["text-summary", "json"] : ["text-summary", "text", [require.resolve("./lcov.js"), {}]],
|
|
289
|
+
coverageReporters: shard ? ["text-summary", ...(specCoverageDir ? [] : ["json"])] : ["text-summary", "text", [require.resolve("./lcov.js"), {}]],
|
|
286
290
|
projects: [
|
|
287
291
|
{
|
|
288
292
|
...shared,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Config } from "@jest/types";
|
|
2
|
+
import type { TestResult } from "@jest/test-result";
|
|
3
|
+
import type { Test } from "@jest/reporters";
|
|
4
|
+
/** The options of the reporter: the folder it writes into and the `collectCoverageFrom` globs that select the measured sources. */
|
|
5
|
+
interface SpecCoverageOptions {
|
|
6
|
+
readonly dir: string;
|
|
7
|
+
readonly include: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
declare class SpecCoverageReporter {
|
|
10
|
+
private readonly options;
|
|
11
|
+
private readonly pending;
|
|
12
|
+
constructor(_globalConfig: Config.GlobalConfig, options: SpecCoverageOptions);
|
|
13
|
+
onTestResult(test: Test, result: TestResult): void;
|
|
14
|
+
onRunComplete(): Promise<void>;
|
|
15
|
+
private write;
|
|
16
|
+
/** The project-relative posix path of a measured source (a path or file URL), undefined when it is not measured. */
|
|
17
|
+
private relativeSource;
|
|
18
|
+
}
|
|
19
|
+
export = SpecCoverageReporter;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
// spec-coverage-reporter.ts - the Jest reporter that writes the coverage of each spec file on its own (see spec-coverage.ts). starciJestConfig adds it
|
|
6
|
+
// (`reporters: ["default", [require.resolve("./spec-coverage-reporter.js"), { dir, include }]]`) when STARCI_SPEC_COVERAGE_DIR names the folder. Jest runs custom
|
|
7
|
+
// reporters before its coverage reporter, so the spec's v8 coverage is still on the test result when it arrives. A failed spec writes `passed: false` and no source.
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_url_1 = require("node:url");
|
|
11
|
+
const node_module_1 = require("node:module");
|
|
12
|
+
const spec_coverage_ts_1 = require("./spec-coverage.js");
|
|
13
|
+
const fromJest = (0, node_module_1.createRequire)(require.resolve("jest"));
|
|
14
|
+
const fromCore = (0, node_module_1.createRequire)(fromJest.resolve("@jest/core"));
|
|
15
|
+
const fromReporters = (0, node_module_1.createRequire)(fromCore.resolve("@jest/reporters"));
|
|
16
|
+
const v8ToIstanbul = fromReporters("v8-to-istanbul");
|
|
17
|
+
const micromatch = fromCore("micromatch");
|
|
18
|
+
const mergeProcessCovs = fromReporters("@bcoe/v8-coverage").mergeProcessCovs;
|
|
19
|
+
const METADATA_SOURCE = require.resolve("./unit-metadata.generated.js");
|
|
20
|
+
const posix = (file) => file.split(node_path_1.default.sep).join("/");
|
|
21
|
+
/** True when a test result is clean: the suite ran and no test failed. */
|
|
22
|
+
function isPassed(result) {
|
|
23
|
+
return !result.testExecError && !result.failureMessage && result.numFailingTests === 0;
|
|
24
|
+
}
|
|
25
|
+
class SpecCoverageReporter {
|
|
26
|
+
options;
|
|
27
|
+
pending = [];
|
|
28
|
+
constructor(_globalConfig, options) {
|
|
29
|
+
if (typeof options?.dir !== "string" || !Array.isArray(options.include))
|
|
30
|
+
throw new Error("the spec coverage reporter needs { dir, include }");
|
|
31
|
+
this.options = options;
|
|
32
|
+
node_fs_1.default.mkdirSync(options.dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
onTestResult(test, result) {
|
|
35
|
+
const root = test.context.config.rootDir;
|
|
36
|
+
this.pending.push(this.write(root, posix(node_path_1.default.relative(root, test.path)), result));
|
|
37
|
+
}
|
|
38
|
+
async onRunComplete() {
|
|
39
|
+
await Promise.all(this.pending);
|
|
40
|
+
}
|
|
41
|
+
async write(root, spec, result) {
|
|
42
|
+
const passed = isPassed(result);
|
|
43
|
+
const sources = {};
|
|
44
|
+
if (passed && result.v8Coverage) {
|
|
45
|
+
const merged = mergeProcessCovs([{ result: result.v8Coverage.map((entry) => entry.result) }]);
|
|
46
|
+
const transforms = new Map(result.v8Coverage.flatMap((entry) => (entry.codeTransformResult ? [[entry.result.url, entry.codeTransformResult]] : [])));
|
|
47
|
+
for (const script of merged.result) {
|
|
48
|
+
if (this.relativeSource(root, script.url) === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
const transform = transforms.get(script.url);
|
|
51
|
+
const sourceMap = transform?.sourceMapPath && node_fs_1.default.existsSync(transform.sourceMapPath) ? JSON.parse(node_fs_1.default.readFileSync(transform.sourceMapPath, "utf8")) : undefined;
|
|
52
|
+
const converter = v8ToIstanbul(script.url, transform?.wrapperLength ?? 0, transform && sourceMap
|
|
53
|
+
? { originalSource: transform.originalCode, source: transform.code, sourceMap: { sourcemap: { file: script.url, ...sourceMap } } }
|
|
54
|
+
: { source: node_fs_1.default.readFileSync(script.url.startsWith("file:") ? (0, node_url_1.fileURLToPath)(script.url) : script.url, "utf8") });
|
|
55
|
+
await converter.load();
|
|
56
|
+
converter.applyCoverage(script.functions);
|
|
57
|
+
for (const data of Object.values(converter.toIstanbul())) {
|
|
58
|
+
const file = this.relativeSource(root, data.path);
|
|
59
|
+
if (file !== undefined)
|
|
60
|
+
sources[file] = (0, spec_coverage_ts_1.sourceCoverageOf)(data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const record = { spec, passed, sources: Object.fromEntries(Object.entries(sources).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) };
|
|
65
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(this.options.dir, (0, spec_coverage_ts_1.specCoverageFileName)(spec)), JSON.stringify(record));
|
|
66
|
+
}
|
|
67
|
+
/** The project-relative posix path of a measured source (a path or file URL), undefined when it is not measured. */
|
|
68
|
+
relativeSource(root, location) {
|
|
69
|
+
const resolved = node_path_1.default.resolve(location.startsWith("file:") ? (0, node_url_1.fileURLToPath)(location) : location);
|
|
70
|
+
if (resolved === METADATA_SOURCE)
|
|
71
|
+
return undefined;
|
|
72
|
+
const relative = posix(node_path_1.default.relative(root, resolved));
|
|
73
|
+
if (relative.startsWith("..") || node_path_1.default.isAbsolute(relative))
|
|
74
|
+
return undefined;
|
|
75
|
+
const positive = this.options.include.filter((glob) => !glob.startsWith("!"));
|
|
76
|
+
const negative = this.options.include.filter((glob) => glob.startsWith("!")).map((glob) => glob.slice(1));
|
|
77
|
+
const measured = micromatch.isMatch(relative, positive, { dot: true }) && !(negative.length > 0 && micromatch.isMatch(relative, negative, { dot: true }));
|
|
78
|
+
return measured ? relative : undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
module.exports = SpecCoverageReporter;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { CoverageMap, FileCoverageData } from "istanbul-lib-coverage";
|
|
2
|
+
/** A source range: start line, start column, end line, end column. */
|
|
3
|
+
export type Range = readonly [number, number, number, number];
|
|
4
|
+
/** 1 when the spec covered the item, 0 when the item is instrumented and the spec did not reach it. */
|
|
5
|
+
export type Flag = 0 | 1;
|
|
6
|
+
/** A statement: its range and flag. */
|
|
7
|
+
export type StatementItem = readonly [number, number, number, number, Flag];
|
|
8
|
+
/** A function: its name, declaration range, body range and flag. */
|
|
9
|
+
export type FunctionItem = readonly [string, Range, Range, Flag];
|
|
10
|
+
/** A branch: its type, range and the flag of each of its locations. */
|
|
11
|
+
export type BranchItem = readonly [string, Range, ReadonlyArray<readonly [Range, Flag]>];
|
|
12
|
+
/** What one spec says of one source: every instrumented item and whether the spec covered it. */
|
|
13
|
+
export interface SourceCoverage {
|
|
14
|
+
readonly s: readonly StatementItem[];
|
|
15
|
+
readonly f: readonly FunctionItem[];
|
|
16
|
+
readonly b: readonly BranchItem[];
|
|
17
|
+
}
|
|
18
|
+
/** What the reporter writes for one spec file (`<dir>/<specCoverageFileName(spec)>`). */
|
|
19
|
+
export interface SpecCoverageFile {
|
|
20
|
+
/** The spec's path relative to the project root, posix. */
|
|
21
|
+
readonly spec: string;
|
|
22
|
+
/** False when a test of the spec failed or the spec could not run: it then holds no source, and its coverage is never stored. */
|
|
23
|
+
readonly passed: boolean;
|
|
24
|
+
/** Per measured source (path relative to the project root, posix), sorted by path. */
|
|
25
|
+
readonly sources: Readonly<Record<string, SourceCoverage>>;
|
|
26
|
+
}
|
|
27
|
+
/** The covered/not-covered items of one file's istanbul coverage, sorted so equal coverage is equal bytes. */
|
|
28
|
+
export declare function sourceCoverageOf(data: FileCoverageData): SourceCoverage;
|
|
29
|
+
/** The istanbul coverage of `source` that a record stands for: a covered item has count 1, an uncovered one 0. */
|
|
30
|
+
export declare function fileCoverageOf(source: string, coverage: SourceCoverage): FileCoverageData;
|
|
31
|
+
/** The lines a source's coverage covers (a line is covered when a covered statement starts on it), sorted. */
|
|
32
|
+
export declare function coveredLines(coverage: SourceCoverage): number[];
|
|
33
|
+
/** The merged coverage map of per-spec records: per source the union of the instrumented items, covered when any record covers them. */
|
|
34
|
+
export declare function mergeSourceCoverage(records: Iterable<Readonly<Record<string, SourceCoverage>>>): CoverageMap;
|
|
35
|
+
/**
|
|
36
|
+
* The union of the per-spec records written as `<outDir>/lcov.info` and `<outDir>/coverage-final.json`; `projectRoot` is the folder the record paths are
|
|
37
|
+
* relative to (the lcov lists each source relative to it, like an unsharded run's) and `recordRoot` the folder the record paths are relative to when that is
|
|
38
|
+
* another (the app root, for records that carry app-relative paths). Returns the number of sources.
|
|
39
|
+
*/
|
|
40
|
+
export declare function mergeSpecCoverage({ records, outDir, projectRoot, recordRoot }: {
|
|
41
|
+
records: Iterable<Readonly<Record<string, SourceCoverage>>>;
|
|
42
|
+
outDir: string;
|
|
43
|
+
projectRoot: string;
|
|
44
|
+
recordRoot?: string;
|
|
45
|
+
}): {
|
|
46
|
+
files: number;
|
|
47
|
+
};
|
|
48
|
+
/** The file the reporter writes for the spec at `spec` (project-relative, posix). */
|
|
49
|
+
export declare function specCoverageFileName(spec: string): string;
|
|
50
|
+
/** Every spec file the reporter wrote into `dir`, sorted by spec path. */
|
|
51
|
+
export declare function readSpecCoverage(dir: string): SpecCoverageFile[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sourceCoverageOf = sourceCoverageOf;
|
|
7
|
+
exports.fileCoverageOf = fileCoverageOf;
|
|
8
|
+
exports.coveredLines = coveredLines;
|
|
9
|
+
exports.mergeSourceCoverage = mergeSourceCoverage;
|
|
10
|
+
exports.mergeSpecCoverage = mergeSpecCoverage;
|
|
11
|
+
exports.specCoverageFileName = specCoverageFileName;
|
|
12
|
+
exports.readSpecCoverage = readSpecCoverage;
|
|
13
|
+
// spec-coverage.ts - the per-spec coverage of the unit run, the input of the committed check cache (`starci app check --test`).
|
|
14
|
+
// The reporter (spec-coverage-reporter.ts) turns the v8 coverage of ONE spec file into the instrumented items of each measured source and whether this spec
|
|
15
|
+
// covered each: a statement, a function and every location of a branch is 1 (covered) or 0 (instrumented, not covered), never a hit count, so the same run
|
|
16
|
+
// writes the same bytes. This module holds that record and its other direction: `mergeSpecCoverage` is the union of the records of every spec (an item is
|
|
17
|
+
// covered when any spec covers it) written as the lcov and coverage-final.json an unsharded run would write, which is the total the per-file threshold,
|
|
18
|
+
// Codecov and Sonar read.
|
|
19
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
20
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
21
|
+
const node_crypto_1 = require("node:crypto");
|
|
22
|
+
const node_module_1 = require("node:module");
|
|
23
|
+
const StarciLcovReport = require("./lcov.js");
|
|
24
|
+
const fromJest = (0, node_module_1.createRequire)(require.resolve("jest"));
|
|
25
|
+
const fromCore = (0, node_module_1.createRequire)(fromJest.resolve("@jest/core"));
|
|
26
|
+
const fromReporters = (0, node_module_1.createRequire)(fromCore.resolve("@jest/reporters"));
|
|
27
|
+
const libCoverage = fromReporters("istanbul-lib-coverage");
|
|
28
|
+
const libReport = fromReporters("istanbul-lib-report");
|
|
29
|
+
const rangeOf = (loc) => [loc.start.line, loc.start.column, loc.end.line, loc.end.column];
|
|
30
|
+
const locOf = (range) => ({ start: { line: range[0], column: range[1] }, end: { line: range[2], column: range[3] } });
|
|
31
|
+
const flagOf = (count) => (count > 0 ? 1 : 0);
|
|
32
|
+
const compare = (left, right) => {
|
|
33
|
+
const a = JSON.stringify(left);
|
|
34
|
+
const b = JSON.stringify(right);
|
|
35
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
36
|
+
};
|
|
37
|
+
/** The covered/not-covered items of one file's istanbul coverage, sorted so equal coverage is equal bytes. */
|
|
38
|
+
function sourceCoverageOf(data) {
|
|
39
|
+
const s = Object.entries(data.statementMap).map(([id, loc]) => [loc.start.line, loc.start.column, loc.end.line, loc.end.column, flagOf(data.s[id] ?? 0)]).sort(compare);
|
|
40
|
+
const f = Object.entries(data.fnMap).map(([id, meta]) => [meta.name, rangeOf(meta.decl), rangeOf(meta.loc), flagOf(data.f[id] ?? 0)]).sort(compare);
|
|
41
|
+
const b = Object.entries(data.branchMap).map(([id, meta]) => [meta.type, rangeOf(meta.loc), meta.locations.map((loc, index) => [rangeOf(loc), flagOf(data.b[id]?.[index] ?? 0)])]).sort(compare);
|
|
42
|
+
return { s, f, b };
|
|
43
|
+
}
|
|
44
|
+
/** The istanbul coverage of `source` that a record stands for: a covered item has count 1, an uncovered one 0. */
|
|
45
|
+
function fileCoverageOf(source, coverage) {
|
|
46
|
+
const data = { path: source, statementMap: {}, fnMap: {}, branchMap: {}, s: {}, f: {}, b: {} };
|
|
47
|
+
coverage.s.forEach((item, index) => {
|
|
48
|
+
data.statementMap[String(index)] = locOf([item[0], item[1], item[2], item[3]]);
|
|
49
|
+
data.s[String(index)] = item[4];
|
|
50
|
+
});
|
|
51
|
+
coverage.f.forEach(([name, decl, loc, flag], index) => {
|
|
52
|
+
data.fnMap[String(index)] = { name, decl: locOf(decl), loc: locOf(loc), line: loc[0] };
|
|
53
|
+
data.f[String(index)] = flag;
|
|
54
|
+
});
|
|
55
|
+
coverage.b.forEach(([type, loc, locations], index) => {
|
|
56
|
+
data.branchMap[String(index)] = { type, loc: locOf(loc), locations: locations.map(([range]) => locOf(range)), line: loc[0] };
|
|
57
|
+
data.b[String(index)] = locations.map(([, flag]) => flag);
|
|
58
|
+
});
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
/** The lines a source's coverage covers (a line is covered when a covered statement starts on it), sorted. */
|
|
62
|
+
function coveredLines(coverage) {
|
|
63
|
+
return [...new Set(coverage.s.filter((item) => item[4] === 1).map((item) => item[0]))].sort((a, b) => a - b);
|
|
64
|
+
}
|
|
65
|
+
/** The merged coverage map of per-spec records: per source the union of the instrumented items, covered when any record covers them. */
|
|
66
|
+
function mergeSourceCoverage(records) {
|
|
67
|
+
const map = libCoverage.createCoverageMap({});
|
|
68
|
+
for (const record of records) {
|
|
69
|
+
for (const [source, coverage] of Object.entries(record))
|
|
70
|
+
map.merge({ [source]: fileCoverageOf(source, coverage) });
|
|
71
|
+
}
|
|
72
|
+
return map;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The union of the per-spec records written as `<outDir>/lcov.info` and `<outDir>/coverage-final.json`; `projectRoot` is the folder the record paths are
|
|
76
|
+
* relative to (the lcov lists each source relative to it, like an unsharded run's) and `recordRoot` the folder the record paths are relative to when that is
|
|
77
|
+
* another (the app root, for records that carry app-relative paths). Returns the number of sources.
|
|
78
|
+
*/
|
|
79
|
+
function mergeSpecCoverage({ records, outDir, projectRoot, recordRoot = projectRoot }) {
|
|
80
|
+
const absolute = [...records].map((record) => Object.fromEntries(Object.entries(record).map(([source, coverage]) => [node_path_1.default.resolve(recordRoot, source), coverage])));
|
|
81
|
+
const map = mergeSourceCoverage(absolute);
|
|
82
|
+
node_fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
83
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(outDir, "coverage-final.json"), JSON.stringify(map.toJSON()));
|
|
84
|
+
const context = libReport.createContext({ dir: outDir, coverageMap: map });
|
|
85
|
+
new StarciLcovReport({ file: "lcov.info", projectRoot }).execute(context);
|
|
86
|
+
return { files: map.files().length };
|
|
87
|
+
}
|
|
88
|
+
/** The file the reporter writes for the spec at `spec` (project-relative, posix). */
|
|
89
|
+
function specCoverageFileName(spec) {
|
|
90
|
+
return `${(0, node_crypto_1.createHash)("sha256").update(spec).digest("hex").slice(0, 24)}.json`;
|
|
91
|
+
}
|
|
92
|
+
/** Every spec file the reporter wrote into `dir`, sorted by spec path. */
|
|
93
|
+
function readSpecCoverage(dir) {
|
|
94
|
+
const files = node_fs_1.default.readdirSync(dir).filter((name) => name.endsWith(".json")).map((name) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(dir, name), "utf8")));
|
|
95
|
+
return files.sort((a, b) => (a.spec < b.spec ? -1 : a.spec > b.spec ? 1 : 0));
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starci/jest-preset",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.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(), fakeQueryBuilder(), queueQueryBuilders(), fakeCache(), fakeLock(), recordingEventBus(), recordingQueueOutbox(), fakeInbox(), FakeClock, fakeIds(), builder() and the Outcome matchers toBeRefused/toSucceedWith.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "starci183",
|
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
"./merge": {
|
|
74
74
|
"types": "./dist/merge.d.ts",
|
|
75
75
|
"default": "./dist/merge.js"
|
|
76
|
+
},
|
|
77
|
+
"./spec-coverage": {
|
|
78
|
+
"types": "./dist/spec-coverage.d.ts",
|
|
79
|
+
"default": "./dist/spec-coverage.js"
|
|
76
80
|
}
|
|
77
81
|
},
|
|
78
82
|
"files": [
|