82 lines
4.5 KiB
TypeScript
82 lines
4.5 KiB
TypeScript
import { createRoot } from "react-dom/client";
|
|
import { App } from "../../src/renderer/App";
|
|
import type { ElectronApi } from "../../src/shared/preload-api";
|
|
import type { CollectorPersistenceState } from "../../src/shared/collector";
|
|
import { createVisualFixture } from "./fixtures";
|
|
import { createVisualElectronApi } from "./mock-electron-api";
|
|
import "../../src/renderer/theme.css";
|
|
import "../../src/renderer/styles.css";
|
|
|
|
const fixture = createVisualFixture("dense");
|
|
fixture.snapshot.settings.switchToCollectorOnClipboard = false;
|
|
let clipboardListener: ((links: string[]) => void) | undefined;
|
|
const stateListeners = new Set<Parameters<ElectronApi["onStateUpdate"]>[0]>();
|
|
let saved: CollectorPersistenceState = { packages: [], collapsedPackageIds: [] };
|
|
let preparedCount = 0;
|
|
let releasePreparation: (() => void) | undefined;
|
|
let delayPreparation = false;
|
|
window.rd = {
|
|
...createVisualElectronApi(fixture),
|
|
onClipboardDetected: (listener) => { clipboardListener = listener; return () => { clipboardListener = undefined; }; },
|
|
onStateUpdate: (listener) => { stateListeners.add(listener); return () => { stateListeners.delete(listener); }; },
|
|
updateSettings: async (settings) => {
|
|
Object.assign(fixture.snapshot.settings, settings);
|
|
fixture.snapshot.snapshotRevision = (fixture.snapshot.snapshotRevision ?? 0) + 1;
|
|
for (const listener of stateListeners) listener(structuredClone(fixture.snapshot));
|
|
return structuredClone(fixture.snapshot.settings);
|
|
},
|
|
prepareCollectorText: async ({ rawText, addedAt }) => {
|
|
const id = `navigation-${++preparedCount}`;
|
|
if (delayPreparation) await new Promise<void>((resolve) => { releasePreparation = resolve; });
|
|
return {
|
|
packages: [{ id, name: id, nameSource: "explicit", addedAt, links: [{ id: `${id}-link`, url: rawText, fileName: `${id}.rar`, fileSizeBytes: null, hoster: "example.test", availability: "unknown", status: "unknown", addedAt }] }],
|
|
invalidCount: 0, duplicateCount: 0
|
|
};
|
|
},
|
|
saveCollectorState: async (state) => { saved = structuredClone(state); return structuredClone(state); }
|
|
};
|
|
|
|
const report = document.createElement("output");
|
|
document.body.append(report);
|
|
createRoot(document.getElementById("root")!).render(<App />);
|
|
const waitFor = async (condition: () => boolean): Promise<void> => {
|
|
const deadline = Date.now() + 10000;
|
|
while (!condition()) {
|
|
if (Date.now() > deadline) throw new Error("Navigation check timed out");
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
}
|
|
};
|
|
const activeView = (): string | undefined => document.querySelector<HTMLElement>("[data-visual-active-view]")?.dataset.visualActiveView;
|
|
const navigate = async (view: string): Promise<void> => {
|
|
document.querySelector<HTMLButtonElement>(`[data-main-view="${view}"]`)!.click();
|
|
await waitFor(() => activeView() === view);
|
|
};
|
|
const check = (condition: boolean, message: string): void => { if (!condition) throw new Error(message); };
|
|
|
|
async function verify(): Promise<void> {
|
|
await waitFor(() => Boolean(clipboardListener) && activeView() === "downloads");
|
|
clipboardListener!(["https://example.test/background.rar"]);
|
|
await waitFor(() => saved.packages.length === 1);
|
|
check(activeView() === "downloads", "Default changed the downloads tab");
|
|
await navigate("settings");
|
|
clipboardListener!(["https://example.test/settings.rar"]);
|
|
await waitFor(() => saved.packages.length === 2);
|
|
check(activeView() === "settings", "Background import interrupted settings");
|
|
await window.rd.updateSettings({ switchToCollectorOnClipboard: true });
|
|
await new Promise((resolve) => setTimeout(resolve, 900));
|
|
clipboardListener!(["https://example.test/enabled.rar"]);
|
|
await waitFor(() => saved.packages.length === 3 && activeView() === "collector");
|
|
await navigate("downloads");
|
|
delayPreparation = true;
|
|
clipboardListener!(["https://example.test/disabled-during-import.rar"]);
|
|
await waitFor(() => Boolean(releasePreparation));
|
|
await window.rd.updateSettings({ switchToCollectorOnClipboard: false });
|
|
await new Promise((resolve) => setTimeout(resolve, 900));
|
|
releasePreparation!();
|
|
await waitFor(() => saved.packages.length === 4);
|
|
check(activeView() === "downloads", "An in-flight import ignored disabling the setting");
|
|
report.textContent = JSON.stringify({ passed: true, checks: ["default keeps downloads", "background preserves settings", "opt-in opens collector", "opt-out during import prevents switch"], savedPackages: saved.packages.length });
|
|
}
|
|
|
|
void verify().catch((error) => { report.textContent = String(error); });
|