mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
* build(client): upgrade TypeScript to 6.0.3 Staging step toward TypeScript 7 (the native compiler), which needs its 7.1 stable API before typescript-eslint and Stryker's typescript-checker can run on it. TS 6 is the JS-based bridge release that aligns config defaults with 7. Two fallout fixes: - tsconfig.e2e.json: TS 6 defaults "types" to [] instead of every installed @types package, so the Playwright layer's Node globals (process, Buffer) need an explicit "types": ["node"]. - media-visibility.test.ts: TS 6's DOM lib adds scrollMargin to IntersectionObserver, so the mock grows the property. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lw5KEz6gdD816Wxmm4A3Bn * build(server): bump chi to 5.3.2, modernc.org/sqlite to 1.57.0, toolchain to go1.26.7 Go 1.27 deliberately deferred until 1.27.1 lands. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lw5KEz6gdD816Wxmm4A3Bn * build(tauri): bump tokio-tungstenite to 0.30, refresh Cargo.lock In-range lockfile refresh via cargo update; tungstenite 0.29/0.30 changes are client-API-neutral (header handling, server-side handshake hardening). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lw5KEz6gdD816Wxmm4A3Bn * build(client): upgrade vite 8, vitest 4, jsdom 30, stryker 10 + minors - vite 6 -> 8: Rolldown requires the function form of manualChunks; __dirname -> import.meta.dirname in configs - vitest 3 -> 4: browser provider moved to @vitest/browser-playwright; vi.fn() mocks now need explicit signatures (typed throughout tests); constructor mocks use function impls; restoreAllMocks no longer resets vi.fn state; matchMedia spies replaced with vi.stubGlobal - jsdom 29 -> 30: one internal bookkeeping abort listener per signal, listener-count regression tests adjusted (leak detection retained) - stryker 9 -> 10, @types/node 20 -> 24, eslint/oxlint/livekit-client minors - tsconfigs: explicit "types" now that TS6/vitest4 stop injecting @types/node ambiently; build config keeps Node globals out of src/ Validated: tsc (main/build/e2e), eslint, oxlint, knip, prettier, unit+integration (5196 tests), browser suite, vite build, stryker dry run. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lw5KEz6gdD816Wxmm4A3Bn * ci: move Node 20 (EOL 2026-04-30) to Node 24 LTS The jsdom suite runs on modern Node without --no-experimental-webstorage: tests/setup.ts already replaces the shadowed localStorage with an in-memory shim. Client CLAUDE.md gotcha updated accordingly. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lw5KEz6gdD816Wxmm4A3Bn --------- Co-authored-by: Claude <noreply@anthropic.com>
153 lines
3.9 KiB
TypeScript
153 lines
3.9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from "vitest";
|
|
|
|
const { mockVoiceGetState } = vi.hoisted(() => ({
|
|
mockVoiceGetState: vi.fn(() => ({ currentChannelId: null as number | null })),
|
|
}));
|
|
|
|
vi.mock("@lib/logger", () => ({
|
|
createLogger: () => ({
|
|
debug: vi.fn(),
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("@stores/voice.store", () => ({
|
|
voiceStore: { getState: mockVoiceGetState },
|
|
}));
|
|
|
|
const { attachGlobalKeybinds } = await import("../../src/pages/main-page/GlobalKeybinds");
|
|
|
|
function makeHandlers(): {
|
|
onSearch: Mock<() => void>;
|
|
onToggleMute: Mock<() => void>;
|
|
onToggleDeafen: Mock<() => void>;
|
|
onToggleCamera: Mock<() => void>;
|
|
onUploadFile: Mock<() => void>;
|
|
} {
|
|
return {
|
|
onSearch: vi.fn<() => void>(),
|
|
onToggleMute: vi.fn<() => void>(),
|
|
onToggleDeafen: vi.fn<() => void>(),
|
|
onToggleCamera: vi.fn<() => void>(),
|
|
onUploadFile: vi.fn<() => void>(),
|
|
};
|
|
}
|
|
|
|
function press(key: string, opts: Partial<KeyboardEventInit> = {}): KeyboardEvent {
|
|
const event = new KeyboardEvent("keydown", {
|
|
key,
|
|
ctrlKey: true,
|
|
cancelable: true,
|
|
...opts,
|
|
});
|
|
document.dispatchEvent(event);
|
|
return event;
|
|
}
|
|
|
|
describe("global keybinds", () => {
|
|
let detach: (() => void) | null = null;
|
|
|
|
beforeEach(() => {
|
|
mockVoiceGetState.mockReturnValue({ currentChannelId: null });
|
|
});
|
|
|
|
afterEach(() => {
|
|
detach?.();
|
|
detach = null;
|
|
});
|
|
|
|
it("Ctrl+F opens search and swallows the browser default", () => {
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
const event = press("f");
|
|
|
|
expect(h.onSearch).toHaveBeenCalledOnce();
|
|
expect(event.defaultPrevented).toBe(true);
|
|
});
|
|
|
|
it("Ctrl+U opens the file picker", () => {
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
press("u");
|
|
|
|
expect(h.onUploadFile).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("ignores voice shortcuts outside a voice channel", () => {
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
const mute = press("m");
|
|
const deafen = press("d");
|
|
const camera = press("V", { shiftKey: true });
|
|
|
|
expect(h.onToggleMute).not.toHaveBeenCalled();
|
|
expect(h.onToggleDeafen).not.toHaveBeenCalled();
|
|
expect(h.onToggleCamera).not.toHaveBeenCalled();
|
|
// Untouched keys keep their default behaviour.
|
|
expect(mute.defaultPrevented).toBe(false);
|
|
expect(deafen.defaultPrevented).toBe(false);
|
|
expect(camera.defaultPrevented).toBe(false);
|
|
});
|
|
|
|
it("fires voice shortcuts while connected to voice", () => {
|
|
mockVoiceGetState.mockReturnValue({ currentChannelId: 7 });
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
press("m");
|
|
press("d");
|
|
// Shift uppercases the key — the handler must not miss it.
|
|
press("V", { shiftKey: true });
|
|
|
|
expect(h.onToggleMute).toHaveBeenCalledOnce();
|
|
expect(h.onToggleDeafen).toHaveBeenCalledOnce();
|
|
expect(h.onToggleCamera).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("does nothing while suspended (settings overlay open)", () => {
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds({ ...h, isSuspended: () => true });
|
|
|
|
press("f");
|
|
press("u");
|
|
|
|
expect(h.onSearch).not.toHaveBeenCalled();
|
|
expect(h.onUploadFile).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("ignores plain keys and Alt combos", () => {
|
|
const h = makeHandlers();
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
press("f", { ctrlKey: false });
|
|
press("f", { altKey: true });
|
|
|
|
expect(h.onSearch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("keeps a handler error from escaping to the document", () => {
|
|
const h = makeHandlers();
|
|
h.onSearch.mockImplementation(() => {
|
|
throw new Error("boom");
|
|
});
|
|
detach = attachGlobalKeybinds(h);
|
|
|
|
expect(() => press("f")).not.toThrow();
|
|
});
|
|
|
|
it("detaching stops the shortcuts", () => {
|
|
const h = makeHandlers();
|
|
const stop = attachGlobalKeybinds(h);
|
|
stop();
|
|
|
|
press("f");
|
|
|
|
expect(h.onSearch).not.toHaveBeenCalled();
|
|
});
|
|
});
|