Files
wehub-resource-sync c48612c494
CI / E2E Tests (push) Has been cancelled
CI / Lint, Typecheck & Unit Tests (push) Has been cancelled
Docs Build / Build docs site (push) Has been cancelled
Publish @openmaic packages / Build, validate & publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:23 +08:00

158 lines
4.9 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { useCanvasStore, useStageStore } from '@/lib/store';
import { PENDING_SCENE_ID } from '@/lib/store/stage';
import { isCurrentSceneEditable } from '@/lib/edit/stage-mode';
import { sceneEditorRegistry } from '@/lib/edit/scene-editor-registry';
import type { SceneEditorSurface } from '@/lib/edit/scene-editor-surface';
import type { SceneType } from '@/lib/types/stage';
describe('stage edit mode store', () => {
beforeEach(() => {
useStageStore.getState().clearStore();
useCanvasStore.getState().resetCanvasState();
});
test('supports a global edit mode', () => {
useStageStore.getState().setMode('edit');
expect(useStageStore.getState().mode).toBe('edit');
});
test('clears canvas selection when leaving edit mode', () => {
useStageStore.getState().setMode('edit');
useCanvasStore.getState().setActiveElementIdList(['title']);
useCanvasStore.getState().setEditingElementId('title');
useStageStore.getState().setMode('playback');
expect(useCanvasStore.getState().activeElementIdList).toEqual([]);
expect(useCanvasStore.getState().handleElementId).toBe('');
expect(useCanvasStore.getState().editingElementId).toBe('');
});
});
describe('isCurrentSceneEditable', () => {
test('returns true when a real scene is resolved and nothing is generating', () => {
expect(
isCurrentSceneEditable({
currentSceneId: 'scene-1',
sceneCount: 3,
generatingOutlineCount: 0,
hasCurrentScene: true,
}),
).toBe(true);
});
test('returns false on the pending placeholder scene', () => {
expect(
isCurrentSceneEditable({
currentSceneId: PENDING_SCENE_ID,
sceneCount: 3,
generatingOutlineCount: 0,
hasCurrentScene: true,
}),
).toBe(false);
});
test('returns false when no scenes have materialised yet', () => {
expect(
isCurrentSceneEditable({
currentSceneId: null,
sceneCount: 0,
generatingOutlineCount: 0,
hasCurrentScene: false,
}),
).toBe(false);
});
test('returns false while outline generation is still in flight', () => {
expect(
isCurrentSceneEditable({
currentSceneId: 'scene-1',
sceneCount: 1,
generatingOutlineCount: 2,
hasCurrentScene: true,
}),
).toBe(false);
});
test('returns false when current scene id does not resolve to a scene', () => {
expect(
isCurrentSceneEditable({
currentSceneId: 'scene-x',
sceneCount: 3,
generatingOutlineCount: 0,
hasCurrentScene: false,
}),
).toBe(false);
});
});
describe('sceneEditorRegistry', () => {
function makeSurface(sceneType: SceneType, label = 'A'): SceneEditorSurface {
return {
sceneType,
SurfaceComponent: () => null,
useSurfaceState: () => ({
// Cast through unknown because tests don't need a real surface state;
// we only exercise the registry contract here.
content: { type: sceneType, label } as unknown as never,
selection: null,
hasSelection: false,
history: { canUndo: false, canRedo: false, undo: () => {}, redo: () => {} },
insertItems: [],
floatingActions: [],
commands: [],
}),
};
}
afterEach(() => {
sceneEditorRegistry.unregister('slide');
sceneEditorRegistry.unregister('quiz');
});
test('register and resolve round-trip by sceneType', () => {
const surface = makeSurface('slide');
sceneEditorRegistry.register(surface);
expect(sceneEditorRegistry.resolve('slide')).toBe(surface);
});
test('resolve returns undefined for unregistered sceneType', () => {
expect(sceneEditorRegistry.resolve('pbl')).toBeUndefined();
});
test('re-registering the identical surface instance does not warn (HMR-safe)', () => {
const surface = makeSurface('slide');
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
sceneEditorRegistry.register(surface);
sceneEditorRegistry.register(surface);
expect(warn).not.toHaveBeenCalled();
warn.mockRestore();
});
test('registering a different surface for the same sceneType warns in dev', () => {
const first = makeSurface('slide', 'A');
const second = makeSurface('slide', 'B');
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
sceneEditorRegistry.register(first);
sceneEditorRegistry.register(second);
expect(warn).toHaveBeenCalledOnce();
expect(sceneEditorRegistry.resolve('slide')).toBe(second);
warn.mockRestore();
});
test('unregister removes the surface', () => {
const surface = makeSurface('quiz');
sceneEditorRegistry.register(surface);
expect(sceneEditorRegistry.resolve('quiz')).toBe(surface);
sceneEditorRegistry.unregister('quiz');
expect(sceneEditorRegistry.resolve('quiz')).toBeUndefined();
});
});