Files
openmaic/OpenMAIC/tests/edit/surfaces/slide/slide-canvas-renderer-flag.test.ts
2026-08-16 14:58:47 +08:00

212 lines
6.6 KiB
TypeScript

import { createElement, type ReactNode } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { SlideContent } from '@openmaic/dsl';
import type { EditorTransaction } from '@openmaic/editor/core';
import type { EditableSlideCanvasWithUIProps } from '@openmaic/editor/ui';
const state = vi.hoisted(() => ({
rendererEnabled: true,
pickTarget: null as unknown,
lastRendererProps: undefined as EditableSlideCanvasWithUIProps | undefined,
legacyCanvasRenders: 0,
legacyTextBarRenders: 0,
applyTransaction: vi.fn(),
setActiveElementIdList: vi.fn(),
setEditingElementId: vi.fn(),
setCanvasScale: vi.fn(),
translate: vi.fn((key: string) => `app:${key}`),
}));
const content: SlideContent = {
type: 'slide',
canvas: {
id: 'slide-1',
viewportSize: 1000,
viewportRatio: 0.5625,
theme: {
backgroundColor: '#ffffff',
themeColors: ['#7c3aed'],
fontColor: '#333333',
fontName: 'Inter',
},
elements: [
{
id: 'text-1',
type: 'text',
left: 40,
top: 40,
width: 240,
height: 80,
rotate: 0,
content: '<p>Editor</p>',
defaultFontName: 'Inter',
defaultColor: '#333333',
},
],
},
};
vi.mock('@/lib/config/feature-flags', () => ({
isEditorRendererEnabled: () => state.rendererEnabled,
}));
vi.mock('@/components/slide-renderer/Editor/Canvas', () => ({
default: () => {
state.legacyCanvasRenders += 1;
return createElement('div', { 'data-legacy-canvas': '' });
},
}));
vi.mock('@/components/slide-renderer/Editor/SpotlightOverlay', () => ({
SpotlightOverlay: () => null,
}));
vi.mock('@/components/slide-renderer/Editor/LaserPointerOverlay', () => ({
LaserPointerOverlay: () => null,
}));
vi.mock('@/lib/contexts/scene-context', () => ({
SceneProvider: ({ children }: { children: ReactNode }) => children,
}));
vi.mock('@/components/edit/surfaces/slide/AnchoredTextBar', () => ({
AnchoredTextBar: () => {
state.legacyTextBarRenders += 1;
return null;
},
}));
vi.mock('@/components/edit/surfaces/slide/AnchoredElementBar', () => ({
AnchoredElementBar: () => null,
}));
vi.mock('@/components/edit/surfaces/slide/ElementPickLayer', () => ({
ElementPickLayer: () => null,
}));
vi.mock('@/components/edit/surfaces/slide/use-slide-surface', () => ({
useSlideCanvasController: () => ({ controller: {}, gestureProps: {} }),
useEditingTextElementId: () => 'text-1',
useSelectedNonTextElement: () => null,
useSyncEditingElementId: () => undefined,
useResolvedSlideContent: () => content,
}));
vi.mock('@/components/slide-renderer/use-resolved-slide', () => ({
useResolvedSlide: () => content.canvas,
}));
vi.mock('@/lib/hooks/use-i18n', () => ({
useI18n: () => ({ locale: 'ja-JP', t: state.translate }),
}));
vi.mock('@/lib/edit/element-id', () => ({
createElementId: (type: string) => `app-${type}`,
}));
vi.mock('@/components/edit/surfaces/slide/slide-edit-session', () => ({
useSlideEditSession: {
getState: () => ({
sceneId: 'scene-1',
applyTransactionForScene: (sceneId: string, transaction: EditorTransaction) => {
state.applyTransaction(sceneId, transaction);
},
}),
},
}));
vi.mock('@/lib/store/canvas', () => ({
useCanvasStore: {
use: {
activeElementIdList: () => ['text-1'],
hiddenElementIdList: () => [],
editingElementId: () => 'text-1',
pickTarget: () => state.pickTarget,
setActiveElementIdList: () => state.setActiveElementIdList,
setEditingElementId: () => state.setEditingElementId,
setCanvasScale: () => state.setCanvasScale,
},
getState: () => ({ creatingElement: null, setCreatingElement: vi.fn() }),
},
}));
vi.mock('@openmaic/editor/ui', () => ({
EditableSlideCanvasWithUI: (props: EditableSlideCanvasWithUIProps) => {
state.lastRendererProps = props;
return createElement('div', { 'data-renderer-editor': '' });
},
}));
import { SlideCanvas } from '@/components/edit/surfaces/slide/SlideCanvas';
beforeEach(() => {
state.rendererEnabled = true;
state.pickTarget = null;
state.lastRendererProps = undefined;
state.legacyCanvasRenders = 0;
state.legacyTextBarRenders = 0;
state.applyTransaction.mockReset();
state.setActiveElementIdList.mockReset();
state.setEditingElementId.mockReset();
state.setCanvasScale.mockReset();
state.translate.mockClear();
});
describe('slide editor renderer feature flag', () => {
it('uses the renderer editor host bridge when enabled', () => {
const markup = renderToStaticMarkup(createElement(SlideCanvas));
expect(markup).toContain('data-renderer-editor');
expect(state.legacyCanvasRenders).toBe(0);
expect(state.legacyTextBarRenders).toBe(0);
expect(state.lastRendererProps).toMatchObject({
slide: content.canvas,
selection: { elementIds: ['text-1'], primaryId: 'text-1', editingId: 'text-1' },
insertToolbarPlacement: 'top',
snapping: true,
});
expect(state.lastRendererProps?.host).toMatchObject({
locale: 'ja-JP',
shortcutsEnabled: true,
});
expect(state.lastRendererProps?.host?.translate?.('insert.textBox')).toBe(
'app:edit.insert.textBox',
);
expect(state.lastRendererProps?.host?.createElementId?.('image')).toBe('app-image');
});
it('forwards transactions and controlled selection through generic callbacks', () => {
renderToStaticMarkup(createElement(SlideCanvas));
const transaction: EditorTransaction = {
origin: 'toolbar',
history: 'record',
operations: [{ type: 'element.deleteMany', elementIds: ['text-1'] }],
};
state.lastRendererProps?.onTransaction?.(transaction);
state.lastRendererProps?.onSelectionChange?.({ elementIds: ['next'], editingId: 'next' });
expect(state.applyTransaction).toHaveBeenCalledWith('scene-1', transaction);
expect(state.setActiveElementIdList).toHaveBeenCalledWith(['next']);
expect(state.setEditingElementId).toHaveBeenCalledWith('next');
});
it('suppresses editor shortcuts while an external pick interaction is active', () => {
state.pickTarget = { type: 'image' };
renderToStaticMarkup(createElement(SlideCanvas));
expect(state.lastRendererProps?.host?.shortcutsEnabled).toBe(false);
});
it('preserves the legacy editor path when disabled', () => {
state.rendererEnabled = false;
const markup = renderToStaticMarkup(createElement(SlideCanvas));
expect(markup).toContain('data-legacy-canvas');
expect(state.lastRendererProps).toBeUndefined();
expect(state.legacyCanvasRenders).toBe(1);
expect(state.legacyTextBarRenders).toBe(1);
});
});