Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Run script tests
run: pnpm vitest run scripts/__tests__/ --reporter=verbose
run: pnpm vitest run --config scripts/__tests__/vitest.config.js scripts/__tests__/ --reporter=verbose

# ── Lint & format check (needs Node + project with eslint/prettier) ───
lint:
Expand Down
160 changes: 160 additions & 0 deletions scripts/__tests__/audit-cross-browser-css.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { execFileSync } from "child_process";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import { mkdirSync, writeFileSync, rmSync, existsSync, readdirSync } from "fs";

const __dirname = dirname(fileURLToPath(import.meta.url));
const SCRIPT = join(__dirname, "..", "audit-cross-browser-css.sh");

let counter = 0;

function createTmpDir() {
counter++;
const dir = join(__dirname, "fixtures", `audit-css-${counter}-${Date.now()}`);
mkdirSync(dir, { recursive: true });
return dir;
}

function run(cwd, args = []) {
try {
const stdout = execFileSync("bash", [SCRIPT, ...args], {
encoding: "utf-8",
timeout: 15000,
cwd,
});
return { stdout, exitCode: 0 };
} catch (err) {
return {
stdout: err.stdout || "",
stderr: err.stderr || "",
exitCode: err.status,
};
}
}

afterAll(() => {
const fixturesDir = join(__dirname, "fixtures");
if (existsSync(fixturesDir)) {
try {
for (const entry of readdirSync(fixturesDir)) {
if (entry.startsWith("audit-css-")) {
rmSync(join(fixturesDir, entry), { recursive: true, force: true });
}
}
} catch {
// Ignore cleanup errors
}
}
});

describe("audit-cross-browser-css.sh — clean project", () => {
let dir;

beforeAll(() => {
dir = createTmpDir();
mkdirSync(join(dir, "src"), { recursive: true });
writeFileSync(
join(dir, "src", "styles.css"),
`.button {
background: var(--primary);
border-radius: 4px;
}`,
);
});

it("reports no issues on clean CSS", () => {
const result = run(dir, [join(dir, "src")]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("Issues found: 0");
expect(result.stdout).toContain("All clear");
});
});

describe("audit-cross-browser-css.sh — webkit prefix detection", () => {
let dir;

beforeAll(() => {
dir = createTmpDir();
mkdirSync(join(dir, "src"), { recursive: true });
writeFileSync(
join(dir, "src", "app.css"),
`.gradient {
-webkit-linear-gradient(top, red, blue);
background: linear-gradient(top, red, blue);
}`,
);
});

it("detects -webkit- prefixed properties", () => {
const result = run(dir, [join(dir, "src")]);
expect(result.stdout).toContain("-webkit-");
expect(result.stdout).toContain("Vendor prefix");
});
});

describe("audit-cross-browser-css.sh — backdrop-filter without prefix", () => {
let dir;

beforeAll(() => {
dir = createTmpDir();
mkdirSync(join(dir, "src"), { recursive: true });
writeFileSync(
join(dir, "src", "overlay.css"),
`.overlay {
backdrop-filter: blur(10px);
}`,
);
});

it("detects backdrop-filter without -webkit- prefix", () => {
const result = run(dir, [join(dir, "src")]);
expect(result.stdout).toContain("backdrop-filter");
expect(result.stdout).toContain("Safari");
});
});

describe("audit-cross-browser-css.sh — :focus without :focus-visible", () => {
let dir;

beforeAll(() => {
dir = createTmpDir();
mkdirSync(join(dir, "src"), { recursive: true });
writeFileSync(
join(dir, "src", "forms.css"),
`input:focus {
outline: 2px solid blue;
}`,
);
});

it("flags :focus usage without :focus-visible", () => {
const result = run(dir, [join(dir, "src")]);
expect(result.stdout).toContain(":focus");
expect(result.stdout).toContain(":focus-visible");
});
});

describe("audit-cross-browser-css.sh — summary with issue count", () => {
let dir;

beforeAll(() => {
dir = createTmpDir();
mkdirSync(join(dir, "src"), { recursive: true });
writeFileSync(
join(dir, "src", "mixed.css"),
`.a { -webkit-transition: all 0.3s; }
.b:focus { outline: none; }
.c { backdrop-filter: blur(5px); }`,
);
});

it("counts total issues in summary", () => {
const result = run(dir, [join(dir, "src")]);
expect(result.stdout).toContain("Issues found:");
// Should have at least 2 issues (webkit prefix + focus or backdrop)
const match = result.stdout.match(/Issues found: (\d+)/);
expect(match).not.toBeNull();
expect(parseInt(match[1], 10)).toBeGreaterThanOrEqual(2);
});
});
2 changes: 1 addition & 1 deletion scripts/__tests__/canva-pipeline.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { readFileSync, existsSync } from "fs";
import { readFileSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

Expand Down
66 changes: 66 additions & 0 deletions scripts/__tests__/check-dead-code.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, it, expect } from "vitest";
import { execFileSync } from "child_process";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const SCRIPT = join(__dirname, "..", "check-dead-code.sh");
const PROJECT_ROOT = join(__dirname, "..", "..");

/**
* Tests for check-dead-code.sh
*
* Note: This script uses PROJECT_ROOT derived from its own location and cd's into it,
* so it always runs against the actual project. Tests verify CLI behavior and flag parsing.
*/

function run(args = []) {
try {
const stdout = execFileSync("bash", [SCRIPT, ...args], {
encoding: "utf-8",
timeout: 60000,
cwd: PROJECT_ROOT,
});
return { stdout, exitCode: 0 };
} catch (err) {
return {
stdout: err.stdout || "",
stderr: err.stderr || "",
exitCode: err.status,
};
}
}

describe("check-dead-code.sh — help flag", () => {
it("shows usage and exits 0", () => {
const result = run(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("Usage:");
expect(result.stdout).toContain("--json");
});
});

describe("check-dead-code.sh — unknown flag", () => {
it("exits 1 on unknown flag", () => {
const result = run(["--bogus"]);
expect(result.exitCode).toBe(1);
expect(result.stdout).toContain("Unknown flag");
});
});

describe("check-dead-code.sh — runs dead code detection", () => {
it("outputs Dead Code Detection header", { timeout: 120000 }, () => {
const result = run([]);
// Should show the detection header (may pass or fail depending on knip findings)
expect(result.stdout).toContain("Dead Code Detection");
});
});

describe("check-dead-code.sh — JSON output", () => {
it("returns valid JSON with --json flag", { timeout: 120000 }, () => {
const result = run(["--json"]);
const parsed = JSON.parse(result.stdout.trim());
expect(parsed).toHaveProperty("status");
expect(["pass", "fail", "skipped"]).toContain(parsed.status);
});
});
34 changes: 34 additions & 0 deletions scripts/__tests__/check-responsive.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, it, expect } from "vitest";
import { execFileSync } from "child_process";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const SCRIPT = join(__dirname, "..", "check-responsive.sh");

function run(args = []) {
try {
const stdout = execFileSync("bash", [SCRIPT, ...args], {
encoding: "utf-8",
timeout: 15000,
});
return { stdout, exitCode: 0 };
} catch (err) {
return {
stdout: err.stdout || "",
stderr: err.stderr || "",
exitCode: err.status,
};
}
}

describe("check-responsive.sh — help flag", () => {
it("shows usage and exits 0", () => {
const result = run(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("Usage:");
expect(result.stdout).toContain("url");
expect(result.stdout).toContain("output-dir");
expect(result.stdout).toContain("breakpoints");
});
});
90 changes: 90 additions & 0 deletions scripts/__tests__/check-security.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, it, expect } from "vitest";
import { execFileSync } from "child_process";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const SCRIPT = join(__dirname, "..", "check-security.sh");
const PROJECT_ROOT = join(__dirname, "..", "..");

/**
* Tests for check-security.sh
*
* Note: This script uses PROJECT_ROOT derived from its own location and cd's into it,
* so it always runs against the actual project. Tests verify CLI behavior and flag parsing.
* Tests that run pnpm audit need extended timeouts.
*/

function run(args = []) {
try {
const stdout = execFileSync("bash", [SCRIPT, ...args], {
encoding: "utf-8",
timeout: 60000,
cwd: PROJECT_ROOT,
});
return { stdout, exitCode: 0 };
} catch (err) {
return {
stdout: err.stdout || "",
stderr: err.stderr || "",
exitCode: err.status,
};
}
}

describe("check-security.sh — help flag", () => {
it("shows usage and exits 0", () => {
const result = run(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("Usage:");
expect(result.stdout).toContain("--level");
expect(result.stdout).toContain("--no-fail");
expect(result.stdout).toContain("--json");
});
});

describe("check-security.sh — runs audit", { timeout: 120000 }, () => {
it("outputs Security Audit header and summary", () => {
const result = run(["--no-fail"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("=== Security Audit ===");
expect(result.stdout).toContain("Running pnpm audit");
expect(result.stdout).toContain("Scanning for security anti-patterns");
expect(result.stdout).toContain("Checking for outdated packages");
expect(result.stdout).toContain("=== Summary ===");
});

it("exits 0 with --no-fail regardless of issues", () => {
const result = run(["--no-fail"]);
expect(result.exitCode).toBe(0);
});
});

describe("check-security.sh — JSON output", { timeout: 120000 }, () => {
it("returns valid JSON with --json --no-fail", () => {
const result = run(["--json", "--no-fail"]);
expect(result.exitCode).toBe(0);
const parsed = JSON.parse(result.stdout.trim());
expect(parsed).toHaveProperty("status");
expect(parsed).toHaveProperty("auditLevel");
expect(parsed).toHaveProperty("issueCount");
expect(parsed).toHaveProperty("antiPatternCount");
expect(parsed).toHaveProperty("envInGitignore");
expect(parsed).toHaveProperty("hasVulnerabilities");
expect(parsed).toHaveProperty("hasOutdatedPackages");
});

it("auditLevel defaults to moderate", () => {
const result = run(["--json", "--no-fail"]);
const parsed = JSON.parse(result.stdout.trim());
expect(parsed.auditLevel).toBe("moderate");
});
});

describe("check-security.sh — --level flag", { timeout: 120000 }, () => {
it("accepts critical level", () => {
const result = run(["--level", "critical", "--no-fail"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("critical");
});
});
Loading
Loading