feat(progress): keep a check-in goal on the reader's progress record
Goals ride the existing record, so export and the later-edit-wins import merge carry them with no new key. Setting one clears a contradicting "not daily"; saying "not daily" clears the goal, so no conflict hides in storage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9e826f22e4
commit
34c7dce5ca
+47
-1
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { buildExportData, parseImportData } from "../src/client/state/export.ts";
|
||||
import { restorePrefsValue } from "../src/client/state/usePrefs.ts";
|
||||
import { mergeProgress } from "../src/client/state/useProgress.ts";
|
||||
import { applyPatch, goalPatch, mergeProgress } from "../src/client/state/useProgress.ts";
|
||||
import type { ProgressMap } from "../src/client/state/useProgress.ts";
|
||||
|
||||
/**
|
||||
@@ -175,3 +175,49 @@ describe("parseImportData", () => {
|
||||
});
|
||||
|
||||
|
||||
describe("goals on progress", () => {
|
||||
const goal = { period: "week", measure: "days", required: 5, weekStartsOn: 1 } as const;
|
||||
const T = "2026-10-13T12:00:00.000Z";
|
||||
|
||||
test("a goal-only record is kept", () => {
|
||||
expect(applyPatch({}, "e", { goal }, T).e).toEqual({ goal, at: T });
|
||||
});
|
||||
|
||||
test("clearing the only field drops the record", () => {
|
||||
expect(applyPatch({ e: { goal, at: T } }, "e", { goal: undefined }, T).e).toBeUndefined();
|
||||
});
|
||||
|
||||
test("setting a goal clears a stored 'not daily', and nothing else", () => {
|
||||
expect(goalPatch({ daily: false, at: T }, goal)).toStrictEqual({ goal, daily: undefined });
|
||||
expect(goalPatch({ daily: true, at: T }, goal)).toStrictEqual({ goal });
|
||||
expect(goalPatch(undefined, undefined)).toStrictEqual({ goal: undefined });
|
||||
});
|
||||
|
||||
test("an unreadable stored goal is left exactly as it was", () => {
|
||||
// A newer shape, as an older cached bundle would meet it.
|
||||
const future = { period: "week", measure: "amount", target: 2100, weekStartsOn: 1 };
|
||||
const next = applyPatch({ e: { goal: future, at: T } }, "e", { note: "hi" }, T);
|
||||
expect(next.e?.goal).toEqual(future);
|
||||
});
|
||||
|
||||
test("goals survive export and import, and the later record wins", () => {
|
||||
const device: ProgressMap = { e: { status: "doing", at: at("10") } };
|
||||
const file = parseImportData(
|
||||
JSON.parse(
|
||||
JSON.stringify(
|
||||
buildExportData({ e: { goal, at: at("11") } }, {}, {}, { games: {}, events: {} }),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(mergeProgress(device, file!.progress!).e).toEqual({ goal, at: at("11") });
|
||||
});
|
||||
|
||||
test("an export from before goals imports unchanged", () => {
|
||||
const old = {
|
||||
format: "gacha-tracker-export",
|
||||
version: 1,
|
||||
progress: { e: { status: "done", at: at("10") } },
|
||||
};
|
||||
expect(parseImportData(old)!.progress).toEqual({ e: { status: "done", at: at("10") } });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user