Files
cozsweet-frontend-nextjs/src/app/private-zoom/components/__tests__/private-album-card.interaction.test.tsx
T

132 lines
3.8 KiB
TypeScript

/* @vitest-environment jsdom */
import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { PrivateAlbumSchema } from "@/data/schemas/private-zoom";
import { PrivateAlbumCard } from "../private-album-card";
describe("PrivateAlbumCard interactions", () => {
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
.IS_REACT_ACT_ENVIRONMENT = true;
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
});
it("opens the selected thumbnail with its original source index", () => {
const onOpenGallery = vi.fn();
const album = PrivateAlbumSchema.parse({
albumId: "album-1",
title: "Private afternoon",
imageCount: 4,
images: [
{ url: "", locked: false, index: 0 },
{ url: "/images/private-zoom/locked.png", locked: true, index: 1 },
{ url: "/images/private-zoom/photo-2.png", locked: false, index: 2 },
{ url: "/images/private-zoom/photo-3.png", locked: false, index: 3 },
],
locked: false,
unlocked: true,
lockDetail: { locked: false },
});
act(() => {
root.render(
<PrivateAlbumCard
album={album}
displayName="Elio Silvestri"
avatarUrl="/images/avatar/elio.png"
index={0}
isUnlocking={false}
onOpenGallery={onOpenGallery}
onUnlock={() => undefined}
/>,
);
});
const thumbnail = container.querySelector<HTMLButtonElement>(
'button[data-image-index="3"]',
);
act(() => thumbnail?.click());
expect(onOpenGallery).toHaveBeenCalledOnce();
expect(onOpenGallery).toHaveBeenCalledWith(3);
});
it("unlocks only from the collection CTA and disables repeat actions", () => {
const onUnlock = vi.fn();
const album = PrivateAlbumSchema.parse({
albumId: "album-locked",
title: "Locked afternoon",
imageCount: 3,
images: [
{ url: "/images/private-zoom/locked.png", locked: true, index: 0 },
],
locked: true,
unlocked: false,
unlockCost: 40,
lockDetail: { locked: true },
});
renderLockedCard(album, false, onUnlock);
const avatar = container.querySelector<HTMLElement>(
'[aria-label="Elio Silvestri locked collection"]',
);
act(() => avatar?.parentElement?.click());
expect(onUnlock).not.toHaveBeenCalled();
const button = findCollectionButton();
act(() => button.click());
expect(onUnlock).toHaveBeenCalledOnce();
renderLockedCard(album, true, onUnlock);
const disabledButton = findCollectionButton();
expect(disabledButton.disabled).toBe(true);
expect(disabledButton.textContent).toBe("Opening...");
act(() => disabledButton.click());
expect(onUnlock).toHaveBeenCalledOnce();
});
function renderLockedCard(
album: ReturnType<typeof PrivateAlbumSchema.parse>,
isUnlocking: boolean,
onUnlock: () => void,
): void {
act(() => {
root.render(
<PrivateAlbumCard
album={album}
displayName="Elio Silvestri"
avatarUrl="/images/avatar/elio.png"
index={0}
isUnlocking={isUnlocking}
onOpenGallery={() => undefined}
onUnlock={onUnlock}
/>,
);
});
}
function findCollectionButton(): HTMLButtonElement {
const button = Array.from(container.querySelectorAll("button")).find(
(item) =>
item.textContent === "View collection" ||
item.textContent === "Opening...",
);
if (!button) throw new Error("Missing View collection button");
return button;
}
});