Add Vitest setup and tests for encryption

- Set up Vitest with testing-library and jsdom for frontend tests - Add
encryption.test.ts to verify deriveKey, wrapMasterKey, unwrapMasterKey -
Add test/setup.ts to extend jest-dom matchers and cleanup after tests -
Enable Vitest in Vite config and add test scripts in
frontend/package.json
This commit is contained in:
james fitzsimons 2025-12-13 12:15:14 +00:00
parent b596c9f34d
commit 2eb924dc9c
5 changed files with 1932 additions and 2 deletions

File diff suppressed because it is too large Load diff

View file

@ -29,10 +29,21 @@
}, },
"devDependencies": { "devDependencies": {
"@catppuccin/tailwindcss": "^1.0.0", "@catppuccin/tailwindcss": "^1.0.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.6", "@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^4.7.0",
"@vitest/ui": "^4.0.15",
"jsdom": "^27.3.0",
"vite": "^5.4.21", "vite": "^5.4.21",
"vite-plugin-svgr": "^4.5.0" "vite-plugin-svgr": "^4.5.0",
"vitest": "^4.0.15"
},
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
} }
} }

View file

@ -0,0 +1,66 @@
// src/api/encryption.test.ts
import { describe, it, expect } from "vitest";
import {
deriveKey,
wrapMasterKey,
unwrapMasterKey,
generateMasterKey,
} from "./encryption";
describe("Encryption", () => {
it("should derive consistent keys from same password and salt", async () => {
const password = "testPassword123";
const salt = "test-salt";
const key1 = await deriveKey(password, salt);
const key2 = await deriveKey(password, salt);
const testMessage = "test data";
const testData = new TextEncoder().encode(testMessage);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key1,
testData,
);
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
key2,
encrypted,
);
const decryptedMessage = new TextDecoder().decode(decrypted);
expect(decryptedMessage).toBe(testMessage);
});
it("should wrap and unwrap master key correctly", async () => {
const masterKey = await generateMasterKey();
const password = "testPassword123";
const salt = "test-salt";
const kek = await deriveKey(password, salt);
const wrapped = await wrapMasterKey(masterKey, kek);
const unwrapped = await unwrapMasterKey(wrapped, kek);
const testMessage = "test message";
const testData = new TextEncoder().encode(testMessage);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
masterKey,
testData,
);
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
unwrapped,
encrypted,
);
const decryptedMessage = new TextDecoder().decode(decrypted);
expect(decryptedMessage).toBe(testMessage);
});
});

View file

@ -0,0 +1,11 @@
// src/test/setup.ts
import { expect, afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
import * as matchers from "@testing-library/jest-dom/matchers";
expect.extend(matchers);
// Cleanup after each test
afterEach(() => {
cleanup();
});

View file

@ -6,6 +6,11 @@ import path from "path";
export default defineConfig({ export default defineConfig({
plugins: [tailwindcss(), react(), svgr()], plugins: [tailwindcss(), react(), svgr()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/test/setup.ts",
},
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),