Files
SnapOtter/tests/unit/web/no-native-error-throws.test.ts
T

41 lines
1.7 KiB
TypeScript
Raw Normal View History

import { readdirSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
// The web Sentry scrubber treats native error classes (TypeError, RangeError,
// and friends) as browser/runtime faults: their messages pass through with
// redaction instead of being replaced wholesale. That is only safe while our
// own code never throws those classes, so own-code throws must stay plain
// Error (or an app-specific subclass). This turns that grep into an invariant.
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../..");
const TREES = ["apps/web/src", "packages/shared/src"];
const NATIVE_THROW =
/throw\s+new\s+(TypeError|RangeError|SyntaxError|ReferenceError|DOMException)\s*\(/;
function sourceFiles(tree: string): string[] {
return readdirSync(join(ROOT, tree), { recursive: true })
.map(String)
.filter((p) => (p.endsWith(".ts") || p.endsWith(".tsx")) && !p.includes(".test."))
.map((p) => join(tree, p));
}
describe("no native error-class throws in web/shared source", () => {
const files = TREES.flatMap(sourceFiles);
it("finds source files", () => {
expect(files.length).toBeGreaterThan(100);
});
it("own code throws plain Error, never native error classes", () => {
const violations: string[] = [];
for (const file of files) {
const lines = readFileSync(join(ROOT, file), "utf-8").split("\n");
lines.forEach((line, i) => {
if (NATIVE_THROW.test(line)) violations.push(`${file}:${i + 1}`);
});
}
expect(violations, "use plain Error so the Sentry scrubber fully redacts").toEqual([]);
});
});