import { act, fireEvent, within } from '@testing-library/react'; import { $createParagraphNode, $createTextNode, $getRoot, type LexicalEditor, } from 'lexical'; type GenerationPromptField = HTMLTextAreaElement & { __lexicalEditor?: LexicalEditor; }; function generationPromptField(scope: HTMLElement) { return within(scope).getByLabelText('生成提示词') as GenerationPromptField; } /** * 往生成面板的提示词输入区写一段文本。 * * 提示词输入区有**两种**实现,按入口不同而不同,这个 helper 两种都要支持: * - 图片类入口(生成图片 / 生成规范 / 生成角色形象 / 生成 UI 设计图)用的是与聊天、资源快速编辑 * 同一份 `@` 引用输入区(Lexical contenteditable)。jsdom 没有可用的 DOM Selection,Lexical 会 * 忽略浏览器输入事件——`userEvent.type` 与 `beforeinput` 都不会落字,`fireEvent.change` 更不适用, * 所以这条链路只能在编辑器实例上做等价更新:仍然走 Lexical 的 `update()` → `OnChangePlugin` → * 面板状态。 * - 纯文本入口(图集 / 图标规范 / 音效 / 背景音乐)仍然是普通 `textarea`,`fireEvent.change` 就是 * 它真实的输入路径,不需要也不应该套用编辑器 API。 * * 两种路径断言的都是面板真正收到的提示词;浏览器里的真实输入由引用输入区自己的测试与实机验收覆盖。 */ export async function typeGenerationPrompt(scope: HTMLElement, text: string) { const field = generationPromptField(scope); const editor = field.__lexicalEditor; if (!editor) { await act(async () => { fireEvent.change(field, { target: { value: text } }); }); return; } await act(async () => { editor.update(() => { const root = $getRoot(); root.clear(); const paragraph = $createParagraphNode(); paragraph.append($createTextNode(text)); root.append(paragraph); }); }); } /** 读回提示词输入区当前的文本:`textarea` 读 `value`,contenteditable 读文本内容。 */ export function generationPromptText(scope: HTMLElement) { const field = generationPromptField(scope) as GenerationPromptField & { value?: string; textContent?: string | null; }; return field.value ?? field.textContent ?? ''; }