@@ -89,6 +89,69 @@ test("an infeasible block keeps its burner row editable", async ({ page }) => {
8989 }
9090} ) ;
9191
92+ test ( "favoriting a fuel keeps the burner rows on the current solve" , async ( { page } ) => {
93+ const db = new DatabaseSync ( activeProjectDbFile ( ) ) ;
94+ db . exec ( "PRAGMA busy_timeout = 5000" ) ;
95+ const favoriteFuels = db
96+ . prepare ( "SELECT value FROM meta WHERE key = 'favorite_fuels'" )
97+ . get ( ) as { value : string } | undefined ;
98+ const data = {
99+ recipes : [ "boil-steam-250" ] ,
100+ made : [ "steam" ] ,
101+ machines : { "boil-steam-250" : "boiler" } ,
102+ fuels : { "boil-steam-250" : "coal" } ,
103+ goals : [ { name : "steam" , rate : 1 } ] ,
104+ } ;
105+ const inserted = db
106+ . prepare ( "INSERT INTO blocks (name, data) VALUES (?, ?)" )
107+ . run ( uniqueName ( "Favorite fuel" ) , JSON . stringify ( data ) ) ;
108+ const id = Number ( inserted . lastInsertRowid ) ;
109+ db . close ( ) ;
110+
111+ try {
112+ await goto ( page , `/block/${ id } ` ) ;
113+ const row = page . locator ( '[data-recipe-row="boil-steam-250"]' ) ;
114+
115+ // Make a post-load editor change. The solve query's initial loader still
116+ // holds the original coal document, while the live saved solve now uses raw
117+ // coal — invalidating that query would put the row back on stale data.
118+ await row . getByTitle ( / C o a l .* c l i c k t o c h a n g e f u e l / ) . click ( ) ;
119+ let fuels = page . getByRole ( "dialog" , { name : "Fuel for Boil Steam (250°)" } ) ;
120+ await fuels . getByRole ( "button" , { name : / ^ R a w c o a l / } ) . click ( ) ;
121+ await expect ( fuels ) . toBeHidden ( ) ;
122+ await expect ( row . getByTitle ( / R a w c o a l .* c l i c k t o c h a n g e f u e l / ) ) . toBeVisible ( ) ;
123+
124+ await row . getByTitle ( / R a w c o a l .* c l i c k t o c h a n g e f u e l / ) . click ( ) ;
125+ fuels = page . getByRole ( "dialog" , { name : "Fuel for Boil Steam (250°)" } ) ;
126+ const rawCoal = fuels . getByRole ( "button" , { name : / ^ R a w c o a l / } ) ;
127+ const star = rawCoal . locator ( '[title*="favorite fuel" i]' ) ;
128+ const previousTitle = await star . getAttribute ( "title" ) ;
129+ await star . click ( ) ;
130+ await expect ( star ) . not . toHaveAttribute ( "title" , previousTitle ! ) ;
131+
132+ // Let any invalidated background query settle: the favorite is an app-level
133+ // default for future rows and must not replace this editor's live solve.
134+ await page . waitForTimeout ( 750 ) ;
135+ await expect ( row . getByTitle ( / R a w c o a l .* c l i c k t o c h a n g e f u e l / ) ) . toBeVisible ( ) ;
136+ } finally {
137+ const cleanup = new DatabaseSync ( activeProjectDbFile ( ) ) ;
138+ try {
139+ cleanup . exec ( "PRAGMA busy_timeout = 5000" ) ;
140+ if ( favoriteFuels )
141+ cleanup
142+ . prepare (
143+ `INSERT INTO meta (key, value) VALUES ('favorite_fuels', ?)
144+ ON CONFLICT(key) DO UPDATE SET value = excluded.value` ,
145+ )
146+ . run ( favoriteFuels . value ) ;
147+ else cleanup . prepare ( "DELETE FROM meta WHERE key = 'favorite_fuels'" ) . run ( ) ;
148+ cleanup . prepare ( "DELETE FROM blocks WHERE id = ?" ) . run ( id ) ;
149+ } finally {
150+ cleanup . close ( ) ;
151+ }
152+ }
153+ } ) ;
154+
92155test ( "variable generator shows its average and min-max output" , async ( { page } ) => {
93156 const db = new DatabaseSync ( activeProjectDbFile ( ) ) ;
94157 db . exec ( "PRAGMA busy_timeout = 5000" ) ;
0 commit comments