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
51 changes: 49 additions & 2 deletions scripts/coverage-floor.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,23 @@ export function cliEntrySpans(src) {
// top-level body exactly there, and counting braces would trip over the `{`
// inside the string and regex literals the checkers are made of. A one-line
// declaration has no span (it would otherwise run on to the next body).
// Also matches top-level const arrow-function helpers:
// `const name = (...) => {` / `const name = async (...) => {` /
// `const name = function(...) {` / `const name = async function(...) {`.
// An assigned function closes with `};`, so the const form also accepts a
// `;` right after the brace; the span ends at the brace either way.
export function functionSpans(src) {
const spans = [];
for (const m of src.matchAll(/^(?:export )?(?:async )?function\b[^\n]*\{$/gm)) {
// Match both function declarations and const arrow-function/function-assigned helpers
for (const m of src.matchAll(/^(?:export )?(?:(?:async )?function\b|const\s+\w+\s*=\s*(?:async\s+)?(?:\(|function\b))[^\n]*\{$/gm)) {
const close = src.indexOf("\n}", m.index);
if (close < 0) break;
const isConst = /^(?:export )?const\b/.test(m[0]);
const after = src[close + 2];
if (after !== undefined && after !== "\n") continue;
if (after === ";" && isConst) {
const next = src[close + 3];
if (next !== undefined && next !== "\n") continue;
} else if (after !== undefined && after !== "\n") continue;
spans.push([m.index, close + 2]);
}
return spans;
Expand Down Expand Up @@ -300,6 +310,43 @@ test("fixture: a range zero in one run but inherited as executed in another is n
assert.equal(countOf([], 0, 1), undefined);
});

test("fixture: functionSpans recognizes top-level arrow-const and function-assigned const helpers", () => {
const arrow = "const siteHas = (path) => {\n return path === '';\n}\n";
const asyncArrow = "const check = async (x) => {\n return x > 0;\n}\n";
const funcAssigned = "const helper = function(a) {\n return a ? 'yes' : 'no';\n}\n";
const asyncFuncAssigned = "const asyncHelper = async function(b) {\n return b;\n}\n";
const exported = "export const f = (x) => {\n return x * 2;\n}\n";
const mixed = "import x from 'y';\n\nconst a = (p) => {\n if (p) { return 1; }\n return 0;\n}\n\nexport function b() {\n return 2;\n}\n\nconst c = async (q) => {\n return q || null;\n}\n";

const arrowSpans = functionSpans(arrow);
assert.equal(arrowSpans.length, 1, "arrow const should have one span");
assert.equal(arrow.slice(...arrowSpans[0]), arrow.trimEnd(), "arrow const span should cover the entire function up to its closing brace");

const asyncArrowSpans = functionSpans(asyncArrow);
assert.equal(asyncArrowSpans.length, 1, "async arrow const should have one span");

const funcSpans = functionSpans(funcAssigned);
assert.equal(funcSpans.length, 1, "function-assigned const should have one span");

const asyncFuncSpans = functionSpans(asyncFuncAssigned);
assert.equal(asyncFuncSpans.length, 1, "async function-assigned const should have one span");

const exportedSpans = functionSpans(exported);
assert.equal(exportedSpans.length, 1, "exported arrow const should have one span");

const mixedSpans = functionSpans(mixed);
assert.equal(mixedSpans.length, 3, "mixed file should have three spans (a, b, c)");

const semi = "const siteHas = (path) => {\n if (path === '') return true;\n return false;\n};\n\nexport const g = function(x) {\n return x;\n};\n";
const semiSpans = functionSpans(semi);
assert.equal(semiSpans.length, 2, "arrow/function consts closing with `};` should each have one span");
assert.equal(semi.slice(...semiSpans[0]), "const siteHas = (path) => {\n if (path === '') return true;\n return false;\n}", "the `};` span ends at the closing brace");
assert.equal(semi.slice(...semiSpans[1]), "export const g = function(x) {\n return x;\n}");
assert.deepEqual(functionSpans("const h = (x) => {\n return x;\n}; h(1);\n"), [], "a `};` followed by more code on the line is not a top-level close");
assert.deepEqual(functionSpans("function d() {\n return 1;\n};\n"), [], "a declaration followed by `;` keeps the old rejection");
});


test("fixture: problems name file, line and the first line of the dead source, sorted by file then offset", () => {
const src = "export function f(x) {\n return x\n ? 1\n : 2;\n}\n";
const runs = [[{ functionName: "f", ranges: [{ startOffset: 0, endOffset: src.length, count: 1 }, { startOffset: src.indexOf(": 2"), endOffset: src.indexOf(";"), count: 0 }, { startOffset: src.indexOf("? 1"), endOffset: src.indexOf("\n :"), count: 0 }], isBlockCoverage: true }]];
Expand Down
7 changes: 7 additions & 0 deletions scripts/issue-forms.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ test("every issue form parses and is sound", () => {
for (const f of forms) assert.deepEqual(formProblems(parseYaml(readFileSync(join(DIR, f), "utf8"))), [], f);
});

test("fixture: siteHas resolves the root, a bare page, a plain file, and nothing else", () => {
assert.equal(siteHas(""), true);
assert.equal(siteHas("404"), true);
assert.equal(siteHas("robots.txt"), true);
assert.equal(siteHas("no-such-page/"), false);
});

test("fixtures: each rule rejects a bad input", () => {
const ok = () => true;
assert.ok(configProblems({ blank_issues_enabled: "yes", contact_links: [] }, ok).length);
Expand Down
Loading