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:
@@ -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$/);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user