1
+ import type { DockviewApi , GridviewApi } from 'dockview' ;
1
2
import { DockviewPanel , GridviewPanel } from 'dockview' ;
2
3
import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest' ;
3
4
@@ -78,27 +79,27 @@ describe('AppNavigationApi', () => {
78
79
let navigationApi : NavigationApi ;
79
80
let mockSetAppTab : ReturnType < typeof vi . fn > ;
80
81
let mockGetAppTab : ReturnType < typeof vi . fn > ;
81
- let mockSetPanelState : ReturnType < typeof vi . fn > ;
82
- let mockGetPanelState : ReturnType < typeof vi . fn > ;
83
- let mockDeletePanelState : ReturnType < typeof vi . fn > ;
82
+ let mockSetStorage : ReturnType < typeof vi . fn > ;
83
+ let mockGetStorage : ReturnType < typeof vi . fn > ;
84
+ let mockDeleteStorage : ReturnType < typeof vi . fn > ;
84
85
let mockAppApi : NavigationAppApi ;
85
86
86
87
beforeEach ( ( ) => {
87
88
navigationApi = new NavigationApi ( ) ;
88
89
mockSetAppTab = vi . fn ( ) ;
89
90
mockGetAppTab = vi . fn ( ) ;
90
- mockSetPanelState = vi . fn ( ) ;
91
- mockGetPanelState = vi . fn ( ) ;
92
- mockDeletePanelState = vi . fn ( ) ;
91
+ mockSetStorage = vi . fn ( ) ;
92
+ mockGetStorage = vi . fn ( ) ;
93
+ mockDeleteStorage = vi . fn ( ) ;
93
94
mockAppApi = {
94
95
activeTab : {
95
96
set : mockSetAppTab ,
96
97
get : mockGetAppTab ,
97
98
} ,
98
99
storage : {
99
- set : mockSetPanelState ,
100
- get : mockGetPanelState ,
101
- delete : mockDeletePanelState ,
100
+ set : mockSetStorage ,
101
+ get : mockGetStorage ,
102
+ delete : mockDeleteStorage ,
102
103
} ,
103
104
} ;
104
105
} ) ;
@@ -118,9 +119,9 @@ describe('AppNavigationApi', () => {
118
119
expect ( navigationApi . _app ) . not . toBeNull ( ) ;
119
120
expect ( navigationApi . _app ?. activeTab . set ) . toBe ( mockSetAppTab ) ;
120
121
expect ( navigationApi . _app ?. activeTab . get ) . toBe ( mockGetAppTab ) ;
121
- expect ( navigationApi . _app ?. storage . set ) . toBe ( mockSetPanelState ) ;
122
- expect ( navigationApi . _app ?. storage . get ) . toBe ( mockGetPanelState ) ;
123
- expect ( navigationApi . _app ?. storage . delete ) . toBe ( mockDeletePanelState ) ;
122
+ expect ( navigationApi . _app ?. storage . set ) . toBe ( mockSetStorage ) ;
123
+ expect ( navigationApi . _app ?. storage . get ) . toBe ( mockGetStorage ) ;
124
+ expect ( navigationApi . _app ?. storage . delete ) . toBe ( mockDeleteStorage ) ;
124
125
} ) ;
125
126
126
127
it ( 'should disconnect from app' , ( ) => {
@@ -985,4 +986,122 @@ describe('AppNavigationApi', () => {
985
986
expect ( focusResult ) . toBe ( false ) ;
986
987
} ) ;
987
988
} ) ;
989
+
990
+ describe ( 'registerContainer' , ( ) => {
991
+ const tab = 'generate' ;
992
+ const viewId = 'myView' ;
993
+ const key = `${ tab } :container:${ viewId } ` ;
994
+
995
+ beforeEach ( ( ) => {
996
+ navigationApi = new NavigationApi ( ) ;
997
+ navigationApi . connectToApp ( mockAppApi ) ;
998
+ } ) ;
999
+
1000
+ it ( 'initializes from scratch when no stored state' , ( ) => {
1001
+ mockGetStorage . mockReturnValue ( undefined ) ;
1002
+ const initialize = vi . fn ( ) ;
1003
+ const panel1 = { id : 'p1' } ;
1004
+ const panel2 = { id : 'p2' } ;
1005
+ const mockApi = {
1006
+ panels : [ panel1 , panel2 ] ,
1007
+ toJSON : vi . fn ( ( ) => ( { foo : 'bar' } ) ) ,
1008
+ onDidLayoutChange : vi . fn ( ( ) => ( { dispose : vi . fn ( ) } ) ) ,
1009
+ } as unknown as DockviewApi | GridviewApi ;
1010
+ navigationApi . registerContainer ( tab , viewId , mockApi , initialize ) ;
1011
+
1012
+ expect ( initialize ) . toHaveBeenCalledOnce ( ) ;
1013
+ expect ( mockSetStorage ) . toHaveBeenCalledOnce ( ) ;
1014
+ expect ( mockSetStorage ) . toHaveBeenCalledWith ( key , { foo : 'bar' } ) ;
1015
+ // panels registered
1016
+ expect ( navigationApi . isPanelRegistered ( tab , 'p1' ) ) . toBe ( true ) ;
1017
+ expect ( navigationApi . isPanelRegistered ( tab , 'p2' ) ) . toBe ( true ) ;
1018
+ } ) ;
1019
+
1020
+ it ( 'restores from storage when fromJSON succeeds' , ( ) => {
1021
+ const stored = { saved : true } ;
1022
+ mockGetStorage . mockReturnValue ( stored ) ;
1023
+ const initialize = vi . fn ( ) ;
1024
+ const panel = { id : 'p' } ;
1025
+ const mockApi = {
1026
+ panels : [ panel ] ,
1027
+ fromJSON : vi . fn ( ) ,
1028
+ toJSON : vi . fn ( ) ,
1029
+ onDidLayoutChange : vi . fn ( ( ) => ( { dispose : vi . fn ( ) } ) ) ,
1030
+ } as unknown as DockviewApi | GridviewApi ;
1031
+ navigationApi . registerContainer ( tab , viewId , mockApi , initialize ) ;
1032
+
1033
+ expect ( mockApi . fromJSON ) . toHaveBeenCalledWith ( stored ) ;
1034
+ expect ( initialize ) . not . toHaveBeenCalled ( ) ;
1035
+ expect ( mockSetStorage ) . not . toHaveBeenCalled ( ) ; // no initial persist
1036
+ expect ( navigationApi . isPanelRegistered ( tab , 'p' ) ) . toBe ( true ) ;
1037
+ } ) ;
1038
+
1039
+ it ( 're-initializes when fromJSON throws, deletes then sets' , ( ) => {
1040
+ const stored = { saved : true } ;
1041
+ mockGetStorage . mockReturnValue ( stored ) ;
1042
+ const initialize = vi . fn ( ) ;
1043
+ const panel = { id : 'p' } ;
1044
+ const mockApi = {
1045
+ panels : [ panel ] ,
1046
+ fromJSON : vi . fn ( ( ) => {
1047
+ throw new Error ( 'bad' ) ;
1048
+ } ) ,
1049
+ toJSON : vi . fn ( ( ) => ( { new : 'state' } ) ) ,
1050
+ onDidLayoutChange : vi . fn ( ( ) => ( { dispose : vi . fn ( ) } ) ) ,
1051
+ } as unknown as DockviewApi | GridviewApi ;
1052
+ navigationApi . registerContainer ( tab , viewId , mockApi , initialize ) ;
1053
+
1054
+ expect ( mockApi . fromJSON ) . toHaveBeenCalledWith ( stored ) ;
1055
+ expect ( mockDeleteStorage ) . toHaveBeenCalledOnce ( ) ;
1056
+ expect ( mockDeleteStorage ) . toHaveBeenCalledWith ( key ) ;
1057
+ expect ( initialize ) . toHaveBeenCalledOnce ( ) ;
1058
+ expect ( mockSetStorage ) . toHaveBeenCalledOnce ( ) ;
1059
+ expect ( mockSetStorage ) . toHaveBeenCalledWith ( key , { new : 'state' } ) ;
1060
+ expect ( navigationApi . isPanelRegistered ( tab , 'p' ) ) . toBe ( true ) ;
1061
+ } ) ;
1062
+
1063
+ it ( 'persists on layout change after debounce' , ( ) => {
1064
+ vi . useFakeTimers ( ) ;
1065
+ mockGetStorage . mockReturnValue ( undefined ) ;
1066
+ const initialize = vi . fn ( ) ;
1067
+ const panel = { id : 'p' } ;
1068
+ let layoutCb : ( ) => void = ( ) => { } ;
1069
+ const mockApi = {
1070
+ panels : [ panel ] ,
1071
+ toJSON : vi . fn ( ( ) => ( { x : 1 } ) ) ,
1072
+ onDidLayoutChange : vi . fn ( ( cb ) => {
1073
+ layoutCb = cb ;
1074
+ return { dispose : vi . fn ( ) } ;
1075
+ } ) ,
1076
+ } as unknown as DockviewApi | GridviewApi ;
1077
+ navigationApi . registerContainer ( tab , viewId , mockApi , initialize ) ;
1078
+
1079
+ // first set: initial persistence
1080
+ expect ( mockSetStorage ) . toHaveBeenCalledWith ( key , { x : 1 } ) ;
1081
+
1082
+ // simulate layout change
1083
+ layoutCb ( ) ;
1084
+ // advance past debounce (300ms)
1085
+ vi . advanceTimersByTime ( 300 ) ;
1086
+
1087
+ expect ( mockSetStorage ) . toHaveBeenCalledTimes ( 2 ) ;
1088
+ expect ( mockSetStorage ) . toHaveBeenLastCalledWith ( key , { x : 1 } ) ;
1089
+
1090
+ vi . useRealTimers ( ) ;
1091
+ } ) ;
1092
+
1093
+ it ( 'does nothing if app not connected' , ( ) => {
1094
+ navigationApi . disconnectFromApp ( ) ;
1095
+ const initialize = vi . fn ( ) ;
1096
+ const mockApi = {
1097
+ panels : [ ] ,
1098
+ fromJSON : vi . fn ( ) ,
1099
+ toJSON : vi . fn ( ) ,
1100
+ onDidLayoutChange : vi . fn ( ) ,
1101
+ } as unknown as DockviewApi | GridviewApi ;
1102
+ expect ( ( ) => navigationApi . registerContainer ( tab , viewId , mockApi , initialize ) ) . not . toThrow ( ) ;
1103
+ expect ( mockGetStorage ) . not . toHaveBeenCalled ( ) ;
1104
+ expect ( initialize ) . not . toHaveBeenCalled ( ) ;
1105
+ } ) ;
1106
+ } ) ;
988
1107
} ) ;
0 commit comments