refactor(private-zone): use canonical product name
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
/* @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-zone";
|
||||
|
||||
import { PrivateAlbumGallery } from "../private-album-gallery";
|
||||
|
||||
const album = PrivateAlbumSchema.parse({
|
||||
albumId: "album-1",
|
||||
title: "Private afternoon",
|
||||
imageCount: 5,
|
||||
images: [
|
||||
{ url: "/images/private-zone/photo-0.png", locked: false, index: 0 },
|
||||
{ url: "", locked: false, index: 1 },
|
||||
{ url: "/images/private-zone/locked.png", locked: true, index: 2 },
|
||||
{ url: "/images/private-zone/photo-3.png", locked: false, index: 3 },
|
||||
{ url: "/images/private-zone/photo-4.png", locked: false, index: 4 },
|
||||
],
|
||||
locked: false,
|
||||
unlocked: true,
|
||||
lockDetail: { locked: false },
|
||||
});
|
||||
|
||||
describe("PrivateAlbumGallery", () => {
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
let onClose: () => void;
|
||||
let onImageIndexChange: (index: number) => void;
|
||||
|
||||
beforeEach(() => {
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
|
||||
.IS_REACT_ACT_ENVIRONMENT = true;
|
||||
Object.defineProperties(HTMLElement.prototype, {
|
||||
setPointerCapture: {
|
||||
configurable: true,
|
||||
value: vi.fn(),
|
||||
},
|
||||
hasPointerCapture: {
|
||||
configurable: true,
|
||||
value: vi.fn(() => true),
|
||||
},
|
||||
releasePointerCapture: {
|
||||
configurable: true,
|
||||
value: vi.fn(),
|
||||
},
|
||||
});
|
||||
onClose = vi.fn();
|
||||
onImageIndexChange = vi.fn();
|
||||
container = document.createElement("div");
|
||||
document.body.append(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it("follows a drag and switches to the next available source index", () => {
|
||||
renderGallery(0);
|
||||
const viewport = getViewport();
|
||||
Object.defineProperty(viewport, "clientWidth", {
|
||||
configurable: true,
|
||||
value: 390,
|
||||
});
|
||||
|
||||
dispatchPointer(viewport, "pointerdown", 300);
|
||||
dispatchPointer(viewport, "pointermove", 200);
|
||||
expect(viewport.querySelector<HTMLElement>("[aria-hidden=false]")?.style.getPropertyValue("--gallery-drag-offset")).toBe("-100px");
|
||||
dispatchPointer(viewport, "pointerup", 200);
|
||||
|
||||
expect(onImageIndexChange).toHaveBeenCalledWith(3);
|
||||
expect(container.textContent).toContain("2 / 3");
|
||||
});
|
||||
|
||||
it("snaps back on pointer cancellation without changing images", () => {
|
||||
renderGallery(3);
|
||||
const viewport = getViewport();
|
||||
Object.defineProperty(viewport, "clientWidth", {
|
||||
configurable: true,
|
||||
value: 390,
|
||||
});
|
||||
|
||||
dispatchPointer(viewport, "pointerdown", 300);
|
||||
dispatchPointer(viewport, "pointermove", 200);
|
||||
dispatchPointer(viewport, "pointercancel", 200);
|
||||
|
||||
expect(onImageIndexChange).not.toHaveBeenCalled();
|
||||
expect(container.textContent).toContain("2 / 3");
|
||||
});
|
||||
|
||||
it("uses the same available indexes for controls and keyboard navigation", () => {
|
||||
renderGallery(3);
|
||||
|
||||
act(() => {
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[aria-label="Next photo"]')
|
||||
?.click();
|
||||
});
|
||||
expect(onImageIndexChange).toHaveBeenLastCalledWith(4);
|
||||
|
||||
act(() => {
|
||||
document.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { key: "ArrowLeft", bubbles: true }),
|
||||
);
|
||||
});
|
||||
expect(onImageIndexChange).toHaveBeenLastCalledWith(3);
|
||||
|
||||
act(() => {
|
||||
document.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
||||
);
|
||||
});
|
||||
expect(onClose).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
function renderGallery(imageIndex: number): void {
|
||||
act(() => {
|
||||
root.render(
|
||||
<PrivateAlbumGallery
|
||||
album={album}
|
||||
imageIndex={imageIndex}
|
||||
onClose={onClose}
|
||||
onImageIndexChange={onImageIndexChange}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getViewport(): HTMLDivElement {
|
||||
const viewport = container.querySelector<HTMLDivElement>(
|
||||
'[data-testid="private-album-gallery-viewport"]',
|
||||
);
|
||||
if (!viewport) throw new Error("Missing Gallery viewport");
|
||||
return viewport;
|
||||
}
|
||||
});
|
||||
|
||||
function dispatchPointer(
|
||||
target: HTMLElement,
|
||||
type: "pointerdown" | "pointermove" | "pointerup" | "pointercancel",
|
||||
clientX: number,
|
||||
): void {
|
||||
const event = new MouseEvent(type, {
|
||||
bubbles: true,
|
||||
button: 0,
|
||||
clientX,
|
||||
});
|
||||
Object.defineProperty(event, "pointerId", { value: 1 });
|
||||
act(() => target.dispatchEvent(event));
|
||||
}
|
||||
Reference in New Issue
Block a user