@@ -45,6 +45,14 @@ const key = async (value) => {
4545try {
4646 await page . goto ( fixture . url ) ;
4747 await saved ( ) ;
48+ await page . waitForFunction ( ( ) => window . __latestContext ?. structuredContent . documentId === "demo" ) ;
49+ assert . equal ( await page . getByRole ( "tab" ) . count ( ) , 1 ) ;
50+ assert . equal (
51+ await page
52+ . getByRole ( "tab" , { name : "应用启动流程" , exact : true } )
53+ . getAttribute ( "aria-selected" ) ,
54+ "true" ,
55+ ) ;
4856 assert . ok (
4957 ! ( await fixture . client . listTools ( ) ) . tools . some (
5058 ( tool ) => tool . name === "restore_last_agent_edit" ,
@@ -216,23 +224,161 @@ try {
216224 await page . locator ( "#new-document" ) . click ( ) ;
217225 await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id !== "demo" ) ;
218226 await saved ( ) ;
227+ const blankId = await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document . id ) ;
228+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . api . getAppState ( ) . zoom . value ) , 1 ) ;
229+ await page . waitForFunction (
230+ ( id ) => window . __latestContext ?. structuredContent . documentId === id ,
231+ blankId ,
232+ ) ;
233+ assert . equal (
234+ await page . evaluate ( ( ) => window . __latestContext . structuredContent . elementIds ) ,
235+ undefined ,
236+ ) ;
237+ // 双击当前标签重命名;取消、空名称及保存都通过真实表单交互。
238+ await page . getByRole ( "tab" , { name : "未命名画板" , exact : true } ) . dblclick ( ) ;
239+ await page . locator ( "#rename-input" ) . fill ( "Discard this name" ) ;
240+ await page . keyboard . press ( "Escape" ) ;
241+ assert . equal ( await page . getByRole ( "tab" , { name : "未命名画板" , exact : true } ) . count ( ) , 1 ) ;
242+ await page . getByRole ( "tab" , { name : "未命名画板" , exact : true } ) . dblclick ( ) ;
243+ await page . locator ( "#rename-input" ) . fill ( " " ) ;
244+ assert . equal ( await page . locator ( "#rename-button" ) . isDisabled ( ) , true ) ;
245+ await page . locator ( "#rename-input" ) . fill ( "我的新画板" ) ;
246+ await page . keyboard . press ( "Enter" ) ;
247+ await page . waitForFunction ( ( ) => ! document . querySelector ( "dialog[open]" ) ) ;
248+ await saved ( ) ;
249+ assert . equal ( ( await fixture . call ( "read_scene" , { id : blankId } ) ) . document . title , "我的新画板" ) ;
250+ await page . waitForFunction (
251+ ( ) => window . __latestContext ?. structuredContent . title === "我的新画板" ,
252+ ) ;
219253 await key ( "ControlOrMeta+z" ) ;
220254 assert . equal (
221255 await page . evaluate ( ( ) => window . __excalidrawPanel . api . getSceneElements ( ) . length ) ,
222256 0 ,
223257 ) ;
224- await page . locator ( ".document-menu summary" ) . click ( ) ;
225- await page . getByRole ( "button" , { name : "应用启动流程" , exact : true } ) . click ( ) ;
258+ await page . getByRole ( "tab" , { name : "应用启动流程" , exact : true } ) . click ( ) ;
226259 await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id === "demo" ) ;
227260 await saved ( ) ;
261+ await page . waitForFunction ( ( ) => window . __latestContext ?. structuredContent . documentId === "demo" ) ;
228262 assert . equal ( await field ( "box" , "backgroundColor" ) , "#ffc9c9" ) ;
263+ await page . getByRole ( "tab" , { name : "我的新画板" , exact : true } ) . click ( ) ;
264+ await page . waitForFunction ( ( id ) => window . __excalidrawPanel . state ( ) . document ?. id === id , blankId ) ;
265+ await saved ( ) ;
266+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . api . getAppState ( ) . zoom . value ) , 1 ) ;
267+ await page . getByRole ( "tab" , { name : "我的新画板" , exact : true } ) . dblclick ( ) ;
268+ await page . locator ( "#rename-input" ) . fill ( "重命名后的画板" ) ;
269+ await page . keyboard . press ( "Enter" ) ;
270+ await page . waitForFunction ( ( ) => ! document . querySelector ( "dialog[open]" ) ) ;
271+ await saved ( ) ;
272+ // 快捷新建在菜单外可直接使用;原有画板可通过 tab 返回。
273+ await page . locator ( "#new-document-shortcut" ) . click ( ) ;
274+ await page . waitForFunction ( ( id ) => window . __excalidrawPanel . state ( ) . document ?. id !== id , blankId ) ;
275+ await saved ( ) ;
276+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . api . getAppState ( ) . zoom . value ) , 1 ) ;
277+ const widgetState = await page . evaluate ( ( ) => window . __pluginClient . widgetState ) ;
278+ fixture . setBootstrap ( { toolOutput : changed , widgetState } ) ;
279+ await page . reload ( ) ;
280+ await saved ( ) ;
281+ assert . equal (
282+ await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document . id ) ,
283+ widgetState . documentId ,
284+ ) ;
285+ await page . getByRole ( "tab" , { name : "重命名后的画板" , exact : true } ) . waitFor ( ) ;
286+ assert . equal ( await page . getByRole ( "tab" ) . count ( ) , 3 ) ;
287+ assert . equal ( await page . evaluate ( ( ) => document . documentElement . scrollWidth <= innerWidth ) , true ) ;
288+ // A regular vertical wheel scrolls overflowing tabs without moving the canvas;
289+ // native horizontal trackpad gestures still work.
290+ const tabs = page . getByRole ( "tablist" ) ;
291+ const canvasView = await page . evaluate ( ( ) => {
292+ const { scrollX, scrollY, zoom } = window . __excalidrawPanel . api . getAppState ( ) ;
293+ return { scrollX, scrollY, zoom } ;
294+ } ) ;
295+ await tabs . evaluate ( ( element ) => {
296+ element . scrollLeft = 0 ;
297+ } ) ;
298+ await tabs . hover ( ) ;
299+ await page . mouse . wheel ( 0 , 120 ) ;
300+ await page . waitForFunction ( ( ) => document . querySelector ( ".document-tabs" ) . scrollLeft > 0 ) ;
301+ await page . mouse . wheel ( 0 , - 1200 ) ;
302+ await page . waitForFunction ( ( ) => document . querySelector ( ".document-tabs" ) . scrollLeft === 0 ) ;
303+ await page . mouse . wheel ( 120 , 0 ) ;
304+ await page . waitForFunction ( ( ) => document . querySelector ( ".document-tabs" ) . scrollLeft > 0 ) ;
305+ assert . deepEqual (
306+ await page . evaluate ( ( ) => {
307+ const { scrollX, scrollY, zoom } = window . __excalidrawPanel . api . getAppState ( ) ;
308+ return { scrollX, scrollY, zoom } ;
309+ } ) ,
310+ canvasView ,
311+ ) ;
312+ await page . getByRole ( "tab" , { name : "未命名画板" , exact : true } ) . scrollIntoViewIfNeeded ( ) ;
313+ await page . screenshot ( { path : join ( artifacts , "narrow-tabs.png" ) } ) ;
314+ await page . setViewportSize ( { width : 1100 , height : 800 } ) ;
315+ await page . getByRole ( "tab" , { name : "应用启动流程" , exact : true } ) . click ( ) ;
316+ await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id === "demo" ) ;
317+ await saved ( ) ;
318+ await page . screenshot ( { path : join ( artifacts , "wide-tabs.png" ) } ) ;
229319 const exportData = await readFile ( join ( fixture . workspace , "diagram.excalidraw" ) ) ;
230320 await page
231321 . locator ( 'input[type="file"]' )
232322 . setInputFiles ( { name : "import.excalidraw" , mimeType : "application/json" , buffer : exportData } ) ;
233323 await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id !== "demo" ) ;
234324 await saved ( ) ;
235325 assert . equal ( await field ( "box" , "backgroundColor" ) , "#ffc9c9" ) ;
326+ // Closing tabs preserves documents, the active canvas, and the restored tab list.
327+ const importedId = await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document . id ) ;
328+ const diagramIds = ( await fixture . call ( "list_diagrams" ) ) . documents . map ( ( doc ) => doc . id ) . sort ( ) ;
329+ await page . getByRole ( "button" , { name : "关闭标签 应用启动流程" , exact : true } ) . click ( ) ;
330+ await page . waitForFunction ( ( ) => ! document . getElementById ( "diagram-tab-demo" ) ) ;
331+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document . id ) , importedId ) ;
332+ await page . waitForFunction ( ( ) => ! document . getElementById ( "new-document-shortcut" ) . disabled ) ;
333+ fixture . setBootstrap ( {
334+ toolOutput : changed ,
335+ widgetState : await page . evaluate ( ( ) => window . __pluginClient . widgetState ) ,
336+ } ) ;
337+ await page . reload ( ) ;
338+ await saved ( ) ;
339+ assert . equal ( await page . getByRole ( "tab" , { name : "应用启动流程" , exact : true } ) . count ( ) , 0 ) ;
340+ await page . locator ( ".document-menu summary" ) . click ( ) ;
341+ await page . locator ( ".document-list button" ) . filter ( { hasText : "应用启动流程" } ) . click ( ) ;
342+ await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id === "demo" ) ;
343+ await saved ( ) ;
344+ assert . equal ( await field ( "box" , "backgroundColor" ) , "#ffc9c9" ) ;
345+ await page . getByRole ( "button" , { name : "关闭标签 应用启动流程" , exact : true } ) . click ( ) ;
346+ await page . waitForFunction (
347+ ( id ) => window . __excalidrawPanel . state ( ) . document ?. id === id ,
348+ importedId ,
349+ ) ;
350+ await page . waitForFunction (
351+ ( id ) => window . __latestContext ?. structuredContent . documentId === id ,
352+ importedId ,
353+ ) ;
354+ while ( await page . locator ( ".document-tab-close" ) . count ( ) ) {
355+ await page . locator ( ".document-tab-close" ) . first ( ) . click ( ) ;
356+ await page . waitForFunction ( ( ) => ! document . getElementById ( "new-document-shortcut" ) . disabled ) ;
357+ }
358+ await page . waitForFunction ( ( ) => window . __latestContext ?. structuredContent . documentId === null ) ;
359+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document ) , null ) ;
360+ assert . equal (
361+ await page . evaluate ( ( ) => window . __excalidrawPanel . api . getSceneElements ( ) . length ) ,
362+ 0 ,
363+ ) ;
364+ assert . deepEqual (
365+ ( await fixture . call ( "list_diagrams" ) ) . documents . map ( ( doc ) => doc . id ) . sort ( ) ,
366+ diagramIds ,
367+ ) ;
368+ const closedState = await page . evaluate ( ( ) => window . __pluginClient . widgetState ) ;
369+ assert . equal ( closedState . documentId , null ) ;
370+ assert . deepEqual ( closedState . recentDocumentIds , [ ] ) ;
371+ fixture . setBootstrap ( { toolOutput : changed , widgetState : closedState } ) ;
372+ await page . reload ( ) ;
373+ await page . waitForLoadState ( "networkidle" ) ;
374+ assert . equal ( await page . evaluate ( ( ) => window . __excalidrawPanel . state ( ) . document ) , null ) ;
375+ assert . equal ( await page . getByRole ( "tab" ) . count ( ) , 0 ) ;
376+ await page . screenshot ( { path : join ( artifacts , "closed-tabs.png" ) } ) ;
377+ await page . locator ( ".document-menu summary" ) . click ( ) ;
378+ await page . locator ( ".document-list button" ) . filter ( { hasText : "应用启动流程" } ) . click ( ) ;
379+ await page . waitForFunction ( ( ) => window . __excalidrawPanel . state ( ) . document ?. id === "demo" ) ;
380+ await saved ( ) ;
381+ assert . equal ( await field ( "box" , "backgroundColor" ) , "#ffc9c9" ) ;
236382 assert . deepEqual ( errors , [ ] ) ;
237383 console . log (
238384 `PASS: mixed undo/redo + persistence, context menu, document switch, all exports, themes and narrow layout. Screenshots: ${ artifacts } ` ,
0 commit comments