eharris128_Obsidian-LLM-Plugin/scripts/stage-plugin.mjs
Evan Harris edd21015c2 Add local E2E suite driving real sandboxed Obsidian (wdio-obsidian-service)
npm run test:e2e builds the plugin, type-checks the specs, stages a clean
copy into test/plugin-dist/, and runs 13 Mocha specs against a real,
sandboxed Obsidian instance (downloaded to .obsidian-cache/ on first run;
~6s wall-clock after that). No API keys or network providers needed.

Coverage: plugin load + command registration + built-in skill seeding,
widget open/focus-or-open/new-chat-always-fresh/per-tab input isolation,
slash menu open/Escape/filtering, chat modal, chats panel, chat details
panel, and the header settings toggle.

Design notes (details in test/README.md):
- The service copies data.json from the plugin dir into test vaults, so
  plugins: ["."] would leak the developer's real API keys. The staging
  script writes a deterministic test data.json instead (rootVaultFolder
  "AI" so built-in skills seed; nothing else).
- Specs read the plugin id from the staged manifest at runtime rather
  than hardcoding it.
- Specs live under test/ with their own tsconfig (root tsconfig now
  excludes test/); OBSIDIAN_APP_VERSION / OBSIDIAN_INSTALLER_VERSION pin
  the Obsidian version under test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 14:16:18 -05:00

42 lines
1.8 KiB
JavaScript

/**
* Stage the built plugin into test/plugin-dist/ for the E2E suite.
*
* wdio-obsidian-service installs a plugin by copying manifest.json, main.js,
* styles.css AND data.json (if present) from the plugin directory into the
* sandboxed test vault. Pointing it at the repo root would therefore copy the
* developer's real data.json (API keys, personal settings) into every test
* vault. Instead we stage a clean copy here with a deterministic test
* data.json so runs are reproducible and never touch real credentials.
*
* Run `npm run build` first — this script copies the built main.js.
*/
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
import * as path from "path";
import { fileURLToPath } from "url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const stageDir = path.join(repoRoot, "test", "plugin-dist");
mkdirSync(stageDir, { recursive: true });
for (const file of ["manifest.json", "main.js", "styles.css"]) {
const src = path.join(repoRoot, file);
if (!existsSync(src)) {
console.error(`stage-plugin: missing ${file} — run \`npm run build\` first.`);
process.exit(1);
}
copyFileSync(src, path.join(stageDir, file));
}
// Minimal deterministic settings. loadSettings() deep-merges with
// DEFAULT_SETTINGS, so only overrides go here. rootVaultFolder enables the
// Skills/Projects/Assistants subsystems and seeds built-in skills, which the
// slash-menu spec relies on. No API keys — provider calls are out of scope
// for the smoke suite.
const testSettings = {
rootVaultFolder: "AI",
hasOnboarded: true,
};
writeFileSync(path.join(stageDir, "data.json"), JSON.stringify(testSettings, null, 2) + "\n");
console.log(`stage-plugin: staged plugin into ${path.relative(repoRoot, stageDir)}`);