refactor(ext/state): lift vault_locked intercept into shared/state.ts (Plan C Phase 4)
The session-lost intercept lived in vault.ts's local sendMessage; both surfaces
now consume it through the shared sendMessage() wrapper. On a vault_locked
response to any non-bypassed request, the wrapper calls host.navigate('locked').
The vault host's navigate gains a 'locked' branch (it shows its lock screen off
state.unlocked); the popup's navigate already handles 'locked'. vault.ts routes
ctx.sendMessage through the shared wrapper and registers a plain transport as
host.sendMessage, so internal RPCs keep the intercept without recursion.
grep -c vault_locked vault.ts == 0. New state-vault-locked.test.ts (TDD, 6 cases).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
66
extension/src/shared/__tests__/state-vault-locked.test.ts
Normal file
66
extension/src/shared/__tests__/state-vault-locked.test.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { registerHost, __resetHostForTests, sendMessage } from '../state';
|
||||
import type { StateHost } from '../state';
|
||||
import type { Response } from '../messages';
|
||||
|
||||
function makeHost(response: { ok: boolean; error?: string }): StateHost {
|
||||
return {
|
||||
getState: () => ({ view: 'list' } as never),
|
||||
setState: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
sendMessage: vi.fn().mockResolvedValue(response as Response),
|
||||
escapeHtml: (s) => s,
|
||||
popOutToTab: vi.fn(),
|
||||
isInTab: () => false,
|
||||
openVaultTab: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
describe('shared/state sendMessage vault_locked intercept', () => {
|
||||
beforeEach(() => __resetHostForTests());
|
||||
|
||||
it('navigates to the lock screen on a vault_locked response', async () => {
|
||||
const host = makeHost({ ok: false, error: 'vault_locked' });
|
||||
registerHost(host);
|
||||
await sendMessage({ type: 'list_items' });
|
||||
expect(host.navigate).toHaveBeenCalledWith(
|
||||
'locked',
|
||||
expect.objectContaining({ error: expect.any(String) }),
|
||||
);
|
||||
});
|
||||
|
||||
it('does NOT intercept the unlock request itself', async () => {
|
||||
const host = makeHost({ ok: false, error: 'vault_locked' });
|
||||
registerHost(host);
|
||||
await sendMessage({ type: 'unlock', passphrase: 'x' });
|
||||
expect(host.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does NOT intercept is_unlocked', async () => {
|
||||
const host = makeHost({ ok: false, error: 'vault_locked' });
|
||||
registerHost(host);
|
||||
await sendMessage({ type: 'is_unlocked' });
|
||||
expect(host.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not intercept a successful response', async () => {
|
||||
const host = makeHost({ ok: true });
|
||||
registerHost(host);
|
||||
await sendMessage({ type: 'list_items' });
|
||||
expect(host.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not intercept a non-vault_locked error', async () => {
|
||||
const host = makeHost({ ok: false, error: 'something_else' });
|
||||
registerHost(host);
|
||||
await sendMessage({ type: 'list_items' });
|
||||
expect(host.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns the response unchanged', async () => {
|
||||
const host = makeHost({ ok: false, error: 'vault_locked' });
|
||||
registerHost(host);
|
||||
const resp = await sendMessage({ type: 'list_items' });
|
||||
expect(resp).toEqual({ ok: false, error: 'vault_locked' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user