diff --git a/src/components/engines/EnginesPage.tsx b/src/components/engines/EnginesPage.tsx index b4a1c41..f176748 100644 --- a/src/components/engines/EnginesPage.tsx +++ b/src/components/engines/EnginesPage.tsx @@ -44,6 +44,7 @@ import { commands } from "@/bindings"; import { Route } from "@/routes/engines"; import { enginesAtom } from "@/state/atoms"; import { + duplicateEngine, type Engine, engineSchema, type LocalEngine, @@ -566,11 +567,7 @@ function EngineSettings({ leftSection={} variant="default" onClick={() => { - const duplicatedEngine: LocalEngine = { - ...engine, - id: crypto.randomUUID(), - name: `${engine.name} (Copy)`, - }; + const duplicatedEngine = duplicateEngine(engine); setEngines(async (prev) => { const copy = [...(await prev)]; copy.splice(selected + 1, 0, duplicatedEngine); diff --git a/src/utils/engines.ts b/src/utils/engines.ts index 30f3ae5..5c010d5 100644 --- a/src/utils/engines.ts +++ b/src/utils/engines.ts @@ -69,6 +69,14 @@ export type RemoteEngine = z.output; export const engineSchema = z.union([localEngineSchema, remoteEngineSchema]); export type Engine = z.output; +export function duplicateEngine(engine: LocalEngine): LocalEngine { + return { + ...structuredClone(engine), + id: crypto.randomUUID(), + name: `${engine.name} (Copy)`, + }; +} + export function stopEngine(engine: LocalEngine, tab: string): Promise { return commands.stopEngine(engine.id, tab).then((r) => { unwrap(r); diff --git a/src/utils/tests/engines.test.ts b/src/utils/tests/engines.test.ts new file mode 100644 index 0000000..0ad3e46 --- /dev/null +++ b/src/utils/tests/engines.test.ts @@ -0,0 +1,49 @@ +import { expect, test } from "vitest"; +import { duplicateEngine, type LocalEngine } from "../engines"; + +const stockfish: () => LocalEngine = () => ({ + type: "local", + id: "stockfish", + name: "Stockfish", + version: "17", + path: "/usr/bin/stockfish", + go: { t: "Depth", c: 20 }, + settings: [ + { name: "MultiPV", value: 1 }, + { name: "Threads", value: 4 }, + { name: "Hash", value: 16 }, + ], +}); + +test("duplicateEngine gives the copy a new id and name", () => { + const original = stockfish(); + const copy = duplicateEngine(original); + + expect(copy.id).not.toBe(original.id); + expect(copy.name).toBe("Stockfish (Copy)"); + expect(copy.settings).toEqual(original.settings); + expect(copy.go).toEqual(original.go); +}); + +test("duplicateEngine does not share settings with the original", () => { + const original = stockfish(); + const copy = duplicateEngine(original); + + copy.settings!.find((s) => s.name === "Threads")!.value = 8; + copy.settings!.push({ name: "Ponder", value: true }); + + expect(original.settings).toEqual([ + { name: "MultiPV", value: 1 }, + { name: "Threads", value: 4 }, + { name: "Hash", value: 16 }, + ]); +}); + +test("duplicateEngine does not share the go mode with the original", () => { + const original = stockfish(); + const copy = duplicateEngine(original); + + (copy.go as { c: number }).c = 30; + + expect(original.go).toEqual({ t: "Depth", c: 20 }); +});