export function typeInElement()

in src/cdk/testing/testbed/fake-events/type-in-element.ts [60:116]


export function typeInElement(element: HTMLElement, ...modifiersAndKeys: any) {
  const first = modifiersAndKeys[0];
  let modifiers: ModifierKeys;
  let rest: (string | {keyCode?: number; key?: string})[];
  if (typeof first !== 'string' && first.keyCode === undefined && first.key === undefined) {
    modifiers = first;
    rest = modifiersAndKeys.slice(1);
  } else {
    modifiers = {};
    rest = modifiersAndKeys;
  }
  const isInput = isTextInput(element);
  const inputType = element.getAttribute('type') || 'text';
  const keys: {keyCode?: number; key?: string}[] = rest
    .map(k =>
      typeof k === 'string'
        ? k.split('').map(c => ({keyCode: c.toUpperCase().charCodeAt(0), key: c}))
        : [k],
    )
    .reduce((arr, k) => arr.concat(k), []);

  // We simulate the user typing in a value by incrementally assigning the value below. The problem
  // is that for some input types, the browser won't allow for an invalid value to be set via the
  // `value` property which will always be the case when going character-by-character. If we detect
  // such an input, we have to set the value all at once or listeners to the `input` event (e.g.
  // the `ReactiveFormsModule` uses such an approach) won't receive the correct value.
  const enterValueIncrementally =
    inputType === 'number' && keys.length > 0
      ? // The value can be set character by character in number inputs if it doesn't have any decimals.
        keys.every(key => key.key !== '.' && key.keyCode !== PERIOD)
      : incrementalInputTypes.has(inputType);

  triggerFocus(element);

  // When we aren't entering the value incrementally, assign it all at once ahead
  // of time so that any listeners to the key events below will have access to it.
  if (!enterValueIncrementally) {
    (element as HTMLInputElement).value = keys.reduce((value, key) => value + (key.key || ''), '');
  }

  for (const key of keys) {
    dispatchKeyboardEvent(element, 'keydown', key.keyCode, key.key, modifiers);
    dispatchKeyboardEvent(element, 'keypress', key.keyCode, key.key, modifiers);
    if (isInput && key.key && key.key.length === 1) {
      if (enterValueIncrementally) {
        (element as HTMLInputElement | HTMLTextAreaElement).value += key.key;
        dispatchFakeEvent(element, 'input');
      }
    }
    dispatchKeyboardEvent(element, 'keyup', key.keyCode, key.key, modifiers);
  }

  // Since we weren't dispatching `input` events while sending the keys, we have to do it now.
  if (!enterValueIncrementally) {
    dispatchFakeEvent(element, 'input');
  }
}