OpenMAIC-React / e2e /tests /classroom-interaction.spec.ts
muthuk1's picture
Add missing files: LICENSE, Dockerfile, .github, tests, e2e, eval, scripts, configs
a0ebf39 verified
import { test, expect } from '../fixtures/base';
import { ClassroomPage } from '../pages/classroom.page';
import { createSettingsStorage } from '../fixtures/test-data/settings';
import { defaultTheme } from '../fixtures/test-data/scene-content';
const TEST_STAGE_ID = 'e2e-test-stage';
const SETTINGS_STORAGE = createSettingsStorage({ sidebarCollapsed: false });
/** Seed IndexedDB with stage + 3 scenes using raw IndexedDB API */
async function seedDatabase(page: import('@playwright/test').Page) {
// Inject settings before navigating so it's available immediately on load
await page.addInitScript((settings) => {
localStorage.setItem('settings-storage', settings);
}, SETTINGS_STORAGE);
// Navigate to home page first — this causes Dexie to open/create the DB at v8
// with the correct schema. We wait for network idle to ensure Dexie is done.
await page.goto('/', { waitUntil: 'networkidle' });
// Now seed data by opening the DB at its current version (no upgrade).
// Opening without a version number returns the current version without triggering
// onupgradeneeded, so we can safely write to the already-initialized schema.
await page.evaluate(
({ stageId, theme }) => {
return new Promise<void>((resolve, reject) => {
// Open without specifying version — uses current DB version, no upgrade event
const request = indexedDB.open('MAIC-Database');
request.onsuccess = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
const tx = db.transaction(['stages', 'scenes', 'stageOutlines'], 'readwrite');
const now = Date.now();
tx.objectStore('stages').put({
id: stageId,
name: '光合作用',
description: '',
language: 'zh-CN',
style: 'professional',
createdAt: now,
updatedAt: now,
});
// Scene content uses SlideContent shape: { type: 'slide', canvas: Slide }
const makeSlideContent = (title: string, elId: string) => ({
type: 'slide',
canvas: {
id: `slide-${elId}`,
viewportSize: 1000,
viewportRatio: 0.5625,
theme,
elements: [
{
type: 'text',
id: `el-${elId}`,
content: title,
left: 50,
top: 50,
width: 900,
height: 100,
},
],
},
});
const scenes = [
{
id: 'scene-0',
stageId,
type: 'slide',
title: '基本概念',
order: 0,
content: makeSlideContent('基本概念', '0'),
createdAt: now,
updatedAt: now,
},
{
id: 'scene-1',
stageId,
type: 'slide',
title: '光反应',
order: 1,
content: makeSlideContent('光反应', '1'),
createdAt: now,
updatedAt: now,
},
{
id: 'scene-2',
stageId,
type: 'slide',
title: '暗反应',
order: 2,
content: makeSlideContent('暗反应', '2'),
createdAt: now,
updatedAt: now,
},
];
for (const scene of scenes) {
tx.objectStore('scenes').put(scene);
}
// Empty outlines = all scenes generated, no pending work
// StageOutlinesRecord requires createdAt + updatedAt
tx.objectStore('stageOutlines').put({
stageId,
outlines: [],
createdAt: now,
updatedAt: now,
});
tx.oncomplete = () => {
db.close();
resolve();
};
tx.onerror = () => reject(tx.error);
};
request.onerror = () => reject(request.error);
});
},
{ stageId: TEST_STAGE_ID, theme: defaultTheme },
);
}
test.describe('Classroom Interaction', () => {
test.beforeEach(async ({ page }) => {
await seedDatabase(page);
});
test('loads classroom and switches scenes', async ({ page }) => {
const classroom = new ClassroomPage(page);
await classroom.goto(TEST_STAGE_ID);
await classroom.waitForLoaded();
// Sidebar shows 3 scenes
await expect(classroom.sidebarScenes).toHaveCount(3, { timeout: 10_000 });
// First scene title visible
await expect(classroom.getSceneTitle(0)).toContainText('基本概念');
// Click second scene
await classroom.clickScene(1);
// Verify second scene is now active — heading in the top bar shows the current scene name
await expect(page.getByRole('heading', { name: '光反应' })).toBeVisible();
});
});