@@ -126,7 +126,7 @@ describe("CursorPane theme picker", () => {
126126 await upload ( "Arrow" , "arrow.PNG" , "arrow-image" ) ;
127127 const arrow = getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomTheme . arrow ;
128128 expect ( arrow ) . toMatch ( / ^ d a t a : i m a g e \/ p n g ; b a s e 6 4 , / ) ;
129- expect ( screen . getByRole ( "button" , { name : "Custom" } ) . getAttribute ( "aria-pressed" ) ) . toBe (
129+ expect ( screen . getByRole ( "button" , { name : "Custom 1 " } ) . getAttribute ( "aria-pressed" ) ) . toBe (
130130 "true" ,
131131 ) ;
132132 await upload ( "Hand" , "hand.png" , "hand-image" ) ;
@@ -140,14 +140,16 @@ describe("CursorPane theme picker", () => {
140140 expect ( replaced . arrow ) . not . toBe ( arrow ) ;
141141 expect ( replaced . pointer ) . toBe ( theme . pointer ) ;
142142 expect ( replaced . text ) . toBe ( theme . text ) ;
143- expect ( screen . getAllByRole ( "button" , { name : "Custom" } ) ) . toHaveLength ( 1 ) ;
143+ expect ( screen . getAllByRole ( "button" , { name : "Custom 1 " } ) ) . toHaveLength ( 1 ) ;
144144 fireEvent . click ( screen . getByRole ( "button" , { name : "Default" } ) ) ;
145145 await waitFor ( ( ) =>
146146 expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorTheme ) . toBe ( "default" ) ,
147147 ) ;
148- fireEvent . click ( screen . getByRole ( "button" , { name : "Custom" } ) ) ;
148+ fireEvent . click ( screen . getByRole ( "button" , { name : "Custom 1 " } ) ) ;
149149 await waitFor ( ( ) =>
150- expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorTheme ) . toBe ( "custom" ) ,
150+ expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorTheme ) . toMatch (
151+ / ^ c u s t o m : / ,
152+ ) ,
151153 ) ;
152154 expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomTheme ) . toEqual (
153155 replaced ,
@@ -158,7 +160,7 @@ describe("CursorPane theme picker", () => {
158160 const arrow = "data:image/png;base64,YXJyb3c=" ;
159161 const pointer = "data:image/png;base64,aGFuZA==" ;
160162 renderWithProject ( { cursorTheme : "custom" , cursorCustomTheme : { arrow, pointer } } ) ;
161- fireEvent . click ( screen . getByRole ( "button" , { name : "Add custom cursor " } ) ) ;
163+ fireEvent . click ( screen . getByRole ( "button" , { name : "Custom 1 " } ) ) ;
162164 fireEvent . click ( screen . getByRole ( "button" , { name : "Remove Hand image" } ) ) ;
163165 await waitFor ( ( ) => {
164166 const settings = getEditorSettings ( useProjectStore . getState ( ) . document ) ;
@@ -173,22 +175,19 @@ describe("CursorPane theme picker", () => {
173175 ) . toBeTruthy ( ) ;
174176 } ) ;
175177
176- it . each ( [
177- "custom" ,
178- "studio-ink" ,
179- ] ) ( "removes the last custom image safely while %s is selected" , async ( selected ) => {
178+ it ( "removes the last custom image safely" , async ( ) => {
180179 renderWithProject ( {
181- cursorTheme : selected ,
180+ cursorTheme : "custom" ,
182181 cursorCustomTheme : { text : "data:image/png;base64,dGV4dA==" } ,
183182 } ) ;
184- fireEvent . click ( screen . getByRole ( "button" , { name : "Add custom cursor " } ) ) ;
183+ fireEvent . click ( screen . getByRole ( "button" , { name : "Custom 1 " } ) ) ;
185184 fireEvent . click ( screen . getByRole ( "button" , { name : "Remove Text image" } ) ) ;
186185 await waitFor ( ( ) => {
187186 const settings = getEditorSettings ( useProjectStore . getState ( ) . document ) ;
188187 expect ( settings . cursorCustomTheme ) . toEqual ( { } ) ;
189- expect ( settings . cursorTheme ) . toBe ( selected === "custom" ? " default" : selected ) ;
188+ expect ( settings . cursorTheme ) . toBe ( " default") ;
190189 } ) ;
191- expect ( screen . queryByRole ( "button" , { name : "Custom" } ) ) . toBeNull ( ) ;
190+ expect ( screen . queryByRole ( "button" , { name : "Custom 1 " } ) ) . toBeNull ( ) ;
192191 expect ( screen . queryByRole ( "button" , { name : "Delete custom cursor set" } ) ) . toBeNull ( ) ;
193192 } ) ;
194193
@@ -200,18 +199,70 @@ describe("CursorPane theme picker", () => {
200199 pointer : "data:image/png;base64,aGFuZA==" ,
201200 } ,
202201 } ) ;
203- fireEvent . click ( screen . getByRole ( "button" , { name : "Add custom cursor " } ) ) ;
202+ fireEvent . click ( screen . getByRole ( "button" , { name : "Custom 1 " } ) ) ;
204203 fireEvent . click ( screen . getByRole ( "button" , { name : "Delete custom cursor set" } ) ) ;
205204 await waitFor ( ( ) =>
206205 expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomTheme ) . toEqual ( { } ) ,
207206 ) ;
208207 expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorTheme ) . toBe ( "default" ) ;
209- expect ( screen . queryByRole ( "button" , { name : "Custom" } ) ) . toBeNull ( ) ;
208+ expect ( screen . queryByRole ( "button" , { name : "Custom 1 " } ) ) . toBeNull ( ) ;
210209 expect (
211210 within ( screen . getByRole ( "group" , { name : "Custom cursor" } ) ) . getAllByRole ( "button" ) ,
212211 ) . toHaveLength ( 3 ) ;
213212 } ) ;
214213
214+ it . each ( [
215+ "en" ,
216+ "de" ,
217+ ] ) ( "keeps five independent sets and localized stable numbers in %s" , async ( locale ) => {
218+ window . localStorage . setItem ( LOCALE_STORAGE_KEY , locale ) ;
219+ const { container } = renderWithProject ( ) ;
220+ const addLabel = locale === "de" ? "Eigenen Cursor hinzufügen" : "Add custom cursor" ;
221+ const name = ( number : number ) =>
222+ `${ locale === "de" ? "Benutzerdefiniert" : "Custom" } ${ number } ` ;
223+ const input = container . querySelector < HTMLInputElement > ( 'input[type="file"]' ) ;
224+ if ( ! input ) throw new Error ( "Missing cursor upload input" ) ;
225+ for ( let number = 1 ; number <= 5 ; number ++ ) {
226+ fireEvent . click ( screen . getByRole ( "button" , { name : addLabel } ) ) ;
227+ fireEvent . click ( screen . getByRole ( "button" , { name : locale === "de" ? "Pfeil" : "Arrow" } ) ) ;
228+ fireEvent . change ( input , { target : { files : [ new File ( [ `image-${ number } ` ] , "arrow.png" ) ] } } ) ;
229+ await waitFor ( ( ) => expect ( screen . getByRole ( "button" , { name : name ( number ) } ) ) . toBeTruthy ( ) ) ;
230+ }
231+ const original = getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomThemes ;
232+ expect ( original ) . toHaveLength ( 5 ) ;
233+ expect ( new Set ( original . map ( ( entry ) => entry . id ) ) . size ) . toBe ( 5 ) ;
234+ fireEvent . click ( screen . getByRole ( "button" , { name : name ( 2 ) } ) ) ;
235+ fireEvent . click (
236+ screen . getByRole ( "button" , {
237+ name : locale === "de" ? "Bild für Pfeil ersetzen" : "Replace Arrow image" ,
238+ } ) ,
239+ ) ;
240+ fireEvent . change ( input , { target : { files : [ new File ( [ "replacement" ] , "arrow.png" ) ] } } ) ;
241+ await waitFor ( ( ) =>
242+ expect (
243+ getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomTheme . arrow ,
244+ ) . not . toBe ( original [ 1 ] . images . arrow ) ,
245+ ) ;
246+ expect (
247+ getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomThemes . filter (
248+ ( entry ) => entry . id !== original [ 1 ] . id ,
249+ ) ,
250+ ) . toEqual ( original . filter ( ( entry ) => entry . id !== original [ 1 ] . id ) ) ;
251+ fireEvent . click (
252+ screen . getByRole ( "button" , {
253+ name :
254+ locale === "de" ? "Benutzerdefiniertes Cursor-Set löschen" : "Delete custom cursor set" ,
255+ } ) ,
256+ ) ;
257+ await waitFor ( ( ) => expect ( screen . queryByRole ( "button" , { name : name ( 2 ) } ) ) . toBeNull ( ) ) ;
258+ for ( const number of [ 1 , 3 , 4 , 5 ] )
259+ expect ( screen . getByRole ( "button" , { name : name ( number ) } ) ) . toBeTruthy ( ) ;
260+ fireEvent . click ( screen . getByRole ( "button" , { name : name ( 1 ) } ) ) ;
261+ expect ( getEditorSettings ( useProjectStore . getState ( ) . document ) . cursorCustomTheme ) . toEqual (
262+ original [ 0 ] . images ,
263+ ) ;
264+ } ) ;
265+
215266 it ( "rejects unsupported files without changing the project" , ( ) => {
216267 const error = vi . spyOn ( toast , "error" ) ;
217268 const { container } = renderWithProject ( ) ;
0 commit comments