Skip to content

Commit

Permalink
Merge pull request #1446 from bendemboski/dom-element-descriptors
Browse files Browse the repository at this point in the history
Support passing IDOMElementDescriptors as targets
  • Loading branch information
NullVoxPopuli authored Feb 12, 2024
2 parents 5dca0a8 + aa465cc commit e6a7205
Show file tree
Hide file tree
Showing 32 changed files with 656 additions and 80 deletions.
28 changes: 14 additions & 14 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ to continue to emulate how actual browsers handle unfocusing a given element.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to unfocus (optional, default `document.activeElement`)
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to unfocus (optional, default `document.activeElement`)

#### Examples

Expand Down Expand Up @@ -129,7 +129,7 @@ You can use this to specifiy modifier keys as well.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to click on
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to click on
* `_options` **MouseEventInit** the options to be merged into the mouse events. (optional, default `{}`)

#### Examples
Expand Down Expand Up @@ -185,7 +185,7 @@ Use the `options` hash to change the parameters of the [MouseEvents][67].

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to double-click on
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to double-click on
* `_options` **MouseEventInit** the options to be merged into the mouse events (optional, default `{}`)

#### Examples
Expand All @@ -212,7 +212,7 @@ events on the specified target.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to enter text into
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to enter text into
* `text` **[string][64]** the text to fill into the target element

#### Examples
Expand Down Expand Up @@ -242,7 +242,7 @@ to continue to emulate how actual browsers handle focusing a given element.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to focus
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to focus

#### Examples

Expand All @@ -256,11 +256,11 @@ Returns **[Promise][66]\<void>** resolves when the application is settled

### scrollTo

Scrolls DOM element or selector to the given coordinates.
Scrolls DOM element, selector, or descriptor to the given coordinates.

#### Parameters

* `target` **([string][64] | [HTMLElement][68])** the element or selector to trigger scroll on
* `target` **([string][64] | [HTMLElement][68] | IDOMElementDescriptor)** the element, selector, or descriptor to trigger scroll on
* `x` **[Number][69]** x-coordinate
* `y` **[Number][69]** y-coordinate

Expand All @@ -284,7 +284,7 @@ multiple attribute is set true on the HTMLSelectElement) then trigger

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector for the select element
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor for the select element
* `options` **([string][64] | [Array][70]<[string][64]>)** the value/values of the items to select
* `keepPreviouslySelected` **[boolean][71]** a flag keep any existing selections (optional, default `false`)

Expand Down Expand Up @@ -365,7 +365,7 @@ Use the `options` hash to change the parameters of the tap events.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to tap on
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to tap on
* `options` **[Object][72]** the options to be merged into the touch events (optional, default `{}`)

#### Examples
Expand All @@ -384,7 +384,7 @@ Triggers an event on the specified target.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to trigger the event on
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to trigger the event on
* `eventType` **[string][64]** the type of event to trigger
* `options` **[Object][72]** additional properties to be set on the event

Expand Down Expand Up @@ -432,7 +432,7 @@ Optionally the user can also provide a POJO with extra modifiers for the event.

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to trigger the event on
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to trigger the event on
* `eventType` **(`"keydown"` | `"keyup"` | `"keypress"`)** the type of event to trigger
* `key` **([number][69] | [string][64])** the `keyCode`(number) or `key`(string) of the event being triggered
* `modifiers` **[Object][72]?** the state of various modifier keys (optional, default `DEFAULT_MODIFIERS`)
Expand Down Expand Up @@ -466,7 +466,7 @@ per character of the passed text (this may vary on some browsers).

#### Parameters

* `target` **([string][64] | [Element][65])** the element or selector to enter text into
* `target` **([string][64] | [Element][65] | IDOMElementDescriptor)** the element, selector, or descriptor to enter text into
* `text` **[string][64]** the test to fill the element with
* `options` **[Object][72]** {delay: x} (default 50) number of milliseconds to wait per keypress (optional, default `{}`)

Expand Down Expand Up @@ -618,7 +618,7 @@ interim DOM states (e.g. loading states, pending promises, etc).

#### Parameters

* `selector` **[string][64]** the selector to wait for
* `target` **([string][64] | IDOMElementDescriptor)** the selector or DOM element descriptor to wait for
* `options` **[Object][72]?** the options to be used (optional, default `{}`)

* `options.timeout` **[number][69]** the time to wait (in ms) for a match (optional, default `1000`)
Expand Down Expand Up @@ -876,7 +876,7 @@ Responsible for:
#### Parameters

* `context` **[Object][72]** the context to setup
* `options` **[Object][72]?** options used to override defaults
* `options` **[Object][72]?** options used to override defaults (optional, default `{}`)

* `options.waitForSettled` **[boolean][71]** should the teardown wait for `settled()`ness (optional, default `true`)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { isDescriptor, lookupDescriptorData } from 'dom-element-descriptors';
import type Target from './-target';

/**
Used internally by the DOM interaction helpers to get a description of a
target for debug/error messaging.
@private
@param {Target} target the target
@returns {string} a description of the target
*/
export default function getDescription(target: Target): string {
let data = isDescriptor(target) ? lookupDescriptorData(target) : null;
if (data) {
return data.description || '<unknown descriptor>';
} else {
return `${target}`;
}
}
16 changes: 15 additions & 1 deletion addon/addon-test-support/@ember/test-helpers/dom/-get-element.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import getRootElement from './get-root-element';
import Target, { isDocument, isElement } from './-target';
import {
type IDOMElementDescriptor,
lookupDescriptorData,
resolveDOMElement,
} from 'dom-element-descriptors';

function getElement<
K extends keyof (HTMLElementTagNameMap | SVGElementTagNameMap)
Expand All @@ -12,8 +17,10 @@ function getElement<K extends keyof SVGElementTagNameMap>(
): SVGElementTagNameMap[K] | null;
function getElement(target: string): Element | null;
function getElement(target: Element): Element;
function getElement(target: IDOMElementDescriptor): Element | null;
function getElement(target: Document): Document;
function getElement(target: Window): Document;
function getElement(target: string | IDOMElementDescriptor): Element | null;
function getElement(target: Target): Element | Document | null;
/**
Used internally by the DOM interaction helpers to find one element.
Expand All @@ -32,7 +39,14 @@ function getElement(target: Target): Element | Document | null {
} else if (target instanceof Window) {
return target.document;
} else {
throw new Error('Must use an element or a selector string');
let descriptorData = lookupDescriptorData(target);
if (descriptorData) {
return resolveDOMElement(descriptorData);
} else {
throw new Error(
'Must use an element, selector string, or DOM element descriptor'
);
}
}
}

Expand Down
21 changes: 19 additions & 2 deletions addon/addon-test-support/@ember/test-helpers/dom/-get-elements.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
import {
type IDOMElementDescriptor,
lookupDescriptorData,
resolveDOMElements,
} from 'dom-element-descriptors';
import getRootElement from './get-root-element';

function getElements(target: string): NodeListOf<Element>;
function getElements(target: IDOMElementDescriptor): Iterable<Element>;
function getElements(target: string | IDOMElementDescriptor): Iterable<Element>;
/**
Used internally by the DOM interaction helpers to find multiple elements.
@private
@param {string} target the selector to retrieve
@returns {NodeList} the matched elements
*/
export default function getElements(target: string): NodeListOf<Element> {
function getElements(
target: string | IDOMElementDescriptor
): NodeListOf<Element> | Iterable<Element> {
if (typeof target === 'string') {
let rootElement = getRootElement();

return rootElement.querySelectorAll(target);
} else {
throw new Error('Must use a selector string');
let descriptorData = lookupDescriptorData(target);
if (descriptorData) {
return resolveDOMElements(descriptorData);
} else {
throw new Error('Must use a selector string or DOM element descriptor');
}
}
}

export default getElements;
4 changes: 3 additions & 1 deletion addon/addon-test-support/@ember/test-helpers/dom/-target.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
type Target = string | Element | Document | Window;
import type { IDOMElementDescriptor } from 'dom-element-descriptors';

type Target = string | Element | IDOMElementDescriptor | Document | Window;

export default Target;

Expand Down
6 changes: 4 additions & 2 deletions addon/addon-test-support/@ember/test-helpers/dom/blur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Target from './-target';
import { log } from './-logging';
import isFocusable from './-is-focusable';
import { runHooks, registerHook } from '../helper-hooks';
import getDescription from './-get-description';

registerHook('blur', 'start', (target: Target) => {
log('blur', target);
Expand Down Expand Up @@ -59,7 +60,7 @@ export function __blur__(
to continue to emulate how actual browsers handle unfocusing a given element.
@public
@param {string|Element} [target=document.activeElement] the element or selector to unfocus
@param {string|Element|IDOMElementDescriptor} [target=document.activeElement] the element, selector, or descriptor to unfocus
@return {Promise<void>} resolves when settled
@example
Expand All @@ -77,8 +78,9 @@ export default function blur(
.then(() => {
let element = getElement(target);
if (!element) {
let description = getDescription(target);
throw new Error(
`Element not found when calling \`blur('${target}')\`.`
`Element not found when calling \`blur('${description}')\`.`
);
}

Expand Down
10 changes: 7 additions & 3 deletions addon/addon-test-support/@ember/test-helpers/dom/click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isFormControl from './-is-form-control';
import Target, { isWindow } from './-target';
import { log } from './-logging';
import { runHooks, registerHook } from '../helper-hooks';
import getDescription from './-get-description';

const PRIMARY_BUTTON = 1;
const MAIN_BUTTON_PRESSED = 0;
Expand Down Expand Up @@ -72,7 +73,7 @@ export function __click__(
You can use this to specify modifier keys as well.
@public
@param {string|Element} target the element or selector to click on
@param {string|Element|IDOMElementDescriptor} target the element, selector, or descriptor to click on
@param {MouseEventInit} _options the options to be merged into the mouse events.
@return {Promise<void>} resolves when settled
Expand All @@ -99,13 +100,16 @@ export default function click(
.then(() => runHooks('click', 'start', target, _options))
.then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `click`.');
throw new Error(
'Must pass an element, selector, or descriptor to `click`.'
);
}

let element = getWindowOrElement(target);
if (!element) {
let description = getDescription(target);
throw new Error(
`Element not found when calling \`click('${target}')\`.`
`Element not found when calling \`click('${description}')\`.`
);
}

Expand Down
10 changes: 7 additions & 3 deletions addon/addon-test-support/@ember/test-helpers/dom/double-click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Target, { isWindow } from './-target';
import { log } from './-logging';
import isFormControl from './-is-form-control';
import { runHooks, registerHook } from '../helper-hooks';
import getDescription from './-get-description';

registerHook('doubleClick', 'start', (target: Target) => {
log('doubleClick', target);
Expand Down Expand Up @@ -72,7 +73,7 @@ export function __doubleClick__(
Use the `options` hash to change the parameters of the [MouseEvents](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent).
@public
@param {string|Element} target the element or selector to double-click on
@param {string|Element|IDOMElementDescriptor} target the element, selector, or descriptor to double-click on
@param {MouseEventInit} _options the options to be merged into the mouse events
@return {Promise<void>} resolves when settled
Expand Down Expand Up @@ -100,13 +101,16 @@ export default function doubleClick(
.then(() => runHooks('doubleClick', 'start', target, _options))
.then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `doubleClick`.');
throw new Error(
'Must pass an element, selector, or descriptor to `doubleClick`.'
);
}

let element = getWindowOrElement(target);
if (!element) {
let description = getDescription(target);
throw new Error(
`Element not found when calling \`doubleClick('${target}')\`.`
`Element not found when calling \`doubleClick('${description}')\`.`
);
}

Expand Down
18 changes: 13 additions & 5 deletions addon/addon-test-support/@ember/test-helpers/dom/fill-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fireEvent from './fire-event';
import Target, { isContentEditable } from './-target';
import { log } from './-logging';
import { runHooks, registerHook } from '../helper-hooks';
import getDescription from './-get-description';

registerHook('fillIn', 'start', (target: Target, text: string) => {
log('fillIn', target, text);
Expand All @@ -18,7 +19,7 @@ registerHook('fillIn', 'start', (target: Target, text: string) => {
events on the specified target.
@public
@param {string|Element} target the element or selector to enter text into
@param {string|Element|IDOMElementDescriptor} target the element, selector, or descriptor to enter text into
@param {string} text the text to fill into the target element
@return {Promise<void>} resolves when the application is settled
Expand All @@ -34,13 +35,16 @@ export default function fillIn(target: Target, text: string): Promise<void> {
.then(() => runHooks('fillIn', 'start', target, text))
.then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `fillIn`.');
throw new Error(
'Must pass an element, selector, or descriptor to `fillIn`.'
);
}

let element = getElement(target) as Element | HTMLElement;
if (!element) {
let description = getDescription(target);
throw new Error(
`Element not found when calling \`fillIn('${target}')\`.`
`Element not found when calling \`fillIn('${description}')\`.`
);
}

Expand All @@ -50,11 +54,15 @@ export default function fillIn(target: Target, text: string): Promise<void> {

if (isFormControl(element)) {
if (element.disabled) {
throw new Error(`Can not \`fillIn\` disabled '${target}'.`);
throw new Error(
`Can not \`fillIn\` disabled '${getDescription(target)}'.`
);
}

if ('readOnly' in element && element.readOnly) {
throw new Error(`Can not \`fillIn\` readonly '${target}'.`);
throw new Error(
`Can not \`fillIn\` readonly '${getDescription(target)}'.`
);
}

guardForMaxlength(element, text, 'fillIn');
Expand Down
Loading

0 comments on commit e6a7205

Please sign in to comment.