code: // Licensed to the Software Freedom Conservancy (SFC) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The SFC licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  package org.openqa.selenium;
  
  import static com.google.common.base.Joiner.on;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  import static org.openqa.selenium.WaitingConditions.elementValueToEqual;
  import static org.openqa.selenium.testing.TestUtilities.getEffectivePlatform;
  import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
  import static org.openqa.selenium.testing.drivers.Browser.IE;
  import static org.openqa.selenium.testing.drivers.Browser.SAFARI;
  
  import org.junit.jupiter.api.Test;
  import org.openqa.selenium.support.ui.ExpectedConditions;
  import org.openqa.selenium.testing.Ignore;
  import org.openqa.selenium.testing.JupiterTestBase;
  import org.openqa.selenium.testing.NotYetImplemented;
  
  class TypingTest extends JupiterTestBase {
  
    private static void checkRecordedKeySequence(WebElement element, int expectedKeyCode) {
      assertThat(element.getText().trim())
          .contains(
              String.format("down: %1$d", expectedKeyCode),
              String.format("up: %1$d", expectedKeyCode));
    }
  
    private static String getValueText(WebElement el) {
      // Standardize on \n and strip any trailing whitespace.
      return el.getAttribute("value").replace("\r\n", "\n").trim();
    }
  
    private Keys primaryModifier() {
      return (getEffectivePlatform(driver).is(Platform.MAC)) ? Keys.COMMAND : Keys.CONTROL;
    }
  
    private Keys homeKey() {
      return (getEffectivePlatform(driver).is(Platform.MAC)) ? Keys.UP : Keys.HOME;
    }
  
    private Keys endKey() {
      return (getEffectivePlatform(driver).is(Platform.MAC)) ? Keys.DOWN : Keys.END;
    }
  
    @Test
    void testShouldFireKeyPressEvents() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("a");
  
      WebElement result = driver.findElement(By.id("result"));
      assertThat(result.getText()).contains("press:");
    }
  
    @Test
    void testShouldFireKeyDownEvents() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("I");
  
      WebElement result = driver.findElement(By.id("result"));
      assertThat(result.getText()).contains("down:");
    }
  
    @Test
    void testShouldFireKeyUpEvents() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("a");
  
      WebElement result = driver.findElement(By.id("result"));
      assertThat(result.getText()).contains("up:");
    }
  
    @Test
    void testShouldTypeLowerCaseLetters() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("abc def");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("abc def");
    }
  
    @Test
    void testShouldBeAbleToTypeCapitalLetters() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("ABC DEF");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("ABC DEF");
    }
  
    @Test
    void testShouldBeAbleToTypeQuoteMarks() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("\"");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("\"");
    }
  
    @Test
    void testShouldBeAbleToTypeTheAtCharacter() {
      // simon: I tend to use a US/UK or AUS keyboard layout with English
      // as my primary language. There are consistent reports that we're
      // not handling i18nised keyboards properly. This test exposes this
      // in a lightweight manner when my keyboard is set to the DE mapping
      // and we're using IE.
  
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("@");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("@");
    }
  
    @Test
    void testShouldBeAbleToMixUpperAndLowerCaseLetters() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("me@eXample.com");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("me@eXample.com");
    }
  
    @Test
    void testArrowKeysShouldNotBePrintable() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys(Keys.ARROW_LEFT);
  
      assertThat(keyReporter.getAttribute("value")).isEmpty();
    }
  
    @Test
    void testShouldBeAbleToUseArrowKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement keyReporter = driver.findElement(By.id("keyReporter"));
      keyReporter.sendKeys("tet", Keys.ARROW_LEFT, "s");
  
      assertThat(keyReporter.getAttribute("value")).isEqualTo("test");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyUpWhenEnteringTextIntoInputElements() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyUp"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      assertThat(result.getText()).isEqualTo("I like cheese");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyDownWhenEnteringTextIntoInputElements() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyDown"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      // Because the key down gets the result before the input element is
      // filled, we're a letter short here
      assertThat(result.getText()).isEqualTo("I like chees");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyPressWhenEnteringTextIntoInputElements() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyPress"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      // Because the key down gets the result before the input element is
      // filled, we're a letter short here
      assertThat(result.getText()).isEqualTo("I like chees");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyUpWhenEnteringTextIntoTextAreas() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyUpArea"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      assertThat(result.getText()).isEqualTo("I like cheese");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyDownWhenEnteringTextIntoTextAreas() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyDownArea"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      // Because the key down gets the result before the input element is
      // filled, we're a letter short here
      assertThat(result.getText()).isEqualTo("I like chees");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testWillSimulateAKeyPressWhenEnteringTextIntoTextAreas() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyPressArea"));
      element.sendKeys("I like cheese");
  
      WebElement result = driver.findElement(By.id("result"));
      // Because the key down gets the result before the input element is
      // filled, we're a letter short here
      assertThat(result.getText()).isEqualTo("I like chees");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testShouldFireFocusKeyEventsInTheRightOrder() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("theworks"));
  
      element.sendKeys("a");
      assertThat(result.getText().trim()).isEqualTo("focus keydown keypress keyup");
    }
  
    @Test
    void testShouldReportKeyCodeOfArrowKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys(Keys.ARROW_DOWN);
      checkRecordedKeySequence(result, 40);
  
      element.sendKeys(Keys.ARROW_UP);
      checkRecordedKeySequence(result, 38);
  
      element.sendKeys(Keys.ARROW_LEFT);
      checkRecordedKeySequence(result, 37);
  
      element.sendKeys(Keys.ARROW_RIGHT);
      checkRecordedKeySequence(result, 39);
  
      // And leave no rubbish/printable keys in the "keyReporter"
      assertThat(element.getAttribute("value")).isEmpty();
    }
  
    @Test
    void testShouldReportKeyCodeOfArrowKeysUpDownEvents() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys(Keys.ARROW_DOWN);
      assertThat(result.getText().trim()).contains("down: 40", "up: 40");
  
      element.sendKeys(Keys.ARROW_UP);
      assertThat(result.getText().trim()).contains("down: 38", "up: 38");
  
      element.sendKeys(Keys.ARROW_LEFT);
      assertThat(result.getText().trim()).contains("down: 37", "up: 37");
  
      element.sendKeys(Keys.ARROW_RIGHT);
      assertThat(result.getText().trim()).contains("down: 39", "up: 39");
  
      // And leave no rubbish/printable keys in the "keyReporter"
      assertThat(element.getAttribute("value")).isEmpty();
    }
  
    @Test
    void testNumericNonShiftKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String numericLineCharsNonShifted = "`1234567890-=[]\\;,.'/42";
      element.sendKeys(numericLineCharsNonShifted);
  
      assertThat(element.getAttribute("value")).isEqualTo(numericLineCharsNonShifted);
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Final assertion isn't 16 since keyUp not sent from shift",
        issue = "https://github.com/mozilla/geckodriver/issues/646")
    public void testNumericShiftKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String numericShiftsEtc = "~!@#$%^&*()_+{}:\"<>?|END~";
      element.sendKeys(numericShiftsEtc);
  
      assertThat(element.getAttribute("value")).isEqualTo(numericShiftsEtc);
      assertThat(result.getText().trim()).contains(" up: 16");
    }
  
    @Test
    void testLowerCaseAlphaKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String lowerAlphas = "abcdefghijklmnopqrstuvwxyz";
      element.sendKeys(lowerAlphas);
  
      assertThat(element.getAttribute("value")).isEqualTo(lowerAlphas);
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Final assertion isn't 16 since keyUp not sent from shift",
        issue = "https://github.com/mozilla/geckodriver/issues/646")
    public void testUppercaseAlphaKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String upperAlphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      element.sendKeys(upperAlphas);
  
      assertThat(element.getAttribute("value")).isEqualTo(upperAlphas);
      assertThat(result.getText().trim()).contains(" up: 16");
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Final assertion isn't 16 since keyUp not sent from shift",
        issue = "https://github.com/mozilla/geckodriver/issues/646")
    public void testAllPrintableKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String allPrintable =
          "!\"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNO"
              + "PQRSTUVWXYZ [\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
      element.sendKeys(allPrintable);
  
      assertThat(element.getAttribute("value")).isEqualTo(allPrintable);
      assertThat(result.getText().trim()).contains(" up: 16");
    }
  
    @Test
    void testArrowKeysAndPageUpAndDown() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys(
          "a"
              + Keys.LEFT
              + "b"
              + Keys.RIGHT
              + Keys.UP
              + Keys.DOWN
              + Keys.PAGE_UP
              + Keys.PAGE_DOWN
              + "1");
      assertThat(element.getAttribute("value")).isEqualTo("ba1");
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Firefox can't type at beginning of field",
        issue = "https://github.com/mozilla/geckodriver/issues/2015")
    public void testHomeAndEndAndPageUpAndPageDownKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys(
          "abc"
              + homeKey()
              + "0"
              + Keys.LEFT
              + Keys.RIGHT
              + Keys.PAGE_UP
              + Keys.PAGE_DOWN
              + endKey()
              + "1"
              + homeKey()
              + "0"
              + Keys.PAGE_UP
              + endKey()
              + "111"
              + homeKey()
              + "00");
      assertThat(element.getAttribute("value")).isEqualTo("0000abc1111");
    }
  
    @Test
    void testDeleteAndBackspaceKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("abcdefghi");
      assertThat(element.getAttribute("value")).isEqualTo("abcdefghi");
  
      element.sendKeys(Keys.LEFT, Keys.LEFT, Keys.DELETE);
      assertThat(element.getAttribute("value")).isEqualTo("abcdefgi");
  
      element.sendKeys(Keys.LEFT, Keys.LEFT, Keys.BACK_SPACE);
      assertThat(element.getAttribute("value")).isEqualTo("abcdfgi");
    }
  
    @Test
    void testSpecialSpaceKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("abcd" + Keys.SPACE + "fgh" + Keys.SPACE + "ij");
      assertThat(element.getAttribute("value")).isEqualTo("abcd fgh ij");
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "Enters dot instead of comma")
    public void testNumberPadKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys(
          "abcd"
              + Keys.MULTIPLY
              + Keys.SUBTRACT
              + Keys.ADD
              + Keys.DECIMAL
              + Keys.SEPARATOR
              + Keys.NUMPAD0
              + Keys.NUMPAD9
              + Keys.ADD
              + Keys.SEMICOLON
              + Keys.EQUALS
              + Keys.DIVIDE
              + Keys.NUMPAD3
              + "abcd");
      assertThat(element.getAttribute("value")).isEqualTo("abcd*-+.,09+;=/3abcd");
    }
  
    @Test
    @Ignore(value = IE, reason = "F4 triggers address bar")
    public void testFunctionKeys() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("FUNCTION" + Keys.F4 + "-KEYS" + Keys.F4);
      element.sendKeys("" + Keys.F4 + "-TOO" + Keys.F4);
      assertThat(element.getAttribute("value")).isEqualTo("FUNCTION-KEYS-TOO");
    }
  
    @Test
    @NotYetImplemented(SAFARI)
    public void testShiftSelectionDeletes() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("abcd efgh");
      assertThat(element.getAttribute("value")).isEqualTo("abcd efgh");
  
      element.sendKeys(Keys.SHIFT, Keys.LEFT, Keys.LEFT, Keys.LEFT);
      element.sendKeys(Keys.DELETE);
      assertThat(element.getAttribute("value")).isEqualTo("abcd e");
    }
  
    @Test
    void testChordControlHomeShiftEndDelete() {
  
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("!\"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFG");
  
      element.sendKeys(endKey());
      element.sendKeys("" + Keys.SHIFT + homeKey());
  
      element.sendKeys(Keys.DELETE);
      assertThat(element.getAttribute("value")).isEmpty();
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Firefox can't type at beginning of field",
        issue = "https://github.com/mozilla/geckodriver/issues/2015")
    public void testChordReverseShiftHomeSelectionDeletes() {
      driver.get(pages.javascriptPage);
  
      WebElement result = driver.findElement(By.id("result"));
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      element.sendKeys("done" + homeKey());
      assertThat(element.getAttribute("value")).isEqualTo("done");
  
      element.sendKeys(Keys.SHIFT + "all " + homeKey());
      assertThat(element.getAttribute("value")).isEqualTo("ALL done");
  
      element.sendKeys(Keys.DELETE);
      assertThat(element.getAttribute("value")).isEqualTo("done");
  
      element.sendKeys("" + endKey() + Keys.SHIFT + homeKey());
      assertThat(element.getAttribute("value")).isEqualTo("done");
      assertThat(result.getText().trim()).contains(" up: 16");
  
      element.sendKeys(Keys.DELETE);
      assertThat(element.getAttribute("value")).isEmpty();
    }
  
    @Test
    @Ignore(
        value = FIREFOX,
        reason = "Firefox can't type at beginning of field",
        issue = "https://github.com/mozilla/geckodriver/issues/2015")
    public void testChordControlCutAndPaste() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      String paste = "!\"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFG";
      element.sendKeys(paste);
      assertThat(element.getAttribute("value")).isEqualTo(paste);
  
      element.sendKeys(homeKey());
      element.sendKeys("" + homeKey() + Keys.SHIFT + endKey());
  
      element.sendKeys(primaryModifier(), "x");
      assertThat(element.getAttribute("value")).isEmpty();
  
      element.sendKeys(primaryModifier(), "v");
      wait.until(elementValueToEqual(element, paste));
  
      // Cut the last 3 letters.
      element.sendKeys("" + Keys.LEFT + Keys.LEFT + Keys.LEFT + Keys.SHIFT + endKey());
  
      element.sendKeys(primaryModifier(), "x");
      assertThat(element.getAttribute("value")).isEqualTo(paste.substring(0, paste.length() - 3));
  
      // Paste the last 3 letters.
      element.sendKeys(primaryModifier(), "v");
      assertThat(element.getAttribute("value")).isEqualTo(paste);
  
      element.sendKeys(homeKey());
      element.sendKeys(primaryModifier(), "v");
      element.sendKeys(primaryModifier(), "v" + "v");
      element.sendKeys(primaryModifier(), "v" + "v" + "v");
      assertThat(element.getAttribute("value")).isEqualTo("EFGEFGEFGEFGEFGEFG" + paste);
  
      element.sendKeys("" + Keys.END + Keys.SHIFT + homeKey() + Keys.NULL + Keys.DELETE);
      assertThat(element.getAttribute("value")).isEmpty();
    }
  
    @Test
    void testShouldTypeIntoInputElementsThatHaveNoTypeAttribute() {
      driver.get(pages.formPage);
  
      WebElement element = driver.findElement(By.id("no-type"));
  
      element.sendKeys("should say cheese");
      assertThat(element.getAttribute("value")).isEqualTo("should say cheese");
    }
  
    @Test
    void testShouldNotTypeIntoElementsThatPreventKeyDownEvents() {
      driver.get(pages.javascriptPage);
  
      WebElement silent = driver.findElement(By.name("suppress"));
  
      silent.sendKeys("s");
      assertThat(silent.getAttribute("value")).isEmpty();
    }
  
    @Test
    @NotYetImplemented(value = SAFARI, reason = "getText does not normalize spaces")
    public void testGenerateKeyPressEventEvenWhenElementPreventsDefault() {
      driver.get(pages.javascriptPage);
  
      WebElement silent = driver.findElement(By.name("suppress"));
      WebElement result = driver.findElement(By.id("result"));
  
      silent.sendKeys("s");
      assertThat(result.getText().trim()).isIn("", "mouseover");
    }
  
    @Test
    void testShouldBeAbleToTypeOnAnEmailInputField() {
      driver.get(pages.formPage);
      WebElement email = driver.findElement(By.id("email"));
      email.sendKeys("foobar");
      assertThat(email.getAttribute("value")).isEqualTo("foobar");
    }
  
    @Test
    void testShouldBeAbleToTypeOnANumberInputField() {
      driver.get(pages.formPage);
      WebElement email = driver.findElement(By.id("age"));
      email.sendKeys("33");
      assertThat(email.getAttribute("value")).isEqualTo("33");
    }
  
    @Test
    void testShouldThrowIllegalArgumentException() {
      driver.get(pages.formPage);
      WebElement email = driver.findElement(By.id("age"));
      assertThatExceptionOfType(IllegalArgumentException.class)
          .isThrownBy(() -> email.sendKeys((CharSequence[]) null));
    }
  
    @Test
    void canSafelyTypeOnElementThatIsRemovedFromTheDomOnKeyPress() {
      driver.get(appServer.whereIs("key_tests/remove_on_keypress.html"));
  
      WebElement input = driver.findElement(By.id("target"));
      WebElement log = driver.findElement(By.id("log"));
  
      assertThat(log.getAttribute("value")).isEmpty();
  
      input.sendKeys("b");
      assertThat(getValueText(log))
          .isEqualTo(on('\n').join("keydown (target)", "keyup (target)", "keyup (body)"));
  
      input.sendKeys("a");
  
      // Some drivers (IE, Firefox) do not always generate the final keyup event since the element
      // is removed from the DOM in response to the keypress (note, this is a product of how events
      // are generated and does not match actual user behavior).
      String expected =
          String.join(
              "\n",
              "keydown (target)",
              "keyup (target)",
              "keyup (body)",
              "keydown (target)",
              "a pressed; removing");
      assertThat(getValueText(log)).isIn(expected, expected + "\nkeyup (body)");
    }
  
    @Test
    void canClearNumberInputAfterTypingInvalidInput() {
      driver.get(pages.formPage);
      WebElement input = driver.findElement(By.id("age"));
      input.sendKeys("e");
      input.clear();
      input.sendKeys("3");
      assertThat(input.getAttribute("value")).isEqualTo("3");
    }
  
    @Test
    void canTypeSingleNewLineCharacterIntoTextArea() {
      driver.get(pages.formPage);
      WebElement element = driver.findElement(By.id("emptyTextArea"));
      element.sendKeys("\n");
      shortWait.until(ExpectedConditions.attributeToBe(element, "value", "\n"));
    }
  
    @Test
    void canTypeMultipleNewLineCharactersIntoTextArea() {
      driver.get(pages.formPage);
      WebElement element = driver.findElement(By.id("emptyTextArea"));
      element.sendKeys("\n\n\n");
      shortWait.until(ExpectedConditions.attributeToBe(element, "value", "\n\n\n"));
    }
  }
  