ember-test-helpers icon indicating copy to clipboard operation
ember-test-helpers copied to clipboard

typeIn helper triggers all key-related events, even if preventDefault is called

Open pgengler opened this issue 4 years ago • 2 comments

The typeIn helper always triggers all of the key-related events (keydown, keypress, input, and keyup) regardless of whether event.preventDefault() was called. This makes the helper work differently than actual browser input in a few cases:

  • When a keydown handler calls preventDefault, only the keydown and keyup events are triggered
  • When a keypress handler calls preventDefault, an input event is not triggered

Calling preventDefault in a keyup or input handler has no effect on which events are triggered.

(Tested with Chrome 84.0.4147.105 and Firefox 79.0.)

pgengler avatar Aug 20 '20 19:08 pgengler

This definitely seems good to fix, though I think it will be somewhat difficult to do.

rwjblue avatar Oct 14 '20 13:10 rwjblue

@pgengler - I had some success rolling my own from a combination of the internal __triggerKeyEvent__ / fireEvent / buildKeyboardEvent functions.

My needs were quite specific in that I didn't need to enter any alphabetic characters, so didn't need to worry about detecting or setting the shiftKey property on the event data. There's also not a great deal of error checking, max length checking, etc., and I had no need to support older browsers, so didn't try all the KeyboardEvent / KeyboardEvents / KeyEvent combinations.

Try this for size with numeric data... your mileage may vary 🙂 .

import { find, settled } from '@ember/test-helpers';

function buildNumberKeyEvent(eventType, key) {
  const keyCode = key.charCodeAt(0);
  const keyEventProps = { keyCode, which: keyCode, key, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, bubbles: true, cancelable: true };
  const keyEvent = new KeyboardEvent(eventType, keyEventProps);
  Object.defineProperty(keyEvent, 'keyCode', {
    get() {
      return parseInt(keyCode, 10); // buildKeyboardEvent is missing the ,10... and the radix doesn't default to 10
    },
  });

  Object.defineProperty(keyEvent, 'which', {
    get() {
      return parseInt(keyCode, 10); // buildKeyboardEvent is missing the ,10... and the radix doesn't default to 10
    },
  });

  return keyEvent;
}

async function preventableNumericTypeIn(selector, string) {
  const element = find(selector);
  if (!element) {
    throw new Error(`Cannot find selector ${selector}`);
  }

  for (const key of [...string]) {
    const keyDownEvent = buildNumberKeyEvent('keydown', key);
    element.dispatchEvent(keyDownEvent);
    await settled();
    if (keyDownEvent.defaultPrevented) {
      return;
    }

    const keyPressEvent = buildNumberKeyEvent('keypress', key);
    element.dispatchEvent(keyPressEvent);
    await settled();
    if (keyPressEvent.defaultPrevented) {
      return;
    }

    element.value += key;
    const inputEvent = document.createEvent('Events');
    inputEvent.initEvent('input', true, true);
    element.dispatchEvent(inputEvent);
    await settled();

    const keyUpEvent = buildNumberKeyEvent('keyup', key);
    element.dispatchEvent(keyUpEvent);
    await settled();
    if (keyUpEvent.defaultPrevented) {
      return;
    }

    const changeEvent = document.createEvent('Events');
    changeEvent.initEvent('change', true, true);
    element.dispatchEvent(inputEvent);
    await settled();
  }
}

await preventableNumericTypeIn('input', '1.2345');

BillyRayPreachersSon avatar Feb 25 '21 15:02 BillyRayPreachersSon