Skip to content

Commit 632ddf0

Browse files
tests(ui): update tests for navigation api
1 parent 2b193ff commit 632ddf0

File tree

1 file changed

+131
-12
lines changed

1 file changed

+131
-12
lines changed

invokeai/frontend/web/src/features/ui/layouts/navigation-api.test.ts

Lines changed: 131 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { DockviewApi, GridviewApi } from 'dockview';
12
import { DockviewPanel, GridviewPanel } from 'dockview';
23
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
34

@@ -78,27 +79,27 @@ describe('AppNavigationApi', () => {
7879
let navigationApi: NavigationApi;
7980
let mockSetAppTab: ReturnType<typeof vi.fn>;
8081
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>;
8485
let mockAppApi: NavigationAppApi;
8586

8687
beforeEach(() => {
8788
navigationApi = new NavigationApi();
8889
mockSetAppTab = vi.fn();
8990
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();
9394
mockAppApi = {
9495
activeTab: {
9596
set: mockSetAppTab,
9697
get: mockGetAppTab,
9798
},
9899
storage: {
99-
set: mockSetPanelState,
100-
get: mockGetPanelState,
101-
delete: mockDeletePanelState,
100+
set: mockSetStorage,
101+
get: mockGetStorage,
102+
delete: mockDeleteStorage,
102103
},
103104
};
104105
});
@@ -118,9 +119,9 @@ describe('AppNavigationApi', () => {
118119
expect(navigationApi._app).not.toBeNull();
119120
expect(navigationApi._app?.activeTab.set).toBe(mockSetAppTab);
120121
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);
124125
});
125126

126127
it('should disconnect from app', () => {
@@ -985,4 +986,122 @@ describe('AppNavigationApi', () => {
985986
expect(focusResult).toBe(false);
986987
});
987988
});
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+
});
9881107
});

0 commit comments

Comments
 (0)