Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure typeIn does not await settled() after each event #707

Merged
merged 3 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 43 additions & 27 deletions addon-test-support/@ember/test-helpers/dom/trigger-key-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,47 @@ function keyCodeFromKey(key: string) {
return keyCode !== undefined ? parseInt(keyCode) : undefined;
}

/**
@private
@param {Element | Document} element the element to trigger the key event on
@param {'keydown' | 'keyup' | 'keypress'} eventType the type of event to trigger
@param {number|string} key the `keyCode`(number) or `key`(string) of the event being triggered
@param {Object} [modifiers] the state of various modifier keys
*/
export function __triggerKeyEvent__(
element: Element | Document,
eventType: KeyboardEventType,
key: number | string,
modifiers: KeyModifiers = DEFAULT_MODIFIERS
) {
let props;
if (typeof key === 'number') {
props = { keyCode: key, which: key, key: keyFromKeyCodeAndModifiers(key, modifiers) };
} else if (typeof key === 'string' && key.length !== 0) {
let firstCharacter = key[0];
if (firstCharacter !== firstCharacter.toUpperCase()) {
throw new Error(
`Must provide a \`key\` to \`triggerKeyEvent\` that starts with an uppercase character but you passed \`${key}\`.`
);
}

if (isNumeric(key) && key.length > 1) {
throw new Error(
`Must provide a numeric \`keyCode\` to \`triggerKeyEvent\` but you passed \`${key}\` as a string.`
);
}

let keyCode = keyCodeFromKey(key);
props = { keyCode, which: keyCode, key };
} else {
throw new Error(`Must provide a \`key\` or \`keyCode\` to \`triggerKeyEvent\``);
}

let options = assign(props, modifiers);

fireEvent(element, eventType, options);
}

/**
Triggers a keyboard event of given type in the target element.
It also requires the developer to provide either a string with the [`key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
Expand All @@ -128,7 +169,7 @@ function keyCodeFromKey(key: string) {
@param {boolean} [modifiers.altKey=false] if true the generated event will indicate the alt key was pressed during the key event
@param {boolean} [modifiers.shiftKey=false] if true the generated event will indicate the shift key was pressed during the key event
@param {boolean} [modifiers.metaKey=false] if true the generated event will indicate the meta key was pressed during the key event
@return {Promise<void>} resolves when the application is settled
@return {Promise<void>} resolves when the application is settled unless awaitSettled is false

@example
<caption>
Expand Down Expand Up @@ -163,32 +204,7 @@ export default function triggerKeyEvent(
);
}

let props;
if (typeof key === 'number') {
props = { keyCode: key, which: key, key: keyFromKeyCodeAndModifiers(key, modifiers) };
} else if (typeof key === 'string' && key.length !== 0) {
let firstCharacter = key[0];
if (firstCharacter !== firstCharacter.toUpperCase()) {
throw new Error(
`Must provide a \`key\` to \`triggerKeyEvent\` that starts with an uppercase character but you passed \`${key}\`.`
);
}

if (isNumeric(key) && key.length > 1) {
throw new Error(
`Must provide a numeric \`keyCode\` to \`triggerKeyEvent\` but you passed \`${key}\` as a string.`
);
}

let keyCode = keyCodeFromKey(key);
props = { keyCode, which: keyCode, key };
} else {
throw new Error(`Must provide a \`key\` or \`keyCode\` to \`triggerKeyEvent\``);
}

let options = assign(props, modifiers);

fireEvent(element, eventType, options);
__triggerKeyEvent__(element, eventType, key, modifiers);

return settled();
});
Expand Down
13 changes: 7 additions & 6 deletions addon-test-support/@ember/test-helpers/dom/type-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import isFocusable from './-is-focusable';
import { Promise } from 'rsvp';
import fireEvent from './fire-event';
import Target from './-target';
import triggerKeyEvent from './trigger-key-event';
import { __triggerKeyEvent__ } from './trigger-key-event';

export interface Options {
delay?: number;
Expand All @@ -29,12 +29,12 @@ export interface Options {
* @param {string} text the test to fill the element with
* @param {Object} options {delay: x} (default 50) number of milliseconds to wait per keypress
* @return {Promise<void>} resolves when the application is settled
*
*
* @example
* <caption>
* Emulating typing in an input using `typeIn`
* </caption>
*
*
* typeIn('hello world');
*/
export default function typeIn(target: Target, text: string, options: Options = {}): Promise<void> {
Expand Down Expand Up @@ -82,13 +82,14 @@ function keyEntry(element: FormControl, character: string): () => void {
let characterKey = character.toUpperCase();

return function() {
return triggerKeyEvent(element, 'keydown', characterKey, options)
.then(() => triggerKeyEvent(element, 'keypress', characterKey, options))
return nextTickPromise()
.then(() => __triggerKeyEvent__(element, 'keydown', characterKey, options))
.then(() => __triggerKeyEvent__(element, 'keypress', characterKey, options))
.then(() => {
element.value = element.value + character;
fireEvent(element, 'input');
})
.then(() => triggerKeyEvent(element, 'keyup', characterKey, options));
.then(() => __triggerKeyEvent__(element, 'keyup', characterKey, options));
};
}

Expand Down
21 changes: 21 additions & 0 deletions tests/unit/dom/type-in-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { module, test } from 'qunit';
import { typeIn, setupContext, teardownContext } from '@ember/test-helpers';
import { buildInstrumentedElement } from '../../helpers/events';
import { isIE11 } from '../../helpers/browser-detect';
import { debounce } from '@ember/runloop';
import { Promise } from 'rsvp';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';

/*
Expand Down Expand Up @@ -153,4 +155,23 @@ module('DOM Helper: typeIn', function(hooks) {
assert.strictEqual(document.activeElement, element, 'activeElement updated');
assert.equal(element.value, 'foo');
});

test('does not wait for other promises to settle', async function(assert) {
element = buildInstrumentedElement('input');

let runcount = 0;
let onInput = function() {
return Promise.resolve().then(() => runcount++);
};

element.oninput = function() {
// debounce 2 seconds for easy visualization in test
debounce(onInput, 2000);
};

await typeIn(element, 'foo');

assert.verifySteps(expectedEvents);
assert.equal(runcount, 1, 'debounced function only called once');
});
});