Files
jhonny-editor/bm/premiere-pro-mcp-main/tests/tools/recovery.test.ts
T
João Henrique b541f502ba feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
2026-09-08 09:59:31 -04:00

162 lines
5.7 KiB
TypeScript
Executable File

import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
utimesSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../../src/bridge/file-bridge.js", async (importOriginal) => {
const actual =
await importOriginal<typeof import("../../src/bridge/file-bridge.js")>();
return { ...actual, sendCommand: vi.fn() };
});
import { sendCommand } from "../../src/bridge/file-bridge.js";
import {
collectBridgeTelemetry,
discoverAdjacentRecoveryCandidates,
getRecoveryTools,
} from "../../src/tools/recovery.js";
const mockedSendCommand = vi.mocked(sendCommand);
const temporaryDirectories: string[] = [];
function temporaryDirectory() {
const directory = mkdtempSync(join(tmpdir(), "premiere-recovery-test-"));
temporaryDirectories.push(directory);
return directory;
}
afterEach(() => {
for (const directory of temporaryDirectories.splice(0)) {
rmSync(directory, { recursive: true, force: true });
}
});
describe("recovery candidate discovery", () => {
it("finds matching adjacent autosaves newest-first without modifying them", () => {
const directory = temporaryDirectory();
const project = join(directory, "Episode 01.prproj");
writeFileSync(project, "project");
const autosaveDirectory = join(directory, "Adobe Premiere Pro Auto-Save");
mkdirSync(autosaveDirectory);
const older = join(autosaveDirectory, "Episode 01-1.prproj");
const newer = join(autosaveDirectory, "Episode 01-2.prproj");
const unrelated = join(autosaveDirectory, "Other Project.prproj");
writeFileSync(older, "old");
writeFileSync(newer, "new");
writeFileSync(unrelated, "unrelated");
utimesSync(project, new Date(1_000), new Date(1_000));
utimesSync(older, new Date(2_000), new Date(2_000));
utimesSync(newer, new Date(3_000), new Date(3_000));
const candidates = discoverAdjacentRecoveryCandidates(project);
expect(candidates.map((item) => item.fileName)).toEqual([
"Episode 01-2.prproj",
"Episode 01-1.prproj",
]);
expect(candidates.every((item) => item.newerThanProjectFile)).toBe(true);
expect(readFileSync(newer, "utf8")).toBe("new");
});
it("does not scan when no saved Premiere project path exists", () => {
expect(discoverAdjacentRecoveryCandidates("")).toEqual([]);
expect(discoverAdjacentRecoveryCandidates("/tmp/not-a-project.txt")).toEqual([]);
});
});
describe("privacy-preserving bridge telemetry", () => {
it("does not report a quiet directory as healthy when no CEP heartbeat is present", () => {
const directory = temporaryDirectory();
expect(collectBridgeTelemetry({ tempDir: directory })).toMatchObject({
directoryAccessible: true,
heartbeat: { state: "unknown" },
healthy: false,
});
});
it("returns aggregate state without paths, filenames, or contents", () => {
const directory = temporaryDirectory();
writeFileSync(join(directory, "cmd_secret.jsx"), "private project script");
writeFileSync(join(directory, "res_secret.json"), '{"project":"Private"}');
writeFileSync(join(directory, "busy_secret.json"), "{}");
writeFileSync(join(directory, "bridge-heartbeat.json"), JSON.stringify({
protocolVersion: 1,
state: "running",
}));
writeFileSync(join(directory, "helpers_ignored.jsx"), "helper");
const telemetry = collectBridgeTelemetry(
{ tempDir: directory },
Date.now() + 100,
);
expect(telemetry).toMatchObject({
directoryAccessible: true,
pendingCommands: 1,
pendingResponses: 1,
busyOperations: 1,
heartbeat: { state: "running" },
healthy: false,
});
const serialized = JSON.stringify(telemetry);
expect(serialized).not.toContain(directory);
expect(serialized).not.toContain("secret");
expect(serialized).not.toContain("Private");
});
});
describe("recovery tools", () => {
beforeEach(() => vi.clearAllMocks());
it("reports the unavailable dirty flag and never emits restore code", async () => {
const directory = temporaryDirectory();
const project = join(directory, "Documentary.prproj");
writeFileSync(project, "project");
mockedSendCommand.mockResolvedValue({
success: true,
data: { name: "Documentary", path: project },
});
const tools = getRecoveryTools({ tempDir: join(directory, "bridge") });
const result = await tools.inspect_project_recovery.handler();
expect(result.success).toBe(true);
expect((result.data as any).unsavedChanges).toMatchObject({
status: "not_exposed",
dirty: null,
});
expect((result.data as any).recovery.automaticRestoreSupported).toBe(false);
const script = mockedSendCommand.mock.calls[0][0];
expect(script).not.toContain("openDocument");
expect(script).not.toContain("openProject");
expect(script).not.toContain(".save");
});
});
describe("bridge panel accessibility", () => {
it("uses semantic live regions, labels, keyboard focus, and forced-colors CSS", () => {
const cepHtml = readFileSync(
new URL("../../cep-plugin/index.html", import.meta.url),
"utf8",
);
const cepCss = readFileSync(
new URL("../../cep-plugin/styles.css", import.meta.url),
"utf8",
);
const uxpHtml = readFileSync(
new URL("../../uxp-plugin/index.html", import.meta.url),
"utf8",
);
expect(cepHtml).toContain('role="status"');
expect(cepHtml).toContain('aria-label="Bridge activity log"');
expect(cepHtml).toContain('tabindex="0"');
expect(cepCss).toContain("@media (forced-colors: active)");
expect(cepCss).toContain("focus-visible");
expect(uxpHtml).toContain('role="status"');
expect(uxpHtml).toContain('lang="en"');
});
});