@@ -95,3 +95,76 @@ export async function openFixtureInLocalEditor(
9595 await injectFixtureIntoLocalStorage ( page , fixture )
9696 await page . goto ( `/local/${ fixture . id as string } ` )
9797}
98+
99+ /**
100+ * Create one of the bundled presets through the real dashboard dialog.
101+ *
102+ * Template visual tests use this instead of injecting the asset directly:
103+ * cloning, transient-state cleanup, fresh-id assignment, persistence and route
104+ * navigation are all part of what a user sees when choosing a preset.
105+ */
106+ export async function createTemplateInLocalEditor (
107+ page : Page ,
108+ templateName : string
109+ ) {
110+ await page . addInitScript ( ( ) => {
111+ localStorage . setItem (
112+ "persistenceModelStore" ,
113+ JSON . stringify ( {
114+ state : { models : { } , currentModelId : null } ,
115+ version : 3 ,
116+ } )
117+ )
118+ } )
119+ await page . goto ( "/" )
120+ await page
121+ . getByRole ( "heading" , { level : 1 , name : "Your diagrams" } )
122+ . waitFor ( { timeout : 15_000 } )
123+
124+ await page . getByRole ( "button" , { name : "New diagram" } ) . first ( ) . click ( )
125+ const dialog = page . getByRole ( "dialog" )
126+ await dialog . getByRole ( "tab" , { name : "Use template" } ) . click ( )
127+ await dialog . getByRole ( "button" , { name : templateName , exact : true } ) . click ( )
128+ await dialog . getByRole ( "button" , { name : "Create Diagram" } ) . click ( )
129+
130+ await page . waitForURL ( / \/ l o c a l \/ [ ^ / ] + $ / , { timeout : 15_000 } )
131+ await waitForCanvasReady ( page )
132+
133+ return page . evaluate ( ( ) => {
134+ const persisted = localStorage . getItem ( "persistenceModelStore" )
135+ if ( ! persisted ) return null
136+
137+ const state = JSON . parse ( persisted ) . state as {
138+ currentModelId ?: string
139+ models ?: Record < string , { model ?: Record < string , unknown > } >
140+ }
141+ const currentId = state . currentModelId
142+ return currentId ? ( state . models ?. [ currentId ] ?. model ?? null ) : null
143+ } )
144+ }
145+
146+ /** Select an edge through the visible middle of its rendered path. */
147+ export async function selectEdgeOnPath ( page : Page , edgeId : string ) {
148+ const point = await page . evaluate ( ( id ) => {
149+ const path = document . querySelector (
150+ `.react-flow__edge[data-id="${ id } "] path.react-flow__edge-path`
151+ ) as SVGPathElement | null
152+ if ( ! path ) return null
153+
154+ const transform = path . getScreenCTM ( )
155+ if ( ! transform ) return null
156+
157+ const midpoint = path . getPointAtLength ( path . getTotalLength ( ) / 2 )
158+ const screenPoint = new DOMPoint ( midpoint . x , midpoint . y ) . matrixTransform (
159+ transform
160+ )
161+ return { x : screenPoint . x , y : screenPoint . y }
162+ } , edgeId )
163+
164+ if ( ! point ) {
165+ throw new Error ( `Edge "${ edgeId } " path was not rendered` )
166+ }
167+
168+ await page . mouse . click ( point . x , point . y )
169+ await page . waitForTimeout ( 200 )
170+ }
0 commit comments