feat: adopt strict TDD framework and add adversarial test suite
- Update AGENTS.md and .agents/rules/blog-system.md with strict TDD mandate - Extract Eleventy custom filters into modular src/_filters/index.js for unit testing - Add npm test script using Node.js native test runner (node:test) - Create test/site-config.test.js for environment validation and fatal error assertions - Create test/filters.test.js for adversarial filter edge cases (dates, limits, reading time, headings) - Create test/frontmatter-schema.test.js enforcing post metadata contracts - Create test/design-rules.test.js enforcing strict anti-patterns (no glassmorphism, no shadows, max 4px radius) - Create test/build-integrity.test.js checking static output, zero 404 links, and TOC anchor targets
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const cssDir = path.resolve("src/assets/css");
|
||||
|
||||
function getAllCssFiles(dir) {
|
||||
let results = [];
|
||||
const list = fs.readdirSync(dir);
|
||||
for (const file of list) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat && stat.isDirectory()) {
|
||||
results = results.concat(getAllCssFiles(filePath));
|
||||
} else if (file.endsWith(".css")) {
|
||||
results.push(filePath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
test("Design Specification: Strict Anti-Pattern Regression Testing", () => {
|
||||
const cssFiles = getAllCssFiles(cssDir);
|
||||
assert.ok(cssFiles.length >= 5, "Must inspect all modular CSS files");
|
||||
|
||||
for (const file of cssFiles) {
|
||||
const relPath = path.relative(process.cwd(), file);
|
||||
const content = fs.readFileSync(file, "utf-8");
|
||||
|
||||
// 1. Anti-Pattern: NO Glassmorphism (backdrop-filter)
|
||||
assert.doesNotMatch(
|
||||
content,
|
||||
/backdrop-filter\s*:/i,
|
||||
`Violation in ${relPath}: Glassmorphism (backdrop-filter) is strictly forbidden`
|
||||
);
|
||||
|
||||
// 2. Anti-Pattern: NO Drop Shadows or Glows (box-shadow, text-shadow)
|
||||
// Allowed: box-shadow: none; or text-shadow: none;
|
||||
const shadowMatches = content.match(/(box-shadow|text-shadow)\s*:\s*([^;]+);/gi) || [];
|
||||
for (const match of shadowMatches) {
|
||||
assert.match(
|
||||
match,
|
||||
/(box-shadow|text-shadow)\s*:\s*none\s*;/i,
|
||||
`Violation in ${relPath}: Drop shadows and glows are forbidden (found: '${match}')`
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Anti-Pattern: NO Gradients (linear, radial, conic)
|
||||
assert.doesNotMatch(
|
||||
content,
|
||||
/(linear-gradient|radial-gradient|conic-gradient)/i,
|
||||
`Violation in ${relPath}: Gradients are strictly forbidden`
|
||||
);
|
||||
|
||||
// 4. Anti-Pattern: NO Rounded Bubbles (border-radius capped at 4px max)
|
||||
const radiusMatches = content.match(/border-radius\s*:\s*([^;]+);/gi) || [];
|
||||
for (const match of radiusMatches) {
|
||||
// Check for pixel values > 4px (e.g. 5px, 6px, 8px, 12px, 20px)
|
||||
const pxMatch = match.match(/(\d+)\s*px/i);
|
||||
if (pxMatch) {
|
||||
const px = parseInt(pxMatch[1], 10);
|
||||
assert.ok(
|
||||
px <= 4,
|
||||
`Violation in ${relPath}: Border radius must be 0px to 4px max, found: ${px}px in '${match}'`
|
||||
);
|
||||
}
|
||||
// Check for percentages (e.g. 50%)
|
||||
assert.doesNotMatch(
|
||||
match,
|
||||
/\d+%/,
|
||||
`Violation in ${relPath}: Rounded pill/circular percentages forbidden in '${match}'`
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Anti-Pattern: NO Float/Spring Transitions (Must be instant snaps)
|
||||
const transitionMatches = content.match(/transition\s*:\s*([^;]+);/gi) || [];
|
||||
for (const match of transitionMatches) {
|
||||
assert.match(
|
||||
match,
|
||||
/transition\s*:\s*(none(\s*!important)?|0s\s*);/i,
|
||||
`Violation in ${relPath}: Transitions must be instant (none or 0s), found: '${match}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user