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:
@@ -33,11 +33,16 @@ describe('devices view', () => {
|
|||||||
vi.clearAllMocks();
|
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 () => {
|
it('renders empty state when no devices', async () => {
|
||||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
mockListPair([]);
|
||||||
ok: true,
|
|
||||||
data: { devices: [] },
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderDevices(app);
|
await renderDevices(app);
|
||||||
|
|
||||||
@@ -45,15 +50,10 @@ describe('devices view', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders devices with "you" indicator on current device', async () => {
|
it('renders devices with "you" indicator on current device', async () => {
|
||||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
mockListPair([
|
||||||
ok: true,
|
{ name: 'Chrome on Linux', public_key: 'abc', added_at: 1000 },
|
||||||
data: {
|
{ name: 'CLI', public_key: 'def', added_at: 500 },
|
||||||
devices: [
|
]);
|
||||||
{ name: 'Chrome on Linux', public_key: 'abc', added_at: 1000 },
|
|
||||||
{ name: 'CLI', public_key: 'def', added_at: 500 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderDevices(app);
|
await renderDevices(app);
|
||||||
|
|
||||||
@@ -68,23 +68,15 @@ describe('devices view', () => {
|
|||||||
|
|
||||||
it('shows unregistered banner when current device not in list', async () => {
|
it('shows unregistered banner when current device not in list', async () => {
|
||||||
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
|
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
|
||||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
mockListPair([{ name: 'CLI', public_key: 'abc', added_at: 1000 }]);
|
||||||
ok: true,
|
|
||||||
data: {
|
|
||||||
devices: [{ name: 'CLI', public_key: 'abc', added_at: 1000 }],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderDevices(app);
|
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 () => {
|
it('back button navigates to list', async () => {
|
||||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
mockListPair([]);
|
||||||
ok: true,
|
|
||||||
data: { devices: [] },
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderDevices(app);
|
await renderDevices(app);
|
||||||
app.querySelector<HTMLButtonElement>('#back-btn')?.click();
|
app.querySelector<HTMLButtonElement>('#back-btn')?.click();
|
||||||
@@ -94,10 +86,7 @@ describe('devices view', () => {
|
|||||||
|
|
||||||
it('clicking register button reveals an inline name input', async () => {
|
it('clicking register button reveals an inline name input', async () => {
|
||||||
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
|
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
|
||||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
mockListPair([{ name: 'CLI', public_key: 'k', added_at: 1 }]);
|
||||||
ok: true,
|
|
||||||
data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }] },
|
|
||||||
});
|
|
||||||
|
|
||||||
await renderDevices(app);
|
await renderDevices(app);
|
||||||
app.querySelector<HTMLButtonElement>('#register-btn')!.click();
|
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 () => {
|
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' });
|
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Unknown' });
|
||||||
// Initial list_devices.
|
// Initial render: list_devices + list_revoked.
|
||||||
(sendMessage as ReturnType<typeof vi.fn>)
|
mockListPair([{ name: 'CLI', public_key: 'k', added_at: 1 }]);
|
||||||
.mockResolvedValueOnce({ ok: true, data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }] } })
|
// register_this_device.
|
||||||
// register_this_device.
|
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ ok: true });
|
||||||
.mockResolvedValueOnce({ ok: true })
|
// Re-render: list_devices + list_revoked.
|
||||||
// Re-render's list_devices.
|
mockListPair([
|
||||||
.mockResolvedValueOnce({ ok: true, data: { devices: [{ name: 'CLI', public_key: 'k', added_at: 1 }, { name: 'Test Browser', public_key: 'q', added_at: 2 }] } });
|
{ 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.
|
// Re-render also re-reads device_name from storage.
|
||||||
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Test Browser' });
|
(chrome.storage.local.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ device_name: 'Test Browser' });
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,19 @@ import { readDevices, addDevice, revokeDevice } from '../devices';
|
|||||||
import type { GitHost } from '../git-host';
|
import type { GitHost } from '../git-host';
|
||||||
|
|
||||||
function makeGitHost(devicesJson = '{"devices":[]}'): GitHost {
|
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 {
|
return {
|
||||||
readFile: vi.fn().mockImplementation(async () => new TextEncoder().encode(stored)),
|
readFile: vi.fn().mockImplementation(async (path: string) => {
|
||||||
writeFile: vi.fn().mockImplementation(async (_p, bytes) => { stored = new TextDecoder().decode(bytes); }),
|
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(),
|
deleteFile: vi.fn(),
|
||||||
listDir: vi.fn(),
|
listDir: vi.fn(),
|
||||||
putBlob: vi.fn(),
|
putBlob: vi.fn(),
|
||||||
|
|||||||
Reference in New Issue
Block a user