// @vitest-environment happy-dom
import React, { act, useRef } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useAppHotkeys } from "../../hooks/useAppHotkeys";
import { usePlayerStore } from "../../player/store/playerStore";
import type { LeftSidebarHandle } from "../sidebar/LeftSidebar";
import type { DomEditSelection } from "./domEditing";
import { SnapToolbar } from "./SnapToolbar";
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
afterEach(() => {
document.body.innerHTML = "";
window.localStorage.clear();
usePlayerStore.getState().reset();
});
function renderToolbar(onSnapChange = vi.fn()) {
const host = document.createElement("div");
document.body.append(host);
const root = createRoot(host);
act(() => {
root.render();
});
return { root, onSnapChange };
}
function AppHotkeyHarness() {
const domEditSelectionRef = useRef(null);
const clearDomSelectionRef = useRef<() => void>(() => undefined);
const domEditSaveTimestampRef = useRef(0);
const leftSidebarRef = useRef(null);
useAppHotkeys({
handleTimelineElementDelete: vi.fn(),
handleTimelineElementSplit: vi.fn(),
handleDomEditElementDelete: vi.fn(),
domEditSelectionRef,
clearDomSelectionRef,
editHistory: {
undo: vi.fn(async () => ({ ok: false })),
redo: vi.fn(async () => ({ ok: false })),
state: { undo: [], redo: [] },
},
readOptionalProjectFile: vi.fn(async () => ""),
readProjectFile: vi.fn(async () => ""),
writeProjectFile: vi.fn(async () => undefined),
domEditSaveTimestampRef,
showToast: vi.fn(),
syncHistoryPreviewAfterApply: vi.fn(async () => undefined),
waitForPendingDomEditSaves: vi.fn(async () => undefined),
leftSidebarRef,
handleCopy: vi.fn(() => false),
handlePaste: vi.fn(async () => undefined),
handleCut: vi.fn(async () => false),
onResetKeyframes: vi.fn(() => false),
onDeleteSelectedKeyframes: vi.fn(),
});
return null;
}
function renderToolbarWithAppHotkeys(onSnapChange = vi.fn()) {
const host = document.createElement("div");
document.body.append(host);
const root = createRoot(host);
act(() => {
root.render(
<>
>,
);
});
return { root, onSnapChange };
}
describe("SnapToolbar keyboard shortcuts", () => {
it("toggles snap on an unclaimed S keypress", () => {
const { root, onSnapChange } = renderToolbar();
act(() => {
document.dispatchEvent(
new KeyboardEvent("keydown", { key: "s", bubbles: true, cancelable: true }),
);
});
expect(onSnapChange).toHaveBeenCalledWith(expect.objectContaining({ snapEnabled: false }));
act(() => root.unmount());
});
it("does not toggle snap when another handler already prevented S", () => {
const { root, onSnapChange } = renderToolbar();
const event = new KeyboardEvent("keydown", {
key: "s",
bubbles: true,
cancelable: true,
});
event.preventDefault();
act(() => {
document.dispatchEvent(event);
});
expect(onSnapChange).not.toHaveBeenCalled();
act(() => root.unmount());
});
it("does not toggle snap when the app split shortcut claims S without a selected clip", () => {
const { root, onSnapChange } = renderToolbarWithAppHotkeys();
act(() => {
document.dispatchEvent(
new KeyboardEvent("keydown", { key: "s", bubbles: true, cancelable: true }),
);
});
expect(onSnapChange).not.toHaveBeenCalled();
act(() => root.unmount());
});
});