mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-15 09:25:28 +02:00
Merge origin/main (v1.64.0.0) into garrytan/time-attack-fork-review
Both waves fixed several of the same bugs; resolutions keep whichever shape this branch's tests pin (#2018 jq bind, #1798 set-- pattern, stop-ack, lock errors, polyfill windowsHide) and take main's richer codex Step 2A (it absorbed the same mktemp fix). True unions: memory- ingest keeps main's capability-probed --include-gitignored inside our GIT_CEILING defense; setup wraps main's Playwright platform override in our stale-healing install lock; package.json takes main's diff@^9 and the combined test glob (design/test + ios-qa/daemon/test, 30s timeout). Generated SKILL.md files regenerated from resolved templates, never hand-picked. Ship goldens refreshed; parity/carve budgets re-measured for the summed preamble growth of both waves (itemized per entry).
This commit is contained in:
@@ -59,6 +59,41 @@ describe("findExecutable", () => {
|
||||
const found = findExecutable("/nonexistent/path/to/nothing");
|
||||
expect(found).toBeNull();
|
||||
});
|
||||
|
||||
// access(X_OK) is TRUE for directories — they carry the execute/traverse bit — so a
|
||||
// bare X_OK test returned ~/.claude/skills/browse, the skill's docs folder, as "the
|
||||
// browse binary". Every browse call then failed with an empty error, which surfaced
|
||||
// as make-pdf reporting "Chromium failed to launch".
|
||||
test("rejects a DIRECTORY even though it passes access(X_OK)", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mkpdf-dir-"));
|
||||
try {
|
||||
// Prove the precondition: the directory really does pass the old test.
|
||||
let passesXok = true;
|
||||
try {
|
||||
fs.accessSync(dir, fs.constants.X_OK);
|
||||
} catch {
|
||||
passesXok = false;
|
||||
}
|
||||
expect(passesXok).toBe(true);
|
||||
|
||||
expect(findExecutable(dir)).toBeNull();
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects a directory that shadows a real binary name", () => {
|
||||
// The exact shape of the bug: a directory named like the thing being looked for.
|
||||
const base = fs.mkdtempSync(path.join(os.tmpdir(), "mkpdf-shadow-"));
|
||||
const shadow = path.join(base, "browse");
|
||||
fs.mkdirSync(shadow);
|
||||
fs.writeFileSync(path.join(shadow, "SKILL.md"), "# not a binary\n");
|
||||
try {
|
||||
expect(findExecutable(shadow)).toBeNull();
|
||||
} finally {
|
||||
fs.rmSync(base, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveBrowseBin", () => {
|
||||
|
||||
@@ -56,6 +56,34 @@ describe("smartypants", () => {
|
||||
expect(out).toContain("\u201cdetails\u201d");
|
||||
});
|
||||
|
||||
// A URL flush against a following tag (no whitespace between) used to let
|
||||
// URL_RE's \S+ swallow the placeholder standing in for that tag. The
|
||||
// placeholder never restored, so the tag vanished and its styling bled
|
||||
// into the rest of the document. The test above misses this because its
|
||||
// URL is followed by a space.
|
||||
test("does not leak placeholders for a bare autolinked URL", () => {
|
||||
const input = `<p>see <a href="https://ex.com">https://ex.com</a> ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a>");
|
||||
expect(out).toBe(input);
|
||||
});
|
||||
|
||||
test("does not leak placeholders for a bold URL", () => {
|
||||
const input = `<p>see <strong><a href="https://ex.com">https://ex.com</a></strong> ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a></strong>");
|
||||
expect(out).toBe(input);
|
||||
});
|
||||
|
||||
test("does not leak placeholders when a URL is followed by punctuation", () => {
|
||||
const input = `<p>see <a href="https://ex.com">https://ex.com</a>, ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a>");
|
||||
});
|
||||
|
||||
test("does NOT touch HTML attribute values", () => {
|
||||
const out = smartypants(`<a href="it's-a-test.html">link</a>`);
|
||||
expect(out).toContain(`href="it's-a-test.html"`);
|
||||
@@ -249,6 +277,48 @@ describe("render (end-to-end)", () => {
|
||||
expect(result.printCss).toContain("Hiragino Kaku Gothic");
|
||||
expect(result.printCss).toContain("Noto Sans CJK");
|
||||
});
|
||||
|
||||
// ─── blank-first-page regression (#1904) ────────────────────────
|
||||
// Any content before the first <h1> used to become a standalone preamble
|
||||
// .chapter. That section takes the `:first-of-type { break-before: auto }`
|
||||
// exception, so the first real chapter inherits `break-before: page` and
|
||||
// starts on page 2 — leaving a blank/near-blank page 1.
|
||||
|
||||
test("a leading <style> preamble does not get its own chapter (no blank page 1)", () => {
|
||||
const result = render({ markdown: `<style>h1{color:#000}</style>\n\n# Hello\n\nBody.\n` });
|
||||
const chapters = result.html.match(/class="chapter"/g) ?? [];
|
||||
// One chapter, not two — the invisible <style> no longer forces a page break.
|
||||
expect(chapters.length).toBe(1);
|
||||
// ...and the style is preserved (folded into the first chapter, not dropped).
|
||||
expect(result.html).toContain("<style>h1{color:#000}</style>");
|
||||
expect(result.html).toMatch(/<section class="chapter"><style>[\s\S]*?<\/style>[\s\S]*?<h1/);
|
||||
});
|
||||
|
||||
test("leading YAML frontmatter is stripped, not rendered as body text", () => {
|
||||
const result = render({
|
||||
markdown: `---\ntitle: My Doc\ntype: memo\ntags: [a, b]\n---\n\n# Hello\n\nBody.\n`,
|
||||
});
|
||||
// Frontmatter keys must not leak into the rendered body.
|
||||
expect(result.html).not.toContain("type: memo");
|
||||
expect(result.html).not.toContain("tags: [a, b]");
|
||||
// No preamble chapter -> single chapter, first H1 on page 1.
|
||||
const chapters = result.html.match(/class="chapter"/g) ?? [];
|
||||
expect(chapters.length).toBe(1);
|
||||
expect(result.meta.title).toBe("Hello");
|
||||
});
|
||||
|
||||
test("a real text preamble still gets its own chapter (unchanged behavior)", () => {
|
||||
const result = render({ markdown: `Intro paragraph.\n\n# Hello\n\nBody.\n` });
|
||||
const chapters = result.html.match(/class="chapter"/g) ?? [];
|
||||
expect(chapters.length).toBe(2);
|
||||
});
|
||||
|
||||
test("a `---` thematic break that is not frontmatter is left intact", () => {
|
||||
// Opening with visible text means the later `---` is a real <hr>, not frontmatter.
|
||||
const result = render({ markdown: `# Hello\n\nBefore.\n\n---\n\nAfter.\n` });
|
||||
expect(result.html).toContain("<hr>");
|
||||
expect(result.html).toContain("After.");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── print-css ──────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user