Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions site/src/play/PlayApp.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,43 @@ describe("PlayApp cabinet presentation", () => {
}
});

it("resumes an existing local save when opened via its permanent ?campaign= link, rather than restarting", async () => {
const user = userEvent.setup();
const { unmount } = render(<PlayApp />);
await screen.findByRole("heading", { name: "Adventure disk library" });

await user.click(screen.getByRole("button", { name: /The Bureaucracy/i }));
await user.click(
screen.getByRole("button", { name: "Load selected adventure" }),
);
await user.click(
await screen.findByRole("button", {
name: /Wait for the municipal registry/i,
}),
);
const advancedScene =
document.querySelector(".scene-body")?.textContent ?? "";
expect(advancedScene).not.toMatch(/handwritten/i);
unmount();

const originalLocation = window.location.href;
window.history.pushState({}, "", "/?campaign=bulgaria-bureaucracy");
try {
render(<PlayApp />);
expect(
await screen.findByRole("heading", { name: "The Bureaucracy" }),
).toBeVisible();
expect(document.querySelector(".scene-body")?.textContent).toBe(
advancedScene,
);
expect(
screen.queryByRole("heading", { name: "Adventure disk library" }),
).not.toBeInTheDocument();
} finally {
window.history.pushState({}, "", originalLocation);
}
});

it("ignores a submission that resolves after the player quits to the library", async () => {
const user = userEvent.setup();
render(<PlayApp />);
Expand Down
4 changes: 3 additions & 1 deletion site/src/play/PlayApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ function PlayAppReady({ demo }: { demo: BrowserDemo }) {
if (!requested || !demo.findCampaign(requested)) return;
autoStarted.current = true;
setSelectedId(requested);
void start(requested);
const saveId = demo.findLocalSave(requested);
if (saveId) void resume(requested, saveId);
else void start(requested);
}, [demo]);

function reducedMotion(): boolean {
Expand Down
1 change: 1 addition & 0 deletions site/src/play/browser/accessibility.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async function scanForViolations(container: Element): Promise<void> {
describe("accessibility (W65.4)", () => {
it("shelf", async () => {
const { container } = render(<PlayApp />);
await screen.findByRole("heading", { level: 1 });
await scanForViolations(container);
});

Expand Down
1 change: 1 addition & 0 deletions site/src/play/browser/viewport.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe("viewport control (W65.2)", () => {
it("renders with no horizontal overflow at one landscape phone size", async () => {
await page.viewport(844, 390);
render(<PlayApp />);
await screen.findByRole("heading", { name: "Adventure disk library" });
assertNoHorizontalOverflow();
});
});
Expand Down
9 changes: 9 additions & 0 deletions site/src/play/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,20 @@ function localPersistence(): SessionPersistence {
return raw ? (JSON.parse(raw) as StoredSaveRecord) : undefined;
},
async put(record) {
const supersededId = localStorage.getItem(
campaignSaveIndexKey(record.campaignId),
);
localStorage.setItem(saveKey(record.saveId), JSON.stringify(record));
localStorage.setItem(
campaignSaveIndexKey(record.campaignId),
record.saveId,
Comment thread
The-Running-Dev marked this conversation as resolved.
);
// Every autosave mints a fresh saveId (types.ts), so the previous full
// record would otherwise sit in localStorage unreachable from the index.
// Removed only after the new record and index are safely written, so a
// failure here can never erase the only usable checkpoint.
if (supersededId && supersededId !== record.saveId)
localStorage.removeItem(saveKey(supersededId));
},
async delete(id) {
const raw = await this.get(id);
Expand Down
Loading