mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
bd267c67f8
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.
119 lines
5.4 KiB
TypeScript
119 lines
5.4 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import { RatchetError } from '../../../scripts/lib/ratchet.mjs';
|
|
import { check, groupOf, sizeOf, WIDTH } from '../../../scripts/lib/size.mjs';
|
|
import { lines, ratchetTree, type RatchetTree } from '../../helpers/ratchetFixture';
|
|
|
|
let tree: RatchetTree;
|
|
afterEach(() => tree?.remove());
|
|
|
|
const BASELINE = 'scripts/size-baseline.json';
|
|
|
|
function run(baseline: Record<string, number>, files: Record<string, string>, update = false) {
|
|
tree = ratchetTree({
|
|
[BASELINE]: JSON.stringify(baseline),
|
|
'tests/keep.test.ts': lines(1),
|
|
'e2e/keep.spec.ts': lines(1),
|
|
...files,
|
|
});
|
|
return check({ root: tree.root, baselinePath: tree.path(BASELINE), update, ...tree.out });
|
|
}
|
|
|
|
describe('lint:size', () => {
|
|
it('SIZE-001: counts lines as an editor wraps them, so joining lines does not shrink a file', () => {
|
|
expect(sizeOf('')).toBe(0);
|
|
expect(sizeOf('a\nb\n')).toBe(2);
|
|
expect(sizeOf('a\nb')).toBe(2);
|
|
expect(sizeOf('a\r\nb\r\n')).toBe(2);
|
|
expect(sizeOf('x'.repeat(WIDTH))).toBe(1);
|
|
expect(sizeOf('x'.repeat(WIDTH + 1))).toBe(2);
|
|
expect(sizeOf(`${'x'.repeat(3760)}\n`)).toBe(Math.ceil(3760 / WIDTH));
|
|
expect(sizeOf('\n\n')).toBe(2);
|
|
});
|
|
|
|
it('SIZE-002: sorts files into source, stylesheet and test, each with its own limit', () => {
|
|
expect(groupOf('src/a/B.tsx')).toMatchObject({ name: 'source', limit: 1000 });
|
|
expect(groupOf('src/index.css')).toMatchObject({ name: 'stylesheet', limit: 1000 });
|
|
expect(groupOf('src/a/B.test.tsx')).toMatchObject({ name: 'test', limit: 2000 });
|
|
expect(groupOf('tests/unit/x.ts')).toMatchObject({ name: 'test', limit: 2000 });
|
|
expect(groupOf('e2e/help/fixtures.ts')).toMatchObject({ name: 'test', limit: 2000 });
|
|
expect(groupOf('e2e/server-launch.mjs')).toBeNull();
|
|
expect(groupOf('src/a/notes.md')).toBeNull();
|
|
});
|
|
|
|
it('SIZE-003: passes files within their limits', () => {
|
|
const code = run({}, { 'src/a.ts': lines(1000), 'src/a.css': lines(1000), 'tests/a.test.ts': lines(2000) });
|
|
expect(code).toBe(0);
|
|
expect(tree.error).toEqual([]);
|
|
});
|
|
|
|
it('SIZE-004: fails a new source file past 1000 lines', () => {
|
|
expect(run({}, { 'src/big.tsx': lines(1001) })).toBe(1);
|
|
expect(tree.error.join('\n')).toMatch(/src\/big\.tsx: 1001 lines .*limit for a source file is 1000/);
|
|
});
|
|
|
|
it('SIZE-005: fails a file that packs its code into long lines', () => {
|
|
expect(run({}, { 'src/packed.tsx': `${'x'.repeat(WIDTH * 2)}\n`.repeat(600) })).toBe(1);
|
|
expect(tree.error.join('\n')).toMatch(/src\/packed\.tsx: 1200 lines/);
|
|
});
|
|
|
|
it('SIZE-006: covers stylesheets and tests', () => {
|
|
expect(run({}, { 'src/styles/a.css': lines(1001), 'src/A.test.tsx': lines(2001) })).toBe(1);
|
|
expect(tree.error.join('\n')).toMatch(/src\/styles\/a\.css/);
|
|
expect(tree.error.join('\n')).toMatch(/src\/A\.test\.tsx: 2001 lines .*test file is 2000/);
|
|
});
|
|
|
|
it('SIZE-010: covers the Playwright specs and fixtures under e2e/', () => {
|
|
const files = { 'src/a.ts': lines(1), 'e2e/help/fixtures.ts': lines(2001), 'e2e/a.spec.ts': lines(2000) };
|
|
expect(run({}, files)).toBe(1);
|
|
expect(tree.error.join('\n')).toMatch(/e2e\/help\/fixtures\.ts: 2001 lines .*test file is 2000/);
|
|
expect(tree.error.join('\n')).not.toMatch(/e2e\/a\.spec\.ts/);
|
|
});
|
|
|
|
it('SIZE-007: holds a baselined file at its entry and fails it once it grows', () => {
|
|
expect(run({ 'src/big.ts': 1200 }, { 'src/big.ts': lines(1200) })).toBe(0);
|
|
tree.remove();
|
|
expect(run({ 'src/big.ts': 1200 }, { 'src/big.ts': lines(1201) })).toBe(1);
|
|
expect(tree.error.join('\n')).toMatch(/its baseline is 1200/);
|
|
});
|
|
|
|
it('SIZE-008: --update lowers an entry, drops one back under the limit and never raises', () => {
|
|
run(
|
|
{ 'src/shrank.ts': 1500, 'src/back.ts': 1200, 'src/grew.ts': 1100 },
|
|
{
|
|
'src/shrank.ts': lines(1300),
|
|
'src/back.ts': lines(900),
|
|
'src/grew.ts': lines(1150),
|
|
},
|
|
true
|
|
);
|
|
expect(JSON.parse(readFileSync(tree.path(BASELINE), 'utf8'))).toEqual({
|
|
'src/shrank.ts': 1300,
|
|
'src/grew.ts': 1100,
|
|
});
|
|
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);
|
|
});
|
|
});
|