test(ext/shared): cover StateHost registration + reset (Plan C Phase 1)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
92
extension/src/shared/__tests__/state.test.ts
Normal file
92
extension/src/shared/__tests__/state.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
registerHost,
|
||||
__resetHostForTests,
|
||||
getState,
|
||||
setState,
|
||||
navigate,
|
||||
sendMessage,
|
||||
} from '../state';
|
||||
import type { StateHost } from '../state';
|
||||
import type { PopupState } from '../popup-state';
|
||||
|
||||
function makeHost(initial?: Partial<PopupState>): StateHost {
|
||||
let state: PopupState = {
|
||||
view: 'list',
|
||||
entries: [],
|
||||
selectedId: null,
|
||||
selectedItem: null,
|
||||
selectedIndex: 0,
|
||||
searchQuery: '',
|
||||
activeGroup: null,
|
||||
error: null,
|
||||
loading: false,
|
||||
capturedTabId: null,
|
||||
capturedUrl: '',
|
||||
newType: null,
|
||||
vaultSettings: null,
|
||||
generatorDefaults: null,
|
||||
historyItemId: null,
|
||||
...initial,
|
||||
};
|
||||
|
||||
return {
|
||||
getState: () => state,
|
||||
setState: (partial) => { state = { ...state, ...partial }; },
|
||||
navigate: vi.fn(),
|
||||
sendMessage: vi.fn().mockResolvedValue({ ok: true }),
|
||||
escapeHtml: (s) => s,
|
||||
popOutToTab: vi.fn(),
|
||||
isInTab: () => false,
|
||||
openVaultTab: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
describe('shared/state', () => {
|
||||
beforeEach(() => {
|
||||
__resetHostForTests();
|
||||
});
|
||||
|
||||
it('register-then-getState round-trips', () => {
|
||||
const host = makeHost({ view: 'detail' });
|
||||
registerHost(host);
|
||||
expect(getState().view).toBe('detail');
|
||||
});
|
||||
|
||||
it('double-register throws', () => {
|
||||
registerHost(makeHost());
|
||||
expect(() => registerHost(makeHost())).toThrow(/already registered/);
|
||||
});
|
||||
|
||||
it('__resetHostForTests clears the singleton', () => {
|
||||
registerHost(makeHost());
|
||||
__resetHostForTests();
|
||||
expect(() => getState()).toThrow(/No state host/);
|
||||
});
|
||||
|
||||
it('getState without host throws', () => {
|
||||
expect(() => getState()).toThrow(/No state host/);
|
||||
});
|
||||
|
||||
it('setState merges partial state', () => {
|
||||
const host = makeHost();
|
||||
registerHost(host);
|
||||
setState({ loading: true });
|
||||
expect(getState().loading).toBe(true);
|
||||
});
|
||||
|
||||
it('navigate delegates to host', () => {
|
||||
const host = makeHost();
|
||||
registerHost(host);
|
||||
navigate('settings');
|
||||
expect(host.navigate).toHaveBeenCalledWith('settings', undefined);
|
||||
});
|
||||
|
||||
it('sendMessage delegates to host', async () => {
|
||||
const host = makeHost();
|
||||
registerHost(host);
|
||||
const resp = await sendMessage({ type: 'is_unlocked' });
|
||||
expect(host.sendMessage).toHaveBeenCalledWith({ type: 'is_unlocked' });
|
||||
expect(resp).toEqual({ ok: true });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user