mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
Merge pull request #661 from Psychotoxical/feat/css-import-graph-check
test(frontend): verify global stylesheet @import graph after vitest
This commit is contained in:
@@ -13,6 +13,7 @@ on:
|
|||||||
- '.github/workflows/frontend-tests.yml'
|
- '.github/workflows/frontend-tests.yml'
|
||||||
- '.github/frontend-hot-path-files.txt'
|
- '.github/frontend-hot-path-files.txt'
|
||||||
- 'scripts/check-frontend-hot-path-coverage.sh'
|
- 'scripts/check-frontend-hot-path-coverage.sh'
|
||||||
|
- 'scripts/check-css-import-graph.mjs'
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
@@ -25,6 +26,7 @@ on:
|
|||||||
- '.github/workflows/frontend-tests.yml'
|
- '.github/workflows/frontend-tests.yml'
|
||||||
- '.github/frontend-hot-path-files.txt'
|
- '.github/frontend-hot-path-files.txt'
|
||||||
- 'scripts/check-frontend-hot-path-coverage.sh'
|
- 'scripts/check-frontend-hot-path-coverage.sh'
|
||||||
|
- 'scripts/check-css-import-graph.mjs'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
+3
-2
@@ -3,15 +3,16 @@
|
|||||||
"version": "1.46.0-dev",
|
"version": "1.46.0-dev",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"check:css-imports": "node scripts/check-css-import-graph.mjs",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri",
|
"tauri": "tauri",
|
||||||
"tauri:dev": "tauri dev",
|
"tauri:dev": "tauri dev",
|
||||||
"tauri:build": "tauri build",
|
"tauri:build": "tauri build",
|
||||||
"test": "vitest run",
|
"test": "vitest run && npm run check:css-imports",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"test:coverage": "vitest run --coverage"
|
"test:coverage": "vitest run --coverage && npm run check:css-imports"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/dm-sans": "^5.2.8",
|
"@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 test # one-shot run
|
||||||
npm run test:watch # watch mode
|
npm run test:watch # watch mode
|
||||||
npm run test:coverage # with v8 coverage → ./coverage/
|
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
|
## Where tests go
|
||||||
|
|
||||||
- **Co-located with the unit under test**: `Foo.tsx` → `Foo.test.tsx`,
|
- **Co-located with the unit under test**: `Foo.tsx` → `Foo.test.tsx`,
|
||||||
|
|||||||
Reference in New Issue
Block a user