aboutsummaryrefslogtreecommitdiff
path: root/test-suite/challenges/21-keyboard-modifiers.html
blob: d18d4847354de6996aef59ed07e181e0289eb1b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!doctype html>
<meta charset="utf-8">
<title>21 keyboard modifiers</title>
<link rel="stylesheet" href="../_style.css">
<script src="../_lib.js"></script>
<body>
<main>
  <p>Goal: focus the input and type a capital <code>A</code> via <b>Shift+a</b>
     (not Caps Lock, not pasted, not assigned). Page asserts a real Shift chord:
     <code>keydown</code> for <code>Shift</code> arrives <em>before</em> <code>keydown</code> for
     <code>A</code>; the <code>A</code> event has <code>shiftKey=true</code>,
     <code>key==="A"</code>, <code>code==="KeyA"</code>, <code>getModifierState("Shift")===true</code>;
     and <code>keyup</code> for Shift arrives <em>after</em> <code>keyup</code> for <code>A</code>.</p>
  <input id="t" placeholder="type Shift+a" style="font-size:18px;padding:8px 12px;width:240px">
</main>
<script>
Challenge.init({ id: "keyboard-modifiers", instructions: "type capital A using Shift+a chord" });
const t = document.getElementById("t");

const log = [];
for (const name of ["keydown","keypress","keyup","input"]) {
  t.addEventListener(name, (e) => {
    log.push({
      name,
      key: e.key, code: e.code,
      shiftKey: e.shiftKey, isTrusted: e.isTrusted,
      modShift: e.getModifierState ? e.getModifierState("Shift") : null,
      t: e.timeStamp,
    });
  });
}

// input fires before keyup. Grade only when Shift is released so a valid chord
// cannot fail merely because its release events have not happened yet.
t.addEventListener("keyup", (event) => {
  if (event.key !== "Shift") return;
  const bad = [];
  if (t.value !== "A") bad.push(`value=${JSON.stringify(t.value)} (need 'A')`);
  const downShift = log.find(e => e.name === "keydown" && e.key === "Shift");
  const downA     = log.find(e => e.name === "keydown" && e.code === "KeyA");
  const upShift   = log.find(e => e.name === "keyup"   && e.key === "Shift");
  const upA       = log.find(e => e.name === "keyup"   && e.code === "KeyA");

  if (!downShift) bad.push("no keydown for Shift");
  if (!downA)     bad.push("no keydown for KeyA");
  if (!upShift)   bad.push("no keyup for Shift");
  if (!upA)       bad.push("no keyup for KeyA");

  if (downShift && downA && downShift.t > downA.t) bad.push("Shift keydown after A keydown");
  if (upShift && upA && upShift.t < upA.t) bad.push("Shift keyup before A keyup");

  if (downA) {
    if (downA.key !== "A")            bad.push(`A keydown.key="${downA.key}" (need 'A')`);
    if (downA.code !== "KeyA")        bad.push(`A keydown.code="${downA.code}"`);
    if (!downA.shiftKey)              bad.push("A keydown.shiftKey=false");
    if (downA.modShift !== true)      bad.push("getModifierState('Shift')!==true on A keydown");
    if (!downA.isTrusted)             bad.push("A keydown isTrusted=false");
  }
  if (downShift) {
    if (!downShift.isTrusted)         bad.push("Shift keydown isTrusted=false");
  }

  Challenge.log("chord", { log });
  if (bad.length) Challenge.fail(...bad);
  else Challenge.pass("Shift+a chord, modifiers + ordering correct");
});
</script>
</body>