Files
blog/test/design-rules.test.js
T
venus e4601dbbcb 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
2026-09-13 01:16:20 -05:00

87 lines
2.9 KiB
JavaScript

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}'`
);
}
}
});