Persist custom world asset configs in runtime snapshots

This commit is contained in:
2026-04-18 17:00:46 +08:00
parent 7ce61e9879
commit ac801fe05f
29 changed files with 3397 additions and 400 deletions

View File

@@ -1,8 +1,15 @@
/* @vitest-environment jsdom */
import { render, screen } from '@testing-library/react';
import { renderToStaticMarkup } from 'react-dom/server';
import { expect, test } from 'vitest';
import { afterEach, expect, test, vi } from 'vitest';
import { CustomWorldAgentClarificationPanel } from './CustomWorldAgentClarificationPanel';
afterEach(() => {
vi.restoreAllMocks();
});
test('clarification panel shows pending questions and ready state', () => {
const pendingHtml = renderToStaticMarkup(
<CustomWorldAgentClarificationPanel
@@ -44,3 +51,48 @@ test('clarification panel shows pending questions and ready state', () => {
expect(pendingHtml).toContain('玩家是谁,故事开场时卡在什么处境里');
expect(readyHtml).toContain('当前设定已齐备,可以进入下一阶段');
});
test('falls back to stable keys when clarification ids are empty', () => {
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => undefined);
render(
<CustomWorldAgentClarificationPanel
readiness={{
isReady: false,
completedKeys: [],
missingKeys: ['player_premise', 'core_conflict'],
}}
pendingClarifications={[
{
id: '',
label: '玩家身份与开局',
question: '玩家是谁,故事开场时卡在什么处境里?',
targetKey: 'player_premise',
priority: 2,
},
{
id: '',
label: '核心冲突',
question: '第一阶段最直接撞上的冲突是什么?',
targetKey: 'core_conflict',
priority: 1,
},
]}
/>,
);
expect(screen.getByText(//u)).toBeTruthy();
expect(screen.getByText(//u)).toBeTruthy();
const duplicateKeyCalls = consoleErrorSpy.mock.calls.filter((call) =>
call.some(
(arg) =>
typeof arg === 'string' &&
arg.includes('Encountered two children with the same key'),
),
);
expect(duplicateKeyCalls).toHaveLength(0);
});

View File

@@ -44,7 +44,7 @@ export function CustomWorldAgentClarificationPanel({
<div className="mt-4 space-y-2">
{pendingClarifications.slice(0, 3).map((item, index) => (
<div
key={item.id}
key={item.id.trim() || `clarification-${item.targetKey}-${index}`}
className="rounded-[1.15rem] border border-white/8 bg-white/5 px-3 py-3"
>
<div className="flex items-start justify-between gap-3">