From f91c14749e7f5f7baac0bdb5b74bad79581ec634 Mon Sep 17 00:00:00 2001 From: Wen Date: Wed, 14 Oct 2026 14:04:26 -0300 Subject: [PATCH] fix(frontend-slides): make relative PDF output paths absolute before export The "resolve output to absolute path" step only rebuilt "$OUTPUT_DIR/$(basename ...)", so ./slides.pdf stayed relative. The script then cd's into a temp dir, Playwright writes the PDF there, and the temp dir is deleted. The script prints "PDF exported successfully!" and then fails on du under set -e, with no PDF produced. This is the script's own documented example. Resolve the output directory with cd + pwd, as the input path already does, and add a stubbed regression test (no network or browser). Signed-off-by: Wen --- manifests/context-packs/skill-triggers@1.json | 2 +- skills/frontend-slides/scripts/export-pdf.sh | 4 +- .../skills/frontend-slides-export-pdf.test.js | 77 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 tests/skills/frontend-slides-export-pdf.test.js diff --git a/manifests/context-packs/skill-triggers@1.json b/manifests/context-packs/skill-triggers@1.json index a49a8c824..9a2d7380b 100644 --- a/manifests/context-packs/skill-triggers@1.json +++ b/manifests/context-packs/skill-triggers@1.json @@ -10,7 +10,7 @@ "id": "hand-seeded", "source": "manual-curation-pending-regeneration" }, - "registryDigest": "498caee2738006d64530e9be34883111cf67742d8c4389f39d61299e1a1c4400", + "registryDigest": "b133a28198126c63f6c9a0b10688c0144cac10dd9a1b2305f4fd1e88187888e5", "schemaVersion": 1, "triggers": { "skill:agent-runtime-gateway-smoke-test": [ diff --git a/skills/frontend-slides/scripts/export-pdf.sh b/skills/frontend-slides/scripts/export-pdf.sh index 2ab289f89..717a0900e 100755 --- a/skills/frontend-slides/scripts/export-pdf.sh +++ b/skills/frontend-slides/scripts/export-pdf.sh @@ -82,10 +82,10 @@ else OUTPUT_PDF="$(dirname "$INPUT_HTML")/$(basename "$INPUT_HTML" .html).pdf" fi -# Resolve output to absolute path +# Resolve output to absolute path (the export later runs from a temp dir that is deleted) OUTPUT_DIR=$(dirname "$OUTPUT_PDF") mkdir -p "$OUTPUT_DIR" -OUTPUT_PDF="$OUTPUT_DIR/$(basename "$OUTPUT_PDF")" +OUTPUT_PDF="$(cd "$OUTPUT_DIR" && pwd)/$(basename "$OUTPUT_PDF")" echo "" echo -e "${BOLD}========================================${NC}" diff --git a/tests/skills/frontend-slides-export-pdf.test.js b/tests/skills/frontend-slides-export-pdf.test.js new file mode 100644 index 000000000..e38490315 --- /dev/null +++ b/tests/skills/frontend-slides-export-pdf.test.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node +/** + * Stubbed run of skills/frontend-slides/scripts/export-pdf.sh: npm, npx and + * node are replaced by stubs, so no network or browser is needed. + * + * Run with: node tests/skills/frontend-slides-export-pdf.test.js + */ + +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +const SCRIPT = path.join(__dirname, '..', '..', 'skills', 'frontend-slides', 'scripts', 'export-pdf.sh'); + +function test(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + return true; + } catch (err) { + console.log(` ✗ ${name}`); + console.log(` Error: ${err.message}`); + return false; + } +} + +function runTests() { + console.log('\n=== Testing frontend-slides export-pdf.sh ===\n'); + + if (process.platform === 'win32') { + console.log(' - skipped: bash script test on Windows'); + console.log('\nPassed: 0'); + console.log('Failed: 0'); + return; + } + + let passed = 0; + let failed = 0; + + if (test('writes a relative output path next to the caller, not into the deleted temp dir', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'slides-export-')); + try { + const bin = path.join(root, 'bin'); + const work = path.join(root, 'work'); + fs.mkdirSync(bin); + fs.mkdirSync(work); + // npm/npx succeed without doing anything; open/xdg-open must not launch a viewer. + for (const name of ['npm', 'npx', 'open', 'xdg-open']) { + fs.writeFileSync(path.join(bin, name), '#!/bin/sh\nexit 0\n', { mode: 0o755 }); + } + // node stub writes the PDF to the output path argument, resolved against its cwd. + fs.writeFileSync(path.join(bin, 'node'), '#!/bin/sh\nprintf "%%PDF-stub" > "$4"\n', { mode: 0o755 }); + fs.writeFileSync(path.join(work, 'presentation.html'), '\n'); + + const result = spawnSync('bash', [SCRIPT, './presentation.html', './slides.pdf'], { + cwd: work, + encoding: 'utf8', + env: { ...process.env, PATH: `${bin}${path.delimiter}${process.env.PATH}` }, + }); + + assert.strictEqual(result.status, 0, result.stdout + result.stderr); + assert.ok(fs.existsSync(path.join(work, 'slides.pdf')), 'slides.pdf was not written to the working directory'); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } + })) passed++; else failed++; + + console.log(`\nPassed: ${passed}`); + console.log(`Failed: ${failed}`); + process.exitCode = failed > 0 ? 1 : 0; +} + +runTests();