mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Editing a file from the library used to silently supersede it: the worker auto-saved every result as a new version and the leaf-only listing hid the original, which read as a destructive overwrite. Tool pages now show a per-edit choice for library-sourced files. The default saves the result as an independent new file and keeps the original; picking overwrite keeps the old superseding-version behavior. The client sends a saveMode multipart field next to fileId, validated with a 400 on unknown values, and autoSaveToLibrary branches on it. Every hand-written route that honors fileId parses the field the same way as the factory. The review panel shows where an auto-saved result went instead of offering a second, duplicate save. Tools whose route or submitter ignores fileId keep the selector hidden via a shared unsupported-tools set, and the choice resets to the non-destructive default whenever a new file is staged. Closes #495
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const revokeObjectURL = vi.fn();
|
|
const createObjectURL = vi.fn((_obj: Blob | MediaSource) => "blob:fake-url");
|
|
|
|
vi.stubGlobal("URL", {
|
|
...globalThis.URL,
|
|
createObjectURL,
|
|
revokeObjectURL,
|
|
});
|
|
|
|
const imagePreviewMock = vi.hoisted(() => ({
|
|
needsServerPreview: vi.fn(() => false),
|
|
fetchDecodedPreview: vi.fn(() => Promise.resolve(null)),
|
|
}));
|
|
|
|
vi.mock("@/lib/image-preview", () => imagePreviewMock);
|
|
|
|
vi.mock("@/lib/analytics", () => ({
|
|
track: vi.fn(),
|
|
}));
|
|
|
|
import { useFileStore } from "@/stores/file-store";
|
|
|
|
function makeFile(name: string, size = 1024, type = "image/png"): File {
|
|
const buf = new ArrayBuffer(size);
|
|
return new File([buf], name, { type });
|
|
}
|
|
|
|
describe("useFileStore library save mode (issue #495)", () => {
|
|
beforeEach(() => {
|
|
useFileStore.getState().reset();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("defaults librarySaveMode to non-destructive 'new'", () => {
|
|
expect(useFileStore.getState().librarySaveMode).toBe("new");
|
|
});
|
|
|
|
it("setLibrarySaveMode switches the mode", () => {
|
|
useFileStore.getState().setLibrarySaveMode("overwrite");
|
|
expect(useFileStore.getState().librarySaveMode).toBe("overwrite");
|
|
});
|
|
|
|
it("reset restores the 'new' default and clears the last saved file id", () => {
|
|
useFileStore.getState().setLibrarySaveMode("overwrite");
|
|
useFileStore.getState().setLastSavedLibraryFileId("lib-1");
|
|
|
|
useFileStore.getState().reset();
|
|
|
|
expect(useFileStore.getState().librarySaveMode).toBe("new");
|
|
expect(useFileStore.getState().lastSavedLibraryFileId).toBeNull();
|
|
});
|
|
|
|
it("undoProcessing keeps the chosen mode but clears the last saved file id", () => {
|
|
useFileStore.getState().setFiles([makeFile("a.png")]);
|
|
useFileStore.getState().setLibrarySaveMode("overwrite");
|
|
useFileStore.getState().setLastSavedLibraryFileId("lib-2");
|
|
|
|
useFileStore.getState().undoProcessing();
|
|
|
|
expect(useFileStore.getState().librarySaveMode).toBe("overwrite");
|
|
expect(useFileStore.getState().lastSavedLibraryFileId).toBeNull();
|
|
});
|
|
|
|
it("staging a new file set restores the non-destructive default", () => {
|
|
useFileStore.getState().setFiles([makeFile("a.png")]);
|
|
useFileStore.getState().setLibrarySaveMode("overwrite");
|
|
|
|
// A later library import stages a different file; the overwrite choice
|
|
// made for the previous file must not carry over (#495 review finding).
|
|
useFileStore.getState().setFiles([makeFile("b.png")]);
|
|
|
|
expect(useFileStore.getState().librarySaveMode).toBe("new");
|
|
});
|
|
|
|
it("tracks the last saved library file id", () => {
|
|
expect(useFileStore.getState().lastSavedLibraryFileId).toBeNull();
|
|
useFileStore.getState().setLastSavedLibraryFileId("lib-3");
|
|
expect(useFileStore.getState().lastSavedLibraryFileId).toBe("lib-3");
|
|
});
|
|
});
|