ci(client): lint:size measures the Playwright specs and fixtures too

The size check walked src/ and tests/ only, while lint:format and
lint:skips already cover e2e/. The help center fixtures under
e2e/help/fixtures.ts hold 2291 wrapped lines, past the 2000 a test may
reach, and could grow without bound. e2e/ is now measured with the test
limit; fixtures.ts enters the baseline at its current size, every other
file there is within the limit.
This commit is contained in:
Maurice
2026-10-08 12:52:18 +02:00
parent 3e37dcfb45
commit 9d596d9916
5 changed files with 25 additions and 8 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ 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; --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 past 2000, counted wrapped at 120 columns; longer ones may only shrink (CI gate; --update lowers the baseline)
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)
npm run lint:format # Prettier: every .ts/.tsx/.mjs/.css file under src/, tests/, e2e/ and scripts/ outside scripts/format-baseline.json must be formatted, the list only shrinks (CI gate; npx prettier --write <file>, then --update)
npm run lint:layers # imports go downwards: components never import pages/ or mobile/, nothing under the views imports a view; per-file counts may only shrink (CI gate; --list, --update)
npm run lint:offline # no view file (components/mobile/pages/hooks) imports src/api/ beyond scripts/offline-baseline.json, which only shrinks (CI gate; --list, --update)
+5 -3
View File
@@ -24,13 +24,15 @@ export const WIDTH = 120;
/**
* The kinds of file the check covers, each with the size a file without a
* baseline entry may reach. Tests get more room: a spec mirrors the component
* it covers and holds fixtures, but past LIMIT it is a god file too.
* it covers and holds fixtures, but past LIMIT it is a god file too. The
* Playwright specs and their fixtures under e2e/ count as tests.
*/
export const GROUPS = [
{
name: 'test',
limit: 2000,
accepts: (key) => /\.tsx?$/.test(key) && (key.startsWith('tests/') || TEST_FILE.test(key)),
accepts: (key) =>
/\.tsx?$/.test(key) && (key.startsWith('tests/') || key.startsWith('e2e/') || TEST_FILE.test(key)),
},
{
name: 'source',
@@ -40,7 +42,7 @@ export const GROUPS = [
{ name: 'stylesheet', limit: 1000, accepts: (key) => key.startsWith('src/') && key.endsWith('.css') },
];
export const DIRS = ['src', 'tests'];
export const DIRS = ['src', 'tests', 'e2e'];
export function groupOf(key) {
return GROUPS.find((group) => group.accepts(key)) ?? null;
+1
View File
@@ -1,4 +1,5 @@
{
"e2e/help/fixtures.ts": 2291,
"src/api/client.ts": 1916,
"src/components/Admin/AdminPluginsPanel.test.tsx": 2704,
"src/components/Admin/AdminPluginsPanel.tsx": 2431,
+2 -2
View File
@@ -4,8 +4,8 @@
*
* A file past a thousand lines holds several concerns that no longer fit in
* one reading, and every change to one of them risks the others. Each source
* file and stylesheet under src/ may hold 1000 lines, each test (under tests/
* or a co-located *.test.*) 2000. Lines are counted as an editor wraps them
* file and stylesheet under src/ may hold 1000 lines, each test (under tests/,
* a Playwright spec or fixture under e2e/, or a co-located *.test.*) 2000. Lines are counted as an editor wraps them
* at 120 columns, so joining lines does not make a file smaller. The files
* 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
+16 -2
View File
@@ -10,7 +10,12 @@ 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), ...files });
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 });
}
@@ -31,6 +36,8 @@ describe('lint:size', () => {
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();
});
@@ -56,6 +63,13 @@ describe('lint:size', () => {
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();
@@ -81,7 +95,7 @@ describe('lint:size', () => {
});
it('SIZE-009: a broken baseline stops the check', () => {
tree = ratchetTree({ [BASELINE]: '{', 'src/a.ts': lines(1), 'tests/a.ts': lines(1) });
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);
});
});