mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
test(frontend): verify global stylesheet @import graph after vitest
Add scripts/check-css-import-graph.mjs and run it from npm test and test:coverage so missing relative CSS imports fail CI like Vite/postcss. Document the step in src/test/README.md; trigger frontend workflow when the script changes.
This commit is contained in:
@@ -13,6 +13,7 @@ on:
|
||||
- '.github/workflows/frontend-tests.yml'
|
||||
- '.github/frontend-hot-path-files.txt'
|
||||
- 'scripts/check-frontend-hot-path-coverage.sh'
|
||||
- 'scripts/check-css-import-graph.mjs'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
@@ -25,6 +26,7 @@ on:
|
||||
- '.github/workflows/frontend-tests.yml'
|
||||
- '.github/frontend-hot-path-files.txt'
|
||||
- 'scripts/check-frontend-hot-path-coverage.sh'
|
||||
- 'scripts/check-css-import-graph.mjs'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
||||
+3
-2
@@ -3,15 +3,16 @@
|
||||
"version": "1.46.0-dev",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"check:css-imports": "node scripts/check-css-import-graph.mjs",
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:build": "tauri build",
|
||||
"test": "vitest run",
|
||||
"test": "vitest run && npm run check:css-imports",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "vitest run --coverage"
|
||||
"test:coverage": "vitest run --coverage && npm run check:css-imports"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/dm-sans": "^5.2.8",
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Walk the same root CSS entrypoints as src/main.tsx and ensure every
|
||||
* relative @import target exists on disk. Catches postcss-import ENOENT
|
||||
* that Vitest does not surface (CSS is not fully resolved like vite build).
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, '..');
|
||||
const stylesRoot = path.join(repoRoot, 'src', 'styles');
|
||||
|
||||
const ENTRY_RELS = [
|
||||
'themes/index.css',
|
||||
'layout/index.css',
|
||||
'components/index.css',
|
||||
'tracks/index.css',
|
||||
];
|
||||
|
||||
/** Quoted path in @import '...' or "..." (postcss-import style in this repo). */
|
||||
const IMPORT_RE = /@import\s+(['"])([^'"]+)\1/g;
|
||||
|
||||
function isFilesystemImport(spec) {
|
||||
if (spec.startsWith('http://') || spec.startsWith('https://') || spec.startsWith('data:')) {
|
||||
return false;
|
||||
}
|
||||
if (spec.startsWith('@')) return false;
|
||||
return spec.startsWith('./') || spec.startsWith('../');
|
||||
}
|
||||
|
||||
function walk(absPath, visited, missing) {
|
||||
const key = path.resolve(absPath);
|
||||
if (visited.has(key)) return;
|
||||
if (!fs.existsSync(key)) {
|
||||
missing.add(path.relative(repoRoot, key));
|
||||
visited.add(key);
|
||||
return;
|
||||
}
|
||||
visited.add(key);
|
||||
const text = fs.readFileSync(key, 'utf8');
|
||||
let m;
|
||||
IMPORT_RE.lastIndex = 0;
|
||||
while ((m = IMPORT_RE.exec(text)) !== null) {
|
||||
const spec = m[2];
|
||||
if (!isFilesystemImport(spec)) continue;
|
||||
const next = path.resolve(path.dirname(key), spec);
|
||||
walk(next, visited, missing);
|
||||
}
|
||||
}
|
||||
|
||||
const visited = new Set();
|
||||
const missing = new Set();
|
||||
for (const rel of ENTRY_RELS) {
|
||||
walk(path.join(stylesRoot, rel), visited, missing);
|
||||
}
|
||||
|
||||
if (missing.size > 0) {
|
||||
const list = [...missing].sort().join('\n ');
|
||||
console.error(`check-css-import-graph: missing @import target(s):\n ${list}`);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -33,8 +33,21 @@ src/test/
|
||||
npm test # one-shot run
|
||||
npm run test:watch # watch mode
|
||||
npm run test:coverage # with v8 coverage → ./coverage/
|
||||
npm run check:css-imports # only the global stylesheet @import graph (see below)
|
||||
```
|
||||
|
||||
## CSS `@import` graph
|
||||
|
||||
Vitest does not load the full global CSS bundle from `main.tsx`, so a broken
|
||||
relative `@import` under `src/styles/**` can slip past the suite until Vite
|
||||
runs (`ENOENT` from postcss-import).
|
||||
|
||||
After **`vitest run`**, **`npm test`** and **`npm run test:coverage`** run
|
||||
**`npm run check:css-imports`**, which executes **`scripts/check-css-import-graph.mjs`**
|
||||
and walks the same four root stylesheets as **`src/main.tsx`**, resolving
|
||||
only filesystem-relative imports (`./…`, `../…`). Package imports such as
|
||||
`@fontsource/...` are ignored.
|
||||
|
||||
## Where tests go
|
||||
|
||||
- **Co-located with the unit under test**: `Foo.tsx` → `Foo.test.tsx`,
|
||||
|
||||
Reference in New Issue
Block a user