test: add Vitest configuration and setup

- Add vitest.config.ts with jsdom environment and path aliases
- Add tests/setup.ts with mocks for Tauri API, crypto, and localStorage
- Add test:coverage script to package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-21 16:55:44 +08:00
parent 9fc17e9d36
commit 2c80a2c3c2
3 changed files with 113 additions and 0 deletions

View File

@@ -22,6 +22,7 @@
"tauri:build:msi:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug --bundles msi",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:e2e": "playwright test --project chromium --config=tests/e2e/playwright.config.ts",
"test:e2e:ui": "playwright test --project chromium-ui --config=tests/e2e/playwright.config.ts --grep 'UI'",
"test:e2e:headed": "playwright test --project chromium-headed --headed",

76
desktop/tests/setup.ts Normal file
View File

@@ -0,0 +1,76 @@
import '@testing-library/jest-dom';
import { vi } from 'vitest';
import { webcrypto } from 'node:crypto';
// Polyfill Web Crypto API for Node.js test environment
Object.defineProperty(global, 'crypto', {
value: webcrypto,
configurable: true,
});
// Mock Tauri API
vi.mock('@tauri-apps/api/core', () => ({
invoke: vi.fn(),
}));
// Mock Tauri runtime check
vi.mock('../src/lib/tauri-gateway', () => ({
isTauriRuntime: () => false,
getGatewayClient: vi.fn(),
startLocalGateway: vi.fn(),
stopLocalGateway: vi.fn(),
getLocalGatewayStatus: vi.fn(),
getLocalGatewayAuth: vi.fn(),
prepareLocalGatewayForTauri: vi.fn(),
approveLocalGatewayDevicePairing: vi.fn(),
getOpenFangProcessList: vi.fn(),
getOpenFangProcessLogs: vi.fn(),
}));
// Mock localStorage with export for test access
export const localStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem: (key: string) => store[key] || null,
setItem: (key: string, value: string) => {
store[key] = value;
},
removeItem: (key: string) => {
delete store[key];
},
clear: () => {
store = {};
},
get length() {
return Object.keys(store).length;
},
key: (index: number) => {
const keys = Object.keys(store);
return keys[index] || null;
},
};
})();
Object.defineProperty(global, 'localStorage', {
value: localStorageMock,
configurable: true,
});
// Mock crypto.subtle for tests (already set via webcrypto above, but ensure subtle mock works)
const subtleMock = {
encrypt: vi.fn(),
decrypt: vi.fn(),
generateKey: vi.fn(),
deriveKey: vi.fn(),
importKey: vi.fn(),
exportKey: vi.fn(),
digest: vi.fn(),
sign: vi.fn(),
verify: vi.fn(),
};
// Override subtle if needed for specific test scenarios
Object.defineProperty(global.crypto, 'subtle', {
value: subtleMock,
configurable: true,
});

36
desktop/vitest.config.ts Normal file
View File

@@ -0,0 +1,36 @@
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: [path.resolve(__dirname, './tests/setup.ts')],
include: ['tests/**/*.test.ts', 'tests/**/*.test.tsx'],
exclude: ['tests/e2e/**', 'node_modules/**'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/**',
'tests/**',
'**/*.d.ts',
'**/*.config.*',
'src/main.tsx',
],
thresholds: {
lines: 60,
functions: 60,
branches: 60,
statements: 60,
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});