generated from nhcarrigan/template
feat: add tests and assert coverage (#71)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Co-authored-by: Hikari <hikari@nhcarrigan.com> Reviewed-on: #71 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #71.
This commit is contained in:
+287
@@ -1 +1,288 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { vi, beforeEach } from "vitest";
|
||||
|
||||
// Mock Tauri invoke API
|
||||
const mockInvokeResults: Record<string, unknown> = {};
|
||||
|
||||
export function setMockInvokeResult(command: string, result: unknown) {
|
||||
mockInvokeResults[command] = result;
|
||||
}
|
||||
|
||||
export function clearMockInvokeResults() {
|
||||
Object.keys(mockInvokeResults).forEach((key) => delete mockInvokeResults[key]);
|
||||
}
|
||||
|
||||
vi.mock("@tauri-apps/api/core", () => ({
|
||||
invoke: vi.fn((command: string) => {
|
||||
if (command in mockInvokeResults) {
|
||||
const result = mockInvokeResults[command];
|
||||
if (result instanceof Error) {
|
||||
return Promise.reject(result);
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
// Default return values for common commands
|
||||
switch (command) {
|
||||
case "get_config":
|
||||
return Promise.resolve({
|
||||
model: null,
|
||||
api_key: null,
|
||||
custom_instructions: null,
|
||||
mcp_servers_json: null,
|
||||
auto_granted_tools: [],
|
||||
theme: "dark",
|
||||
greeting_enabled: true,
|
||||
greeting_custom_prompt: null,
|
||||
notifications_enabled: true,
|
||||
notification_volume: 0.7,
|
||||
always_on_top: false,
|
||||
update_checks_enabled: true,
|
||||
character_panel_width: null,
|
||||
font_size: 14,
|
||||
minimize_to_tray: false,
|
||||
streamer_mode: false,
|
||||
streamer_hide_paths: false,
|
||||
compact_mode: false,
|
||||
profile_name: null,
|
||||
profile_avatar_path: null,
|
||||
profile_bio: null,
|
||||
custom_theme_colors: {},
|
||||
});
|
||||
case "list_quick_actions":
|
||||
return Promise.resolve([]);
|
||||
case "list_snippets":
|
||||
return Promise.resolve([]);
|
||||
case "list_sessions":
|
||||
return Promise.resolve([]);
|
||||
case "get_usage_stats":
|
||||
return Promise.resolve({
|
||||
total_messages: 0,
|
||||
total_sessions: 0,
|
||||
total_tokens: 0,
|
||||
total_cost: 0,
|
||||
});
|
||||
case "get_persisted_stats":
|
||||
return Promise.resolve({
|
||||
lifetime_messages: 0,
|
||||
lifetime_sessions: 0,
|
||||
lifetime_tokens: 0,
|
||||
lifetime_cost: 0,
|
||||
achievements: [],
|
||||
unlocked_achievements: [],
|
||||
});
|
||||
case "load_saved_achievements":
|
||||
return Promise.resolve([]);
|
||||
case "list_clipboard_entries":
|
||||
return Promise.resolve([]);
|
||||
case "cleanup_temp_files":
|
||||
return Promise.resolve();
|
||||
case "validate_directory":
|
||||
return Promise.resolve(true);
|
||||
case "git_status":
|
||||
return Promise.resolve({
|
||||
branch: "main",
|
||||
files: [],
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
});
|
||||
default:
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock Tauri event API
|
||||
const eventListeners: Map<string, Array<(event: { payload: unknown }) => void>> = new Map();
|
||||
|
||||
export function emitMockEvent(eventName: string, payload: unknown) {
|
||||
const listeners = eventListeners.get(eventName) || [];
|
||||
listeners.forEach((listener) => listener({ payload }));
|
||||
}
|
||||
|
||||
export function clearMockEventListeners() {
|
||||
eventListeners.clear();
|
||||
}
|
||||
|
||||
vi.mock("@tauri-apps/api/event", () => ({
|
||||
listen: vi.fn((eventName: string, handler: (event: { payload: unknown }) => void) => {
|
||||
const listeners = eventListeners.get(eventName) || [];
|
||||
listeners.push(handler);
|
||||
eventListeners.set(eventName, listeners);
|
||||
// Return an unlisten function
|
||||
return Promise.resolve(() => {
|
||||
const currentListeners = eventListeners.get(eventName) || [];
|
||||
const index = currentListeners.indexOf(handler);
|
||||
if (index > -1) {
|
||||
currentListeners.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}),
|
||||
emit: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock Tauri plugins
|
||||
vi.mock("@tauri-apps/plugin-dialog", () => ({
|
||||
save: vi.fn(() => Promise.resolve(null)),
|
||||
open: vi.fn(() => Promise.resolve(null)),
|
||||
message: vi.fn(() => Promise.resolve()),
|
||||
ask: vi.fn(() => Promise.resolve(true)),
|
||||
confirm: vi.fn(() => Promise.resolve(true)),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-fs", () => ({
|
||||
writeTextFile: vi.fn(() => Promise.resolve()),
|
||||
readTextFile: vi.fn(() => Promise.resolve("{}")),
|
||||
exists: vi.fn(() => Promise.resolve(false)),
|
||||
mkdir: vi.fn(() => Promise.resolve()),
|
||||
remove: vi.fn(() => Promise.resolve()),
|
||||
readDir: vi.fn(() => Promise.resolve([])),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-opener", () => ({
|
||||
openPath: vi.fn(() => Promise.resolve()),
|
||||
openUrl: vi.fn(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-notification", () => ({
|
||||
sendNotification: vi.fn(() => Promise.resolve()),
|
||||
requestPermission: vi.fn(() => Promise.resolve("granted")),
|
||||
isPermissionGranted: vi.fn(() => Promise.resolve(true)),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-clipboard-manager", () => ({
|
||||
writeText: vi.fn(() => Promise.resolve()),
|
||||
readText: vi.fn(() => Promise.resolve("")),
|
||||
writeImage: vi.fn(() => Promise.resolve()),
|
||||
readImage: vi.fn(() => Promise.resolve(null)),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-os", () => ({
|
||||
platform: vi.fn(() => Promise.resolve("linux")),
|
||||
arch: vi.fn(() => Promise.resolve("x86_64")),
|
||||
type: vi.fn(() => Promise.resolve("Linux")),
|
||||
version: vi.fn(() => Promise.resolve("1.0.0")),
|
||||
}));
|
||||
|
||||
vi.mock("@tauri-apps/plugin-http", () => ({
|
||||
fetch: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: () => Promise.resolve({}),
|
||||
text: () => Promise.resolve(""),
|
||||
})
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock browser APIs
|
||||
class MockAudioElement {
|
||||
src = "";
|
||||
volume = 1;
|
||||
loop = false;
|
||||
currentTime = 0;
|
||||
paused = true;
|
||||
preload = "auto";
|
||||
onloadeddata: (() => void) | null = null;
|
||||
onended: (() => void) | null = null;
|
||||
onerror: ((e: Event) => void) | null = null;
|
||||
|
||||
play() {
|
||||
this.paused = false;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.paused = true;
|
||||
}
|
||||
|
||||
load() {
|
||||
if (this.onloadeddata) {
|
||||
setTimeout(() => this.onloadeddata?.(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener(event: string, handler: () => void) {
|
||||
if (event === "loadeddata") this.onloadeddata = handler;
|
||||
if (event === "ended") this.onended = handler;
|
||||
}
|
||||
|
||||
removeEventListener() {
|
||||
// No-op for tests
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error - Mock Audio constructor
|
||||
globalThis.Audio = MockAudioElement;
|
||||
|
||||
// Mock localStorage
|
||||
const localStorageStore: Record<string, string> = {};
|
||||
|
||||
Object.defineProperty(globalThis, "localStorage", {
|
||||
value: {
|
||||
getItem: vi.fn((key: string) => localStorageStore[key] || null),
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
localStorageStore[key] = value;
|
||||
}),
|
||||
removeItem: vi.fn((key: string) => {
|
||||
delete localStorageStore[key];
|
||||
}),
|
||||
clear: vi.fn(() => {
|
||||
Object.keys(localStorageStore).forEach((key) => delete localStorageStore[key]);
|
||||
}),
|
||||
key: vi.fn((index: number) => Object.keys(localStorageStore)[index] || null),
|
||||
get length() {
|
||||
return Object.keys(localStorageStore).length;
|
||||
},
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
|
||||
// Mock matchMedia
|
||||
Object.defineProperty(globalThis, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn((query: string) => ({
|
||||
matches: query.includes("dark"),
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
// Mock ResizeObserver
|
||||
class MockResizeObserver {
|
||||
observe = vi.fn();
|
||||
unobserve = vi.fn();
|
||||
disconnect = vi.fn();
|
||||
}
|
||||
|
||||
globalThis.ResizeObserver = MockResizeObserver as unknown as typeof ResizeObserver;
|
||||
|
||||
// Mock IntersectionObserver
|
||||
class MockIntersectionObserver {
|
||||
observe = vi.fn();
|
||||
unobserve = vi.fn();
|
||||
disconnect = vi.fn();
|
||||
}
|
||||
|
||||
globalThis.IntersectionObserver =
|
||||
MockIntersectionObserver as unknown as typeof IntersectionObserver;
|
||||
|
||||
// Mock requestAnimationFrame
|
||||
globalThis.requestAnimationFrame = vi.fn((callback) => {
|
||||
return setTimeout(callback, 0) as unknown as number;
|
||||
});
|
||||
|
||||
globalThis.cancelAnimationFrame = vi.fn((id) => {
|
||||
clearTimeout(id);
|
||||
});
|
||||
|
||||
// Reset all mocks before each test
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
clearMockInvokeResults();
|
||||
clearMockEventListeners();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user