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$/);
});
});
+4
View File
@@ -136,6 +136,10 @@ const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigat
export function comboOf(e: KeyboardEvent): string | null {
const key = e.key;
// Chrome's password autofill dispatches a plain Event named "keydown" when a
// saved login is picked: no key, nothing to match, and reading its length
// threw on every sign-in.
if (!key) return null;
if (key === "Shift" || key === "Control" || key === "Alt" || key === "Meta") return null;
const parts: string[] = [];
const mod = isMac ? e.metaKey : e.ctrlKey;