feat(ext/setup): hand off completion to fullscreen vault tab (P2)
After successful device registration (state.configPushed = true), the wizard now opens vault.html in a new tab and closes the setup tab. Both create-new and attach-existing flows funnel through the same finishSetup() handler. Closing the setup tab is best-effort -- chrome.tabs.remove failures don't block the vault open. Add src/__stubs__/relicario_wasm.stub.ts + vitest.config alias so setup.ts can be imported in unit tests without the runtime WASM file. Exclude the stubs dir from the webpack/tsc build in tsconfig.json.
This commit is contained in:
13
extension/src/__stubs__/relicario_wasm.stub.ts
Normal file
13
extension/src/__stubs__/relicario_wasm.stub.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Stub for the runtime-only WASM module. Used by vitest so that modules
|
||||||
|
// importing relicario_wasm.js can be loaded in a Node/happy-dom environment.
|
||||||
|
// Individual tests that exercise WASM calls should mock the relevant exports.
|
||||||
|
|
||||||
|
export default async function init(): Promise<void> {}
|
||||||
|
export const unlock = (): never => { throw new Error('wasm stub: unlock not mocked'); };
|
||||||
|
export const lock = (): void => {};
|
||||||
|
export const manifest_encrypt = (): never => { throw new Error('wasm stub: manifest_encrypt not mocked'); };
|
||||||
|
export const manifest_decrypt = (): never => { throw new Error('wasm stub: manifest_decrypt not mocked'); };
|
||||||
|
export const settings_encrypt = (): never => { throw new Error('wasm stub: settings_encrypt not mocked'); };
|
||||||
|
export const default_vault_settings_json = (): string => '{}';
|
||||||
|
export const embed_image_secret = (): never => { throw new Error('wasm stub: embed_image_secret not mocked'); };
|
||||||
|
export const register_device = (): never => { throw new Error('wasm stub: register_device not mocked'); };
|
||||||
37
extension/src/setup/__tests__/setup.test.ts
Normal file
37
extension/src/setup/__tests__/setup.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { finishSetup } from '../setup';
|
||||||
|
|
||||||
|
describe('finishSetup', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(global as any).chrome = {
|
||||||
|
tabs: {
|
||||||
|
create: vi.fn(() => Promise.resolve({ id: 999 })),
|
||||||
|
getCurrent: vi.fn(() => Promise.resolve({ id: 42 })),
|
||||||
|
remove: vi.fn(() => Promise.resolve()),
|
||||||
|
},
|
||||||
|
runtime: {
|
||||||
|
getURL: vi.fn((p: string) => `chrome-extension://abc/${p}`),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens vault.html in a new tab', async () => {
|
||||||
|
await finishSetup();
|
||||||
|
expect(chrome.runtime.getURL).toHaveBeenCalledWith('vault.html');
|
||||||
|
expect(chrome.tabs.create).toHaveBeenCalledWith({
|
||||||
|
url: 'chrome-extension://abc/vault.html',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('closes the current setup tab after opening the vault tab', async () => {
|
||||||
|
await finishSetup();
|
||||||
|
expect(chrome.tabs.getCurrent).toHaveBeenCalled();
|
||||||
|
expect(chrome.tabs.remove).toHaveBeenCalledWith(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still opens the vault tab even if closing the setup tab fails', async () => {
|
||||||
|
(chrome.tabs.remove as any).mockRejectedValueOnce(new Error('no permission'));
|
||||||
|
await expect(finishSetup()).resolves.not.toThrow();
|
||||||
|
expect(chrome.tabs.create).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1101,6 +1101,7 @@ function attachStep5(): void {
|
|||||||
|
|
||||||
state.configPushed = true;
|
state.configPushed = true;
|
||||||
render();
|
render();
|
||||||
|
void finishSetup();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.error('[relicario setup] register device failed:', err);
|
console.error('[relicario setup] register device failed:', err);
|
||||||
state.error = `Failed to register device: ${err instanceof Error ? err.message : String(err)}`;
|
state.error = `Failed to register device: ${err instanceof Error ? err.message : String(err)}`;
|
||||||
@@ -1131,6 +1132,23 @@ function attachStep5(): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Completion handoff ---
|
||||||
|
|
||||||
|
/// Open the fullscreen vault tab and best-effort close the setup tab.
|
||||||
|
export async function finishSetup(): Promise<void> {
|
||||||
|
const vaultUrl = chrome.runtime.getURL('vault.html');
|
||||||
|
await chrome.tabs.create({ url: vaultUrl });
|
||||||
|
try {
|
||||||
|
const current = await chrome.tabs.getCurrent();
|
||||||
|
if (current?.id !== undefined) {
|
||||||
|
await chrome.tabs.remove(current.id);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Setup tab may not be closeable (e.g., opened as popup rather than a tab).
|
||||||
|
// The vault tab is open — that's the user-visible success.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Boot ---
|
// --- Boot ---
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|||||||
@@ -12,5 +12,5 @@
|
|||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "wasm", "src/**/__tests__/**"]
|
"exclude": ["node_modules", "dist", "wasm", "src/**/__tests__/**", "src/__stubs__/**"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
// Stub the runtime-only WASM module so unit tests can import setup.ts.
|
||||||
|
'../relicario_wasm.js': path.resolve(__dirname, 'src/__stubs__/relicario_wasm.stub.ts'),
|
||||||
|
'relicario-wasm': path.resolve(__dirname, 'src/__stubs__/relicario_wasm.stub.ts'),
|
||||||
|
},
|
||||||
|
},
|
||||||
test: {
|
test: {
|
||||||
environment: 'happy-dom',
|
environment: 'happy-dom',
|
||||||
include: ['src/**/__tests__/**/*.test.ts'],
|
include: ['src/**/__tests__/**/*.test.ts'],
|
||||||
|
|||||||
Reference in New Issue
Block a user