merge: fix/37-reader-webtoon-cbz-naturalwidth dans develop (naturalWidth capturé avant neutralisation currentTarget)

This commit is contained in:
Git Agent
2026-08-24 16:32:32 +02:00
2 changed files with 96 additions and 1 deletions

View File

@ -217,9 +217,10 @@ export function CbzReader({
loading="lazy" loading="lazy"
style={verticalImageStyle(item.page)} style={verticalImageStyle(item.page)}
onLoad={(event) => { onLoad={(event) => {
const { naturalWidth, naturalHeight } = event.currentTarget;
setImageSizes((current) => ({ setImageSizes((current) => ({
...current, ...current,
[item.page]: { width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight } [item.page]: { width: naturalWidth, height: naturalHeight }
})); }));
}} }}
onError={() => setPageError(`Page ${item.page} indisponible.`)} onError={() => setPageError(`Page ${item.page} indisponible.`)}

View File

@ -0,0 +1,94 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const hookState = vi.hoisted(() => ({
stateIndex: 0,
states: [] as unknown[],
updates: [] as unknown[][]
}));
vi.mock("react", async () => {
const actual = await vi.importActual<typeof import("react")>("react");
return {
...actual,
useCallback: (callback: unknown) => callback,
useEffect: () => undefined,
useRef: (current: unknown) => ({ current }),
useState: (initial: unknown) => {
const index = hookState.stateIndex;
hookState.stateIndex += 1;
hookState.updates[index] = [];
return [
hookState.states[index] ?? initial,
(next: unknown) => {
hookState.updates[index].push(next);
}
];
}
};
});
vi.mock("../api/client", () => ({
api: {
cbzPages: vi.fn().mockResolvedValue({ bookId: 2, pageCount: 1, pages: [{ page: 1, name: "page-1.jpg" }] }),
cbzPageUrl: (bookId: number, page: number) => `/books/${bookId}/pages/${page}`
}
}));
import { CbzReader } from "./CbzReader";
type ElementLike = {
type: unknown;
props?: Record<string, unknown> & { children?: unknown };
};
function isElementLike(value: unknown): value is ElementLike {
return Boolean(value && typeof value === "object" && "type" in value);
}
function findElementsByType(node: unknown, type: string): ElementLike[] {
if (Array.isArray(node)) return node.flatMap((child) => findElementsByType(child, type));
if (!isElementLike(node)) return [];
const matches = node.type === type ? [node] : [];
return [...matches, ...findElementsByType(node.props?.children, type)];
}
describe("CbzReader vertical image load", () => {
beforeEach(() => {
hookState.stateIndex = 0;
hookState.updates = [];
hookState.states = [
{ bookId: 2, pageCount: 1, pages: [{ page: 1, name: "page-1.jpg" }] },
undefined,
undefined,
0,
0,
{ width: 800, height: 1200 },
null,
{}
];
});
it("records vertical image dimensions before React clears the load event target", () => {
const tree = CbzReader({
bookId: 2,
page: 1,
zoom: 100,
mode: "vertical",
onPageCommit: vi.fn(),
onControlsChange: vi.fn()
});
const image = findElementsByType(tree, "img")[0];
const onLoad = image.props?.onLoad as (event: { currentTarget: { naturalWidth: number; naturalHeight: number } | null }) => void;
const event: { currentTarget: { naturalWidth: number; naturalHeight: number } | null } = { currentTarget: { naturalWidth: 480, naturalHeight: 960 } };
onLoad(event);
event.currentTarget = null;
let nextSizes: unknown;
expect(() => {
nextSizes = (hookState.updates[7][0] as (current: Record<number, unknown>) => unknown)({});
}).not.toThrow();
expect(nextSizes).toEqual({ 1: { width: 480, height: 960 } });
});
});