mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
ci(client): fail the count ratchets on an entry above the tree
lint:size, lint:layers, lint:rtl (both baselines), lint:skips and lint:warnings only failed a count that passed its entry. A count that fell below its entry was logged and nothing more, so a file that shrank could grow back to its old entry unseen, and the entry of a deleted file stayed behind for the next file at that path. lint:format and lint:offline already fail on such a stale entry, and the docs said these counts may only shrink. Each of them now fails on an entry that lowerCounts would lower or drop, naming its count or saying the file is gone, and tells to run --update, which still only lowers. staleCounts and reportStale in lib/ratchet.mjs hold the shared part. Every baseline matches the tree today, so none needed lowering. The client CLAUDE.md says so, and that CI lints through lint:warnings rather than lint:check.
This commit is contained in:
+4
-2
@@ -8,8 +8,8 @@ Scope: the **`@trek/client`** workspace (React 19 + Vite + Zustand + Tailwind PW
|
||||
npm run dev # Vite dev server; proxies the API/ws/uploads/MCP/OAuth paths → http://localhost:3001
|
||||
npm run build # prebuild generates PWA icons, then vite build
|
||||
npm run typecheck # tsc --noEmit (CI)
|
||||
npm run lint # eslint . (lint:check in CI)
|
||||
npm run lint:warnings # eslint, failing on any error and on any warning count per rule above scripts/eslint-baseline.json (app code, tests and eslint-disable'd messages counted apart; a `/* eslint <rule>: ... */` config comment fails outright; --update lowers it)
|
||||
npm run lint # eslint . (CI runs lint:warnings instead, which fails on the same errors)
|
||||
npm run lint:warnings # eslint, failing on any error and on any warning count per rule above scripts/eslint-baseline.json and on any entry above its count (app code, tests and eslint-disable'd messages counted apart; a `/* eslint <rule>: ... */` config comment fails outright; --update lowers it)
|
||||
npm run lint:pages # enforce the Page pattern (CI gate)
|
||||
npm run lint:rtl # physical left/right sides and rtl-lint-disable comments may only shrink per file (CI gate; --list shows them, --update lowers both baselines)
|
||||
npm run lint:size # no source file or stylesheet past 1000 lines, no test (Playwright specs and fixtures under e2e/ included) past 2000, counted wrapped at 120 columns; longer ones may only shrink (CI gate; --update lowers the baseline)
|
||||
@@ -25,6 +25,8 @@ npm run theme:lint # theme conformance audit (theme:lint:strict exits 1;
|
||||
npm run check:gl-split # after build: fails if one chunk bundles both mapbox-gl and maplibre-gl (CI gate)
|
||||
```
|
||||
|
||||
Every baseline ratchet above also fails on an entry that allows more than the tree holds: a count above what its file or rule has now, or a file that is gone. Run the check with `--update` in the change that made it smaller, so a fixed file cannot grow back to its old entry and a new file at a deleted path does not inherit one.
|
||||
|
||||
Single test: `npx vitest run src/store/slices/budgetSlice.test.ts`, or `npx vitest run -t "optimistically adds the place"`.
|
||||
|
||||
The dev server needs the API on **:3001** — run `npm run dev` at the repo root to start both.
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
* of the kind that has already shown stale money. This runs ESLint over the
|
||||
* client and counts the warnings per rule, separately for app code and
|
||||
* tests, and the messages an eslint-disable comment silenced as a third
|
||||
* group. Each count may not pass its entry in scripts/eslint-baseline.json.
|
||||
* group. Each count may not pass its entry in scripts/eslint-baseline.json,
|
||||
* and an entry above its count fails as well until --update lowers it, so a
|
||||
* fixed warning cannot come back unseen.
|
||||
* Any error fails as well, so the check can stand in for `npm run lint:check`,
|
||||
* and so does a block comment that configures a rule inline (such as one that
|
||||
* turns no-explicit-any off): it leaves no message to count, so it would
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
* all. The imports that already broke a rule are counted per file in
|
||||
* scripts/layers-baseline.json, and a file may hold no more of them than its
|
||||
* entry (a file without an entry holds none). Type-only imports count: a type
|
||||
* the lower layer needs belongs below it.
|
||||
* the lower layer needs belongs below it. An entry above what its file holds
|
||||
* now, or for a file that is gone, fails as well until --update lowers it.
|
||||
*
|
||||
* npm run lint:layers check against the baseline (CI)
|
||||
* npm run lint:layers -- --list print every import against the layering
|
||||
|
||||
@@ -4,7 +4,17 @@
|
||||
import { ESLint } from 'eslint';
|
||||
import { join } from 'node:path';
|
||||
import ts from 'typescript';
|
||||
import { countMap, lowerCounts, readBaseline, readText, TEST_FILE, toKey, writeBaseline } from './ratchet.mjs';
|
||||
import {
|
||||
countMap,
|
||||
lowerCounts,
|
||||
readBaseline,
|
||||
readText,
|
||||
reportStale,
|
||||
staleCounts,
|
||||
TEST_FILE,
|
||||
toKey,
|
||||
writeBaseline,
|
||||
} from './ratchet.mjs';
|
||||
|
||||
/**
|
||||
* Where a warning is counted: in the app code, in the tests, or, for a
|
||||
@@ -151,7 +161,7 @@ export async function check({
|
||||
|
||||
for (const line of errors) error(`FAIL ${line}`);
|
||||
let grown = 0;
|
||||
let lowerable = 0;
|
||||
let stale = 0;
|
||||
for (const area of AREAS) {
|
||||
for (const [rule, n] of Object.entries(counts[area])) {
|
||||
const allowed = baseline[area][rule] ?? 0;
|
||||
@@ -163,7 +173,14 @@ export async function check({
|
||||
.map(([key, k]) => `${key} (${k})`);
|
||||
error(`FAIL ${area}: ${n} ${rule} warning(s), baseline ${allowed}. Most in: ${top.join(', ')}`);
|
||||
}
|
||||
for (const [rule, allowed] of Object.entries(baseline[area])) if ((counts[area][rule] ?? 0) < allowed) lowerable++;
|
||||
const fallen = staleCounts(baseline[area], counts[area]);
|
||||
reportStale(fallen, {
|
||||
file: 'eslint-baseline.json',
|
||||
command: 'lint:warnings',
|
||||
label: (rule) => `${area}: ${rule}`,
|
||||
error,
|
||||
});
|
||||
stale += fallen.length;
|
||||
}
|
||||
if (grown) {
|
||||
error(
|
||||
@@ -171,9 +188,8 @@ export async function check({
|
||||
'Run npx eslint <file> to see each one.'
|
||||
);
|
||||
}
|
||||
if (lowerable && !update) log(`${lowerable} rule count(s) fell below the baseline: run with --update to lower it.`);
|
||||
log(
|
||||
`eslint: ${errors.length} error(s); warnings ${AREAS.map((area) => `${area} ${sum(counts[area])}/${sum(baseline[area])}`).join(', ')} (now/baseline)`
|
||||
);
|
||||
return errors.length || grown ? 1 : 0;
|
||||
return errors.length || grown || stale ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { join } from 'node:path';
|
||||
import { sourceGraph } from './imports.mjs';
|
||||
import { countMap, lowerCounts, readBaseline, writeBaseline } from './ratchet.mjs';
|
||||
import { countMap, lowerCounts, readBaseline, reportStale, staleCounts, writeBaseline } from './ratchet.mjs';
|
||||
|
||||
const VIEW = ['components', 'mobile', 'pages', 'hooks'];
|
||||
|
||||
@@ -82,7 +82,7 @@ export function check({
|
||||
}
|
||||
|
||||
const grown = Object.entries(counts).filter(([key, n]) => n > (baseline[key] ?? 0));
|
||||
const lowerable = Object.entries(baseline).filter(([key, n]) => (counts[key] ?? 0) < n);
|
||||
const stale = staleCounts(baseline, counts);
|
||||
|
||||
for (const [key, n] of grown) {
|
||||
error(`FAIL ${key}: ${n} import(s) against the layering, baseline ${baseline[key] ?? 0}:`);
|
||||
@@ -94,12 +94,10 @@ export function check({
|
||||
'a hook under hooks/, a type under types/) instead of importing upwards.'
|
||||
);
|
||||
}
|
||||
if (lowerable.length && !update) {
|
||||
log(`${lowerable.length} file(s) hold fewer of these imports than the baseline: run with --update to lower it.`);
|
||||
}
|
||||
reportStale(stale, { file: 'layers-baseline.json', command: 'lint:layers', root, error });
|
||||
const sum = (map) => Object.values(map).reduce((a, b) => a + b, 0);
|
||||
log(
|
||||
`layers: ${sum(counts)} import(s) against the layering in ${Object.keys(counts).length} file(s), baseline allows ${sum(baseline)}`
|
||||
);
|
||||
return grown.length ? 1 : 0;
|
||||
return grown.length || stale.length ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,50 @@ export function lowerCounts(baseline, counts, floor = 0) {
|
||||
return lowered;
|
||||
}
|
||||
|
||||
/**
|
||||
* The entries of a count baseline that allow more than is measured now: a
|
||||
* count that fell below its entry, one back at floor or under it, or a file
|
||||
* that is gone. Exactly the entries lowerCounts would lower or drop. Each one
|
||||
* fails its check until --update lowers it, or the count could grow back to
|
||||
* the old entry unseen, and a deleted file would hand its allowance to the
|
||||
* next file at its path.
|
||||
*
|
||||
* @param {Record<string, number>} baseline
|
||||
* @param {Record<string, number>} counts
|
||||
* @param {number | ((key: string) => number)} [floor]
|
||||
* @returns {{ key: string, entry: number, now: number }[]}
|
||||
*/
|
||||
export function staleCounts(baseline, counts, floor = 0) {
|
||||
const lowered = lowerCounts(baseline, counts, floor);
|
||||
return Object.entries(baseline)
|
||||
.filter(([key, entry]) => lowered[key] !== entry)
|
||||
.map(([key, entry]) => ({ key, entry, now: counts[key] ?? 0 }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a FAIL line for each stale entry (see staleCounts) of the baseline
|
||||
* scripts/<file>, then how to lower them. With root, an entry whose file is
|
||||
* not there is called gone; label names an entry that is not a file path.
|
||||
*
|
||||
* @param {{ key: string, entry: number, now: number }[]} stale
|
||||
* @param {{ file: string, command: string, root?: string, label?: (key: string) => string, error: (line: string) => void }} options
|
||||
*/
|
||||
export function reportStale(stale, { file, command, root, label = (key) => key, error }) {
|
||||
for (const { key, entry, now } of stale) {
|
||||
const gone = root !== undefined && !existsSync(join(root, key));
|
||||
error(
|
||||
`FAIL ${label(key)} is held at ${entry} in scripts/${file}, ` +
|
||||
(gone ? 'but the file is gone.' : `but there are ${now} now.`)
|
||||
);
|
||||
}
|
||||
if (stale.length) {
|
||||
error(
|
||||
`Run npm run ${command} -- --update to lower the baseline with the change that made it smaller: ` +
|
||||
'an entry above the count lets it grow back unseen.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** The baseline entries that are still offenders: a list only ever loses files. */
|
||||
export function lowerList(baseline, offenders) {
|
||||
const now = new Set(offenders);
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
lowerCounts,
|
||||
readBaseline,
|
||||
readText,
|
||||
reportStale,
|
||||
staleCounts,
|
||||
TEST_FILE,
|
||||
toKey,
|
||||
writeBaseline,
|
||||
@@ -163,9 +165,8 @@ export function check({
|
||||
|
||||
const grown = Object.entries(counts).filter(([file, n]) => n > (baseline[file] ?? 0));
|
||||
const marked = Object.entries(markers).filter(([file, n]) => n > (allowedMarkers[file] ?? 0));
|
||||
const lowerable =
|
||||
Object.entries(baseline).filter(([file, n]) => (counts[file] ?? 0) < n).length +
|
||||
Object.entries(allowedMarkers).filter(([file, n]) => (markers[file] ?? 0) < n).length;
|
||||
const stale = staleCounts(baseline, counts);
|
||||
const staleMarkers = staleCounts(allowedMarkers, markers);
|
||||
|
||||
for (const [file, n] of grown) {
|
||||
error(
|
||||
@@ -182,12 +183,11 @@ export function check({
|
||||
'(a map, a measured position, a time axis) needs a reviewer to raise the entry by hand.'
|
||||
);
|
||||
}
|
||||
if (lowerable && !update) {
|
||||
log(`${lowerable} entr(ies) are above what the files hold now: run with --update to lower them.`);
|
||||
}
|
||||
reportStale(stale, { file: 'rtl-baseline.json', command: 'lint:rtl', root: join(root, 'src'), error });
|
||||
reportStale(staleMarkers, { file: 'rtl-disable-baseline.json', command: 'lint:rtl', root: join(root, 'src'), error });
|
||||
log(
|
||||
`rtl: ${sum(counts)} physical side(s) in ${Object.keys(counts).length} file(s), baseline allows ${sum(baseline)}; ` +
|
||||
`${sum(markers)} line(s) marked ${DISABLE}, baseline allows ${sum(allowedMarkers)}`
|
||||
);
|
||||
return grown.length || marked.length ? 1 : 0;
|
||||
return grown.length || marked.length || stale.length || staleMarkers.length ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
lowerCounts,
|
||||
readBaseline,
|
||||
readText,
|
||||
reportStale,
|
||||
staleCounts,
|
||||
TEST_FILE,
|
||||
toKey,
|
||||
writeBaseline,
|
||||
@@ -68,7 +70,9 @@ const limitOf = (key) => groupOf(key)?.limit ?? 0;
|
||||
|
||||
/**
|
||||
* Runs the check against the baseline at baselinePath. With update, the
|
||||
* baseline is lowered first. Returns the exit code.
|
||||
* baseline is lowered first. An entry above the file's size now, or for a
|
||||
* file that is gone, fails as well until --update lowers it. Returns the
|
||||
* exit code.
|
||||
*/
|
||||
export function check({
|
||||
root,
|
||||
@@ -86,7 +90,7 @@ export function check({
|
||||
}
|
||||
|
||||
const grown = Object.entries(sizes).filter(([key, n]) => n > Math.max(baseline[key] ?? 0, limitOf(key)));
|
||||
const lowerable = Object.entries(baseline).filter(([key, n]) => (sizes[key] ?? 0) < n);
|
||||
const stale = staleCounts(baseline, sizes, limitOf);
|
||||
|
||||
for (const [key, n] of grown) {
|
||||
const entry = baseline[key];
|
||||
@@ -96,11 +100,9 @@ export function check({
|
||||
'Move a concern into a module of its own instead of growing the file or packing its lines.'
|
||||
);
|
||||
}
|
||||
if (lowerable.length && !update) {
|
||||
log(`${lowerable.length} file(s) are smaller than their baseline now: run with --update to lower it.`);
|
||||
}
|
||||
reportStale(stale, { file: 'size-baseline.json', command: 'lint:size', root, error });
|
||||
log(
|
||||
`size: ${Object.keys(sizes).length} file(s), ${Object.keys(baseline).length} past their limit held at their baseline`
|
||||
);
|
||||
return grown.length ? 1 : 0;
|
||||
return grown.length || stale.length ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
lowerCounts,
|
||||
readBaseline,
|
||||
readText,
|
||||
reportStale,
|
||||
staleCounts,
|
||||
TEST_FILE,
|
||||
toKey,
|
||||
writeBaseline,
|
||||
@@ -139,12 +141,11 @@ export function check({
|
||||
if (grown.length) {
|
||||
error('Fix the test, delete it if its feature is gone, or use skipIf/runIf when it depends on the environment.');
|
||||
}
|
||||
const lowerable = Object.entries(baseline).filter(([key, n]) => (counts[key] ?? 0) < n);
|
||||
if (lowerable.length && !update)
|
||||
log(`${lowerable.length} file(s) skip fewer tests than the baseline: run with --update to lower it.`);
|
||||
const stale = staleCounts(baseline, counts);
|
||||
reportStale(stale, { file: 'skip-baseline.json', command: 'lint:skips', root, error });
|
||||
const sum = (map) => Object.values(map).reduce((a, b) => a + b, 0);
|
||||
log(
|
||||
`skips: ${sum(counts)} skipped or todo test(s), baseline allows ${sum(baseline)}; ${Object.keys(only).length} file(s) with .only`
|
||||
);
|
||||
return grown.length || Object.keys(only).length ? 1 : 0;
|
||||
return grown.length || stale.length || Object.keys(only).length ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
* carries `rtl-lint-disable` in a comment (in a string it is no marker). The
|
||||
* marked lines are counted too, against scripts/rtl-disable-baseline.json, so
|
||||
* the marker cannot quietly take the place of the logical forms: a new one
|
||||
* raises its file's entry by hand, where review sees it.
|
||||
* raises its file's entry by hand, where review sees it. An entry of either
|
||||
* baseline above what its file holds now, or for a file that is gone, fails
|
||||
* as well until --update lowers it.
|
||||
*
|
||||
* npm run lint:rtl check against the baselines (CI)
|
||||
* npm run lint:rtl -- --list print every counted use, file by file
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
* that were already larger are listed in scripts/size-baseline.json with the
|
||||
* size they had, and the check fails when one of them grows past its entry. A
|
||||
* file that needs to grow is split by concern instead (the trip planner hook,
|
||||
* page and road trip sidebar are the precedent).
|
||||
* page and road trip sidebar are the precedent). An entry above the file's
|
||||
* size now, or for a file that is gone, fails as well until --update lowers
|
||||
* it: otherwise the file could grow back unseen, and a new file at a deleted
|
||||
* path would inherit its allowance.
|
||||
*
|
||||
* npm run lint:size check against the baseline (CI)
|
||||
* npm run lint:size -- --update lower the baseline to what the files hold now;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* switched off and then forgotten (JourneyDetailPage.test.tsx has carried 35
|
||||
* skipped blocks since April); the files that hold some are listed in
|
||||
* scripts/skip-baseline.json with their count, and a file may hold no more.
|
||||
* An entry above what its file holds now, or for a file that is gone, fails
|
||||
* as well until --update lowers it.
|
||||
* Playwright's test.fixme counts as a skip. Only a skip that decides at run
|
||||
* time is left out: skipIf and runIf, and in a Playwright spec
|
||||
* test.skip(condition, 'why'), unless the condition is a literal that always
|
||||
|
||||
@@ -67,12 +67,23 @@ describe('lint:warnings', () => {
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
it('ESLINT-002: passes when no count passes its entry', async () => {
|
||||
const code = await run({ ...empty, src: { 'no-empty': 2 } }, (root) => [
|
||||
it('ESLINT-002: passes when every count sits at its entry', async () => {
|
||||
const code = await run({ ...empty, src: { 'no-empty': 1 } }, (root) => [
|
||||
result(root, 'src/a.ts', [warn('no-empty')]),
|
||||
]);
|
||||
expect(code).toBe(0);
|
||||
expect(tree.log.join('\n')).toMatch(/fell below the baseline/);
|
||||
});
|
||||
|
||||
it('ESLINT-010: fails a count that fell below its entry, or a rule with none left, until --update', async () => {
|
||||
const baseline = { ...empty, src: { 'no-empty': 2 }, suppressed: { 'react-hooks/exhaustive-deps': 1 } };
|
||||
const results = (root: string) => [result(root, 'src/a.ts', [warn('no-empty')])];
|
||||
expect(await run(baseline, results)).toBe(1);
|
||||
const out = tree.error.join('\n');
|
||||
expect(out).toMatch(/src: no-empty is held at 2 in scripts\/eslint-baseline\.json, but there are 1 now/);
|
||||
expect(out).toMatch(/suppressed: react-hooks\/exhaustive-deps is held at 1 .*but there are 0 now/);
|
||||
expect(out).toMatch(/npm run lint:warnings -- --update/);
|
||||
tree.remove();
|
||||
expect(await run(baseline, results, true)).toBe(0);
|
||||
});
|
||||
|
||||
it('ESLINT-003: fails a new warning and names the files behind it', async () => {
|
||||
|
||||
@@ -120,6 +120,17 @@ describe('lint:layers', () => {
|
||||
expect(JSON.parse(readFileSync(tree.path(BASELINE), 'utf8'))).toEqual({ 'src/components/A.tsx': 1 });
|
||||
});
|
||||
|
||||
it('LAYERS-008: fails an entry above the imports a file holds, or for a file that is gone, until --update', () => {
|
||||
const files = { 'src/components/A.tsx': "import { m } from '../pages/x/model'\n" };
|
||||
expect(run({ 'src/components/A.tsx': 2, 'src/components/Gone.tsx': 1 }, files)).toBe(1);
|
||||
const out = tree.error.join('\n');
|
||||
expect(out).toMatch(/src\/components\/A\.tsx is held at 2 in scripts\/layers-baseline\.json, but there are 1 now/);
|
||||
expect(out).toMatch(/src\/components\/Gone\.tsx is held at 1 .*but the file is gone/);
|
||||
expect(out).toMatch(/npm run lint:layers -- --update/);
|
||||
tree.remove();
|
||||
expect(run({ 'src/components/A.tsx': 2, 'src/components/Gone.tsx': 1 }, files, true)).toBe(0);
|
||||
});
|
||||
|
||||
it('LAYERS-007: help/ and vacay/ are helpers and import no view', () => {
|
||||
for (const helper of ['help', 'vacay']) {
|
||||
expect(RULES[helper as keyof typeof RULES]).toEqual(
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
RatchetError,
|
||||
readBaseline,
|
||||
readText,
|
||||
reportStale,
|
||||
staleCounts,
|
||||
writeBaseline,
|
||||
} from '../../../scripts/lib/ratchet.mjs';
|
||||
import { ratchetTree, type RatchetTree } from '../../helpers/ratchetFixture';
|
||||
@@ -79,6 +81,36 @@ describe('scripts/lib/ratchet', () => {
|
||||
expect(lowerCounts({ a: 9 }, { a: 4 }, (key: string) => (key === 'a' ? 4 : 0))).toEqual({});
|
||||
});
|
||||
|
||||
it('RATCHET-010: an entry above the count, at the floor or for a gone file is stale, one at the count is not', () => {
|
||||
expect(
|
||||
staleCounts({ held: 5, shrank: 5, gone: 5, floor: 5 }, { held: 5, shrank: 3, floor: 2, fresh: 9 }, 2)
|
||||
).toEqual([
|
||||
{ key: 'shrank', entry: 5, now: 3 },
|
||||
{ key: 'gone', entry: 5, now: 0 },
|
||||
{ key: 'floor', entry: 5, now: 2 },
|
||||
]);
|
||||
expect(staleCounts({ a: 3 }, { a: 4 })).toEqual([]);
|
||||
});
|
||||
|
||||
it('RATCHET-011: a stale entry is reported with its count, or as gone, and how to lower it', () => {
|
||||
tree = ratchetTree({ 'src/here.ts': 'x\n' });
|
||||
const lines: string[] = [];
|
||||
const error = (line: string) => lines.push(line);
|
||||
const stale = [
|
||||
{ key: 'src/here.ts', entry: 4, now: 2 },
|
||||
{ key: 'src/gone.ts', entry: 3, now: 0 },
|
||||
];
|
||||
reportStale(stale, { file: 'b.json', command: 'lint:x', root: tree.root, error });
|
||||
expect(lines).toEqual([
|
||||
'FAIL src/here.ts is held at 4 in scripts/b.json, but there are 2 now.',
|
||||
'FAIL src/gone.ts is held at 3 in scripts/b.json, but the file is gone.',
|
||||
expect.stringMatching(/^Run npm run lint:x -- --update to lower the baseline/),
|
||||
]);
|
||||
lines.length = 0;
|
||||
reportStale([], { file: 'b.json', command: 'lint:x', error });
|
||||
expect(lines).toEqual([]);
|
||||
});
|
||||
|
||||
it('RATCHET-008: a file list only loses the files that stopped offending', () => {
|
||||
expect(lowerList(['a', 'b', 'c'], ['c', 'a', 'new'])).toEqual(['a', 'c']);
|
||||
});
|
||||
|
||||
@@ -76,6 +76,24 @@ describe('lint:rtl', () => {
|
||||
expect(JSON.parse(readFileSync(tree.path(MARKERS), 'utf8'))).toEqual({ 'a.tsx': 1 });
|
||||
});
|
||||
|
||||
it('RTL-009: fails an entry of either baseline above the file, or for a file that is gone, until --update', () => {
|
||||
const files = { 'src/a.tsx': 'const a = <div className="ml-2" />\nconst s = { left: 0 } // rtl-lint-disable\n' };
|
||||
expect(run({ 'a.tsx': 2 }, { 'a.tsx': 1 }, files)).toBe(1);
|
||||
expect(tree.error.join('\n')).toMatch(/a\.tsx is held at 2 in scripts\/rtl-baseline\.json, but there are 1 now/);
|
||||
tree.remove();
|
||||
expect(run({ 'a.tsx': 1 }, { 'a.tsx': 2 }, files)).toBe(1);
|
||||
expect(tree.error.join('\n')).toMatch(
|
||||
/a\.tsx is held at 2 in scripts\/rtl-disable-baseline\.json, but there are 1 now/
|
||||
);
|
||||
tree.remove();
|
||||
expect(run({ 'a.tsx': 1, 'gone.tsx': 3 }, { 'a.tsx': 1, 'gone.tsx': 1 }, files)).toBe(1);
|
||||
const out = tree.error.join('\n');
|
||||
expect(out).toMatch(/gone\.tsx is held at 3 in scripts\/rtl-baseline\.json, but the file is gone/);
|
||||
expect(out).toMatch(/gone\.tsx is held at 1 in scripts\/rtl-disable-baseline\.json, but the file is gone/);
|
||||
tree.remove();
|
||||
expect(run({ 'a.tsx': 2, 'gone.tsx': 3 }, { 'a.tsx': 2, 'gone.tsx': 1 }, files, true)).toBe(0);
|
||||
});
|
||||
|
||||
it('RTL-007: a missing or broken baseline stops the check instead of reading as empty', () => {
|
||||
tree = ratchetTree({ [USES]: '{}', 'src/a.tsx': 'const a = 1\n' });
|
||||
expect(() =>
|
||||
|
||||
@@ -94,6 +94,23 @@ describe('lint:size', () => {
|
||||
expect(tree.error.join('\n')).toMatch(/src\/grew\.ts: 1150 lines/);
|
||||
});
|
||||
|
||||
it('SIZE-011: fails an entry above the file it holds, or for a file that is gone, until --update', () => {
|
||||
expect(run({ 'src/big.ts': 1200 }, { 'src/big.ts': lines(1100) })).toBe(1);
|
||||
expect(tree.error.join('\n')).toMatch(
|
||||
/src\/big\.ts is held at 1200 in scripts\/size-baseline\.json, but there are 1100 now/
|
||||
);
|
||||
expect(tree.error.join('\n')).toMatch(/npm run lint:size -- --update/);
|
||||
tree.remove();
|
||||
// A deleted file would hand its allowance to the next file at its path.
|
||||
expect(run({ 'src/gone.ts': 1200 }, { 'src/a.ts': lines(1) })).toBe(1);
|
||||
expect(tree.error.join('\n')).toMatch(/src\/gone\.ts is held at 1200 .*but the file is gone/);
|
||||
tree.remove();
|
||||
// Back under the limit is stale too: the entry would let it grow past the limit again.
|
||||
expect(run({ 'src/big.ts': 1200 }, { 'src/big.ts': lines(900) })).toBe(1);
|
||||
tree.remove();
|
||||
expect(run({ 'src/big.ts': 1200, 'src/gone.ts': 1200 }, { 'src/big.ts': lines(1100) }, true)).toBe(0);
|
||||
});
|
||||
|
||||
it('SIZE-009: a broken baseline stops the check', () => {
|
||||
tree = ratchetTree({ [BASELINE]: '{', 'src/a.ts': lines(1), 'tests/a.ts': lines(1), 'e2e/a.ts': lines(1) });
|
||||
expect(() => check({ root: tree.root, baselinePath: tree.path(BASELINE), ...tree.out })).toThrow(RatchetError);
|
||||
|
||||
@@ -93,6 +93,17 @@ describe('lint:skips', () => {
|
||||
expect(JSON.parse(readFileSync(tree.path(BASELINE), 'utf8'))).toEqual({ 'src/a.test.ts': 2 });
|
||||
});
|
||||
|
||||
it('SKIPS-010: fails an entry above the skips a file holds, or for a file that is gone, until --update', () => {
|
||||
const files = { 'src/a.test.ts': "it.skip('a', () => {})\n" };
|
||||
expect(run({ 'src/a.test.ts': 2, 'tests/gone.test.ts': 1 }, files)).toBe(1);
|
||||
const out = tree.error.join('\n');
|
||||
expect(out).toMatch(/src\/a\.test\.ts is held at 2 in scripts\/skip-baseline\.json, but there are 1 now/);
|
||||
expect(out).toMatch(/tests\/gone\.test\.ts is held at 1 .*but the file is gone/);
|
||||
expect(out).toMatch(/npm run lint:skips -- --update/);
|
||||
tree.remove();
|
||||
expect(run({ 'src/a.test.ts': 2, 'tests/gone.test.ts': 1 }, files, true)).toBe(0);
|
||||
});
|
||||
|
||||
it('SKIPS-009: counts every vitest skip, whatever its title is', () => {
|
||||
const source = [
|
||||
"const T = 'a'",
|
||||
|
||||
Reference in New Issue
Block a user