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