@@ -58,11 +58,118 @@ describe("uiStateStore pure functions", () => {
5858 projectOrder : [ project1 , project2 , project3 ] ,
5959 } ) ;
6060
61- const next = reorderProjects ( initialState , project1 , project3 ) ;
61+ const next = reorderProjects ( initialState , [ project1 ] , [ project3 ] ) ;
6262
6363 expect ( next . projectOrder ) . toEqual ( [ project2 , project3 , project1 ] ) ;
6464 } ) ;
6565
66+ it ( "reorderProjects is a no-op when dragged key is not in projectOrder" , ( ) => {
67+ const project1 = ProjectId . make ( "project-1" ) ;
68+ const project2 = ProjectId . make ( "project-2" ) ;
69+ const initialState = makeUiState ( {
70+ projectOrder : [ project1 , project2 ] ,
71+ } ) ;
72+
73+ const next = reorderProjects ( initialState , [ ProjectId . make ( "missing" ) ] , [ project2 ] ) ;
74+
75+ expect ( next ) . toBe ( initialState ) ;
76+ } ) ;
77+
78+ it ( "reorderProjects moves all member keys of a multi-member group together" , ( ) => {
79+ const keyALocal = "env-local:proj-a" ;
80+ const keyARemote = "env-remote:proj-a" ;
81+ const keyB = "env-local:proj-b" ;
82+ const keyC = "env-local:proj-c" ;
83+ const initialState = makeUiState ( {
84+ projectOrder : [ keyALocal , keyARemote , keyB , keyC ] ,
85+ } ) ;
86+
87+ const next = reorderProjects ( initialState , [ keyALocal , keyARemote ] , [ keyC ] ) ;
88+
89+ expect ( next . projectOrder ) . toEqual ( [ keyB , keyC , keyALocal , keyARemote ] ) ;
90+ } ) ;
91+
92+ it ( "reorderProjects handles member keys scattered across projectOrder" , ( ) => {
93+ const keyALocal = "env-local:proj-a" ;
94+ const keyB = "env-local:proj-b" ;
95+ const keyARemote = "env-remote:proj-a" ;
96+ const keyC = "env-local:proj-c" ;
97+ const initialState = makeUiState ( {
98+ projectOrder : [ keyALocal , keyB , keyARemote , keyC ] ,
99+ } ) ;
100+
101+ const next = reorderProjects ( initialState , [ keyALocal , keyARemote ] , [ keyC ] ) ;
102+
103+ expect ( next . projectOrder ) . toEqual ( [ keyB , keyC , keyALocal , keyARemote ] ) ;
104+ } ) ;
105+
106+ it ( "reorderProjects places group after target when dragged from before a non-last target" , ( ) => {
107+ const keyALocal = "env-local:proj-a" ;
108+ const keyARemote = "env-remote:proj-a" ;
109+ const keyB = "env-local:proj-b" ;
110+ const keyC = "env-local:proj-c" ;
111+ const keyD = "env-local:proj-d" ;
112+ const initialState = makeUiState ( {
113+ projectOrder : [ keyALocal , keyARemote , keyB , keyC , keyD ] ,
114+ } ) ;
115+
116+ const next = reorderProjects ( initialState , [ keyALocal , keyARemote ] , [ keyC ] ) ;
117+
118+ expect ( next . projectOrder ) . toEqual ( [ keyB , keyC , keyALocal , keyARemote , keyD ] ) ;
119+ } ) ;
120+
121+ it ( "reorderProjects places group before target when dragged from after" , ( ) => {
122+ const keyB = "env-local:proj-b" ;
123+ const keyC = "env-local:proj-c" ;
124+ const keyALocal = "env-local:proj-a" ;
125+ const keyARemote = "env-remote:proj-a" ;
126+ const initialState = makeUiState ( {
127+ projectOrder : [ keyB , keyC , keyALocal , keyARemote ] ,
128+ } ) ;
129+
130+ const next = reorderProjects ( initialState , [ keyALocal , keyARemote ] , [ keyB ] ) ;
131+
132+ expect ( next . projectOrder ) . toEqual ( [ keyALocal , keyARemote , keyB , keyC ] ) ;
133+ } ) ;
134+
135+ it ( "reorderProjects with multi-member target inserts after first target occurrence" , ( ) => {
136+ const keyALocal = "env-local:proj-a" ;
137+ const keyARemote = "env-remote:proj-a" ;
138+ const keyBLocal = "env-local:proj-b" ;
139+ const keyBRemote = "env-remote:proj-b" ;
140+ const initialState = makeUiState ( {
141+ projectOrder : [ keyALocal , keyARemote , keyBLocal , keyBRemote ] ,
142+ } ) ;
143+
144+ const next = reorderProjects ( initialState , [ keyALocal , keyARemote ] , [ keyBLocal , keyBRemote ] ) ;
145+
146+ // Target members may become non-contiguous; this is fine because the
147+ // sidebar groups by logical key using first-occurrence positioning.
148+ expect ( next . projectOrder ) . toEqual ( [ keyBLocal , keyALocal , keyARemote , keyBRemote ] ) ;
149+ } ) ;
150+
151+ it ( "reorderProjects is a no-op when dragged group equals target group" , ( ) => {
152+ const key1 = "env-local:proj-a" ;
153+ const key2 = "env-remote:proj-a" ;
154+ const initialState = makeUiState ( {
155+ projectOrder : [ key1 , key2 , "env-local:proj-b" ] ,
156+ } ) ;
157+
158+ const next = reorderProjects ( initialState , [ key1 , key2 ] , [ key1 , key2 ] ) ;
159+
160+ expect ( next ) . toBe ( initialState ) ;
161+ } ) ;
162+
163+ it ( "reorderProjects is a no-op when dragged keys are not in projectOrder" , ( ) => {
164+ const initialState = makeUiState ( {
165+ projectOrder : [ "env-local:proj-a" , "env-local:proj-b" ] ,
166+ } ) ;
167+
168+ const next = reorderProjects ( initialState , [ "env-local:missing" ] , [ "env-local:proj-b" ] ) ;
169+
170+ expect ( next ) . toBe ( initialState ) ;
171+ } ) ;
172+
66173 it ( "syncProjects preserves current project order during snapshot recovery" , ( ) => {
67174 const project1 = ProjectId . make ( "project-1" ) ;
68175 const project2 = ProjectId . make ( "project-2" ) ;
0 commit comments