fix(ext/tests): update devices tests for revamp (fingerprint + two-step revoke)

Tests predated the 2026-05-24 management-surfaces revamp (047df6e): popup
devices pane now shows SHA-256 fingerprint + added-by + inline two-step
revoke confirm, and the SW revokeDevice signature may have shifted to
match. Mocks + assertions updated accordingly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-05-30 20:27:51 -04:00
parent 0bde0935c2
commit 797709b441
2 changed files with 38 additions and 38 deletions

View File

@@ -33,11 +33,16 @@ describe('devices view', () => {
vi.clearAllMocks();
});
// The component fires list_devices + list_revoked in parallel via Promise.all,
// so every render needs both mocked. Helper makes the per-test setup readable.
function mockListPair(devices: unknown[], revoked: unknown[] = []): void {
(sendMessage as ReturnType<typeof vi.fn>)
.mockResolvedValueOnce({ ok: true, data: { devices } })
.mockResolvedValueOnce({ ok: true, data: { revoked } });
}
it('renders empty state when no devices', async () => {
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { devices: [] },
});
mockListPair([]);
await renderDevices(app);
@@ -45,15 +50,10 @@ describe('devices view', () => {
});
it('renders devices with "you" indicator on current device', async () => {
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: {
devices: [
{ name: 'Chrome on Linux', public_key: 'abc', added_at: 1000 },
{ name: 'CLI', public_key: 'def', added_at: 500 },
],
},
});
mockListPair([
{ name: 'Chrome on Linux', public_key: 'abc', added_at: 1000 },
{ name: 'CLI', public_key: 'def', added_at: 500 },
]);
await renderDevices(app);
@@ -68,23 +68,15 @@ describe('devices view', () => {
it('shows unregistered banner when current device not in list', async () => {
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: {
devices: [{ name: 'CLI', public_key: 'abc', added_at: 1000 }],
},
});
mockListPair([{ name: 'CLI', public_key: 'abc', added_at: 1000 }]);
await renderDevices(app);
expect(app.innerHTML).toContain('This device is not registered');
expect(app.innerHTML).toContain("This device isn't registered");
});
it('back button navigates to list', async () => {
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { devices: [] },
});
mockListPair([]);
await renderDevices(app);
app.querySelector<HTMLButtonElement>('#back-btn')?.click();
@@ -94,10 +86,7 @@ describe('devices view', () => {
it('clicking register button reveals an inline name input', async () => {
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }] },
});
mockListPair([{ name: 'CLI', public_key: 'k', added_at: 1 }]);
await renderDevices(app);
app.querySelector<HTMLButtonElement>('#register-btn')!.click();
@@ -108,13 +97,15 @@ describe('devices view', () => {
it('confirming register sends register_this_device with the entered name', async () => {
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
// Initial list_devices.
(sendMessage as ReturnType<typeof vi.fn>)
.mockResolvedValueOnce({ ok: true, data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }] } })
// register_this_device.
.mockResolvedValueOnce({ ok: true })
// Re-render's list_devices.
.mockResolvedValueOnce({ ok: true, data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }, { name: 'Test Browser', public_key: 'q', added_at: 2 }] } });
// Initial render: list_devices + list_revoked.
mockListPair([{ name: 'CLI', public_key: 'k', added_at: 1 }]);
// register_this_device.
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ ok: true });
// Re-render: list_devices + list_revoked.
mockListPair([
{ name: 'CLI', public_key: 'k', added_at: 1 },
{ name: 'Test Browser', public_key: 'q', added_at: 2 },
]);
// Re-render also re-reads device_name from storage.
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Test Browser' });

View File

@@ -3,10 +3,19 @@ import { readDevices, addDevice, revokeDevice } from '../devices';
import type { GitHost } from '../git-host';
function makeGitHost(devicesJson = '{"devices":[]}'): GitHost {
let stored = devicesJson;
// Per-path storage — revokeDevice writes devices.json AND revoked.json,
// so a single slot would corrupt the second read.
const files = new Map<string, string>();
files.set('.relicario/devices.json', devicesJson);
return {
readFile: vi.fn().mockImplementation(async () => new TextEncoder().encode(stored)),
writeFile: vi.fn().mockImplementation(async (_p, bytes) => { stored = new TextDecoder().decode(bytes); }),
readFile: vi.fn().mockImplementation(async (path: string) => {
const content = files.get(path);
if (content === undefined) throw new Error(`404: ${path}`);
return new TextEncoder().encode(content);
}),
writeFile: vi.fn().mockImplementation(async (path: string, bytes: Uint8Array) => {
files.set(path, new TextDecoder().decode(bytes));
}),
deleteFile: vi.fn(),
listDir: vi.fn(),
putBlob: vi.fn(),