Files
Genarrative/packages/image-canvas-react/src/sharedCanvasReact.test.tsx
T
menghao 03a4410272
Project CI / Repository checks (push) Successful in 1m5s
Project CI / Frontend tests (push) Successful in 3m20s
Project CI / Backend tests (push) Successful in 4m1s
Project CI / Native shell tests (push) Failing after 11m56s
game agent无限画布开发 (#136)
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136
Co-authored-by: menghao <mh18530625731@163.com>
Co-committed-by: menghao <mh18530625731@163.com>
2026-08-12 10:54:16 +08:00

147 lines
4.4 KiB
TypeScript

/* @vitest-environment jsdom */
import type { CanvasLayer } from '@genarrative/image-canvas-core';
import { act, renderHook } from '@testing-library/react';
import { createRef } from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { useImageCanvasViewportControls } from './useImageCanvasViewportControls';
function layer(id: string, x: number): CanvasLayer {
return {
id,
resourceId: `resource-${id}`,
title: id,
src: `media:${id}`,
x,
y: 0,
width: 100,
height: 100,
originalWidth: 100,
originalHeight: 100,
zIndex: 1,
sourceType: 'uploaded',
};
}
function createViewport() {
const viewport = document.createElement('div');
Object.defineProperties(viewport, {
clientWidth: { configurable: true, value: 800 },
clientHeight: { configurable: true, value: 600 },
});
viewport.getBoundingClientRect = () => ({
left: 0,
top: 0,
right: 800,
bottom: 600,
width: 800,
height: 600,
x: 0,
y: 0,
toJSON: () => undefined,
});
document.body.append(viewport);
return viewport;
}
afterEach(() => {
document.body.replaceChildren();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
describe('shared canvas React lifecycle', () => {
it('removes every native listener across repeated mounts', () => {
const viewport = createViewport();
const addListener = vi.spyOn(viewport, 'addEventListener');
const removeListener = vi.spyOn(viewport, 'removeEventListener');
const ref = createRef<HTMLDivElement>();
ref.current = viewport;
const options = {
canvasViewportRef: ref,
layers: [layer('a', 0)],
captureCanvasHistory: vi.fn(),
};
const first = renderHook(() => useImageCanvasViewportControls(options));
first.unmount();
const second = renderHook(() => useImageCanvasViewportControls(options));
second.unmount();
expect(
addListener.mock.calls.filter(([type]) => type === 'wheel'),
).toHaveLength(2);
expect(
removeListener.mock.calls.filter(([type]) => type === 'wheel'),
).toHaveLength(2);
});
it('disconnects the instance ResizeObserver on unmount', () => {
const viewport = createViewport();
const disconnect = vi.fn();
const observe = vi.fn();
vi.stubGlobal(
'ResizeObserver',
class {
observe = observe;
disconnect = disconnect;
},
);
const ref = createRef<HTMLDivElement>();
ref.current = viewport;
const hook = renderHook(() =>
useImageCanvasViewportControls({
canvasViewportRef: ref,
layers: [layer('a', 0)],
captureCanvasHistory: vi.fn(),
}),
);
expect(observe).toHaveBeenCalledWith(viewport);
hook.unmount();
expect(disconnect).toHaveBeenCalledTimes(1);
});
it('uses the minimap inside each viewport instead of another canvas instance', () => {
const firstViewport = createViewport();
const secondViewport = createViewport();
const firstMinimap = document.createElement('button');
const secondMinimap = document.createElement('button');
firstMinimap.className = 'genarrative-image-canvas__minimap';
secondMinimap.className = 'genarrative-image-canvas__minimap';
firstMinimap.getBoundingClientRect = () =>
({ left: 10, top: 10, width: 132, height: 84 }) as DOMRect;
secondMinimap.getBoundingClientRect = () =>
({ left: 300, top: 200, width: 132, height: 84 }) as DOMRect;
firstViewport.append(firstMinimap);
secondViewport.append(secondMinimap);
const firstRef = createRef<HTMLDivElement>();
const secondRef = createRef<HTMLDivElement>();
firstRef.current = firstViewport;
secondRef.current = secondViewport;
const first = renderHook(() =>
useImageCanvasViewportControls({
canvasViewportRef: firstRef,
layers: [layer('a', 0), layer('b', 500)],
captureCanvasHistory: vi.fn(),
}),
);
const second = renderHook(() =>
useImageCanvasViewportControls({
canvasViewportRef: secondRef,
layers: [layer('c', 1000), layer('d', 1600)],
captureCanvasHistory: vi.fn(),
}),
);
const firstBefore = first.result.current.viewport;
const secondBefore = second.result.current.viewport;
act(() => first.result.current.moveViewportFromMinimapPointer(130, 80));
expect(first.result.current.viewport).not.toEqual(firstBefore);
expect(second.result.current.viewport).toEqual(secondBefore);
});
});