Ignore a keydown that carries no key

Picking a saved login from Chrome's password autofill dispatches a plain
Event named "keydown", with no key on it. The shortcut listener passed it to
comboOf, which read the key's length and threw -- an uncaught TypeError in
the console on every sign-in. Harmless, since nothing was bound to it, but
it was noise that looks like a real fault.

comboOf now returns null for an event with no key, as it already does for a
bare modifier, so the listener stops there.
This commit is contained in:
2026-09-15 07:27:12 -07:00
parent 14e22c21fe
commit 8e9ca0498a
2 changed files with 46 additions and 0 deletions
@@ -0,0 +1,42 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { comboOf, keyboard } from "@/lib/keyboard";
/*
* A "keydown" that carries no key. Chrome's password autofill dispatches one
* as a plain Event when a saved login is picked, and comboOf read `key.length`
* off it -- an uncaught TypeError in the console on every sign-in.
*/
let pop: (() => void) | null = null;
afterEach(() => {
pop?.();
pop = null;
});
describe("a keydown with no key", () => {
it("has no combo", () => {
expect(comboOf(new Event("keydown") as KeyboardEvent)).toBeNull();
});
it("reaches no binding and throws nothing", () => {
const handler = vi.fn();
pop = keyboard.pushScope("test", [{ keys: "e", description: "Archive", group: "Mail", handler }]);
const errors: unknown[] = [];
const onError = (ev: ErrorEvent) => errors.push(ev.error);
window.addEventListener("error", onError);
try {
window.dispatchEvent(new Event("keydown", { bubbles: true }));
} finally {
window.removeEventListener("error", onError);
}
expect(errors).toEqual([]);
expect(handler).not.toHaveBeenCalled();
});
it("leaves real keys alone", () => {
expect(comboOf(new KeyboardEvent("keydown", { key: "e" }))).toBe("e");
expect(comboOf(new KeyboardEvent("keydown", { key: "E", shiftKey: true }))).toBe("E");
expect(comboOf(new KeyboardEvent("keydown", { key: "Enter", ctrlKey: true }))).toMatch(/enter$/);
});
});