Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
75
frontend/tests/quotaUiState.test.ts
Normal file
75
frontend/tests/quotaUiState.test.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Session-scoped quota page preferences.
|
||||
*
|
||||
* The merge-on-write case is the one that matters: the tab strip and the sort
|
||||
* control each write a single field and know nothing about the other, so a
|
||||
* whole-object write would make changing a tab silently reset the sort.
|
||||
*/
|
||||
|
||||
import { afterAll, beforeEach, describe, expect, test } from 'vitest';
|
||||
import { readQuotaUiState, writeQuotaUiState } from '@/features/quota/uiState';
|
||||
|
||||
const KEY = 'quotaPage.uiState';
|
||||
|
||||
/** Test files share one process — leaving a fake `window` behind would leak. */
|
||||
const originalWindow = (globalThis as { window?: unknown }).window;
|
||||
|
||||
/** The Node test environment has no sessionStorage; a Map-backed stub is enough here. */
|
||||
function installSessionStorage() {
|
||||
const store = new Map<string, string>();
|
||||
const storage = {
|
||||
getItem: (key: string) => store.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => void store.set(key, value),
|
||||
removeItem: (key: string) => void store.delete(key),
|
||||
clear: () => store.clear(),
|
||||
key: (index: number) => [...store.keys()][index] ?? null,
|
||||
get length() {
|
||||
return store.size;
|
||||
},
|
||||
};
|
||||
(globalThis as unknown as { window: unknown }).window = { sessionStorage: storage };
|
||||
return storage;
|
||||
}
|
||||
|
||||
let storage: ReturnType<typeof installSessionStorage>;
|
||||
|
||||
beforeEach(() => {
|
||||
storage = installSessionStorage();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (originalWindow === undefined) {
|
||||
delete (globalThis as { window?: unknown }).window;
|
||||
} else {
|
||||
(globalThis as { window?: unknown }).window = originalWindow;
|
||||
}
|
||||
});
|
||||
|
||||
describe('quota ui state', () => {
|
||||
test('round-trips both preferences', () => {
|
||||
writeQuotaUiState({ tab: 'codex', sortMode: 'soonest' });
|
||||
expect(readQuotaUiState()).toEqual({ tab: 'codex', sortMode: 'soonest' });
|
||||
});
|
||||
|
||||
test('writing one preference preserves the other', () => {
|
||||
writeQuotaUiState({ sortMode: 'soonest' });
|
||||
writeQuotaUiState({ tab: 'kimi' });
|
||||
|
||||
expect(readQuotaUiState()).toEqual({ tab: 'kimi', sortMode: 'soonest' });
|
||||
});
|
||||
|
||||
test('rejects values that are not part of the current contract', () => {
|
||||
storage.setItem(KEY, JSON.stringify({ tab: 'not-a-tab', sortMode: 'by-vibes' }));
|
||||
expect(readQuotaUiState()).toEqual({ tab: undefined, sortMode: undefined });
|
||||
});
|
||||
|
||||
test('survives absent, malformed, and non-object payloads', () => {
|
||||
expect(readQuotaUiState()).toBeNull();
|
||||
|
||||
storage.setItem(KEY, '{not json');
|
||||
expect(readQuotaUiState()).toBeNull();
|
||||
|
||||
storage.setItem(KEY, '"a string"');
|
||||
expect(readQuotaUiState()).toBeNull();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue