This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
declare function blur(element: Element): void;
export { blur };

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.blur = blur;
var _utils = require("./utils");
function blur(element) {
if (!(0, _utils.isFocusable)(element)) return;
const wasActive = (0, _utils.getActiveElement)(element.ownerDocument) === element;
if (!wasActive) return;
(0, _utils.eventWrapper)(() => element.blur());
}

View File

@@ -0,0 +1,2 @@
declare function clear(element: Element): void;
export { clear };

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.clear = clear;
var _utils = require("./utils");
var _type = require("./type");
function clear(element) {
var _element$selectionSta, _element$selectionEnd;
if (!(0, _utils.isElementType)(element, ['input', 'textarea'])) {
// TODO: support contenteditable
throw new Error('clear currently only supports input and textarea elements.');
}
if ((0, _utils.isDisabled)(element)) {
return;
} // TODO: track the selection range ourselves so we don't have to do this input "type" trickery
// just like cypress does: https://github.com/cypress-io/cypress/blob/8d7f1a0bedc3c45a2ebf1ff50324b34129fdc683/packages/driver/src/dom/selection.ts#L16-L37
const elementType = element.type;
if (elementType !== 'textarea') {
// setSelectionRange is not supported on certain types of inputs, e.g. "number" or "email"
;
element.type = 'text';
}
(0, _type.type)(element, '{selectall}{del}', {
delay: 0,
initialSelectionStart: (_element$selectionSta = element.selectionStart) != null ? _element$selectionSta :
/* istanbul ignore next */
undefined,
initialSelectionEnd: (_element$selectionEnd = element.selectionEnd) != null ? _element$selectionEnd :
/* istanbul ignore next */
undefined
});
if (elementType !== 'textarea') {
;
element.type = elementType;
}
}

View File

@@ -0,0 +1,8 @@
import { PointerOptions } from './utils';
export declare interface clickOptions {
skipHover?: boolean;
clickCount?: number;
}
declare function click(element: Element, init?: MouseEventInit, { skipHover, clickCount, skipPointerEventsCheck, }?: clickOptions & PointerOptions): void;
declare function dblClick(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: clickOptions & PointerOptions): void;
export { click, dblClick };

View File

@@ -0,0 +1,176 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.click = click;
exports.dblClick = dblClick;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
var _hover = require("./hover");
var _blur = require("./blur");
var _focus = require("./focus");
function getPreviouslyFocusedElement(element) {
const focusedElement = element.ownerDocument.activeElement;
const wasAnotherElementFocused = focusedElement && focusedElement !== element.ownerDocument.body && focusedElement !== element;
return wasAnotherElementFocused ? focusedElement : null;
}
function clickLabel(label, init, {
clickCount
}) {
if ((0, _utils.isLabelWithInternallyDisabledControl)(label)) return;
_dom.fireEvent.pointerDown(label, init);
_dom.fireEvent.mouseDown(label, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
_dom.fireEvent.pointerUp(label, init);
_dom.fireEvent.mouseUp(label, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
fireClick(label, (0, _utils.getMouseEventOptions)('click', init, clickCount)); // clicking the label will trigger a click of the label.control
// however, it will not focus the label.control so we have to do it
// ourselves.
if (label.control) (0, _focus.focus)(label.control);
}
function clickBooleanElement(element, init, {
clickCount
}) {
_dom.fireEvent.pointerDown(element, init);
if (!element.disabled) {
_dom.fireEvent.mouseDown(element, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
}
(0, _focus.focus)(element);
_dom.fireEvent.pointerUp(element, init);
if (!element.disabled) {
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
fireClick(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
}
}
function clickElement(element, init, {
clickCount
}) {
const previousElement = getPreviouslyFocusedElement(element);
_dom.fireEvent.pointerDown(element, init);
if (!(0, _utils.isDisabled)(element)) {
const continueDefaultHandling = _dom.fireEvent.mouseDown(element, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
if (continueDefaultHandling) {
const closestFocusable = findClosest(element, _utils.isFocusable);
if (previousElement && !closestFocusable) {
(0, _blur.blur)(previousElement);
} else if (closestFocusable) {
(0, _focus.focus)(closestFocusable);
}
}
}
_dom.fireEvent.pointerUp(element, init);
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
fireClick(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
const parentLabel = element.closest('label');
if (parentLabel != null && parentLabel.control) (0, _focus.focus)(parentLabel.control);
}
}
function findClosest(element, callback) {
let el = element;
do {
if (callback(el)) {
return el;
}
el = el.parentElement;
} while (el && el !== element.ownerDocument.body);
return undefined;
}
function click(element, init, {
skipHover = false,
clickCount = 0,
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to click element as it has or inherits pointer-events set to "none".');
} // We just checked for `pointerEvents`. We can always skip this one in `hover`.
if (!skipHover) (0, _hover.hover)(element, init, {
skipPointerEventsCheck: true
});
if ((0, _utils.isElementType)(element, 'label')) {
clickLabel(element, init, {
clickCount
});
} else if ((0, _utils.isElementType)(element, 'input')) {
if (element.type === 'checkbox' || element.type === 'radio') {
clickBooleanElement(element, init, {
clickCount
});
} else {
clickElement(element, init, {
clickCount
});
}
} else {
clickElement(element, init, {
clickCount
});
}
}
function fireClick(element, mouseEventOptions) {
if (mouseEventOptions.button === 2) {
_dom.fireEvent.contextMenu(element, mouseEventOptions);
} else {
_dom.fireEvent.click(element, mouseEventOptions);
}
}
function dblClick(element, init, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to double-click element as it has or inherits pointer-events set to "none".');
}
(0, _hover.hover)(element, init, {
skipPointerEventsCheck
});
click(element, init, {
skipHover: true,
clickCount: 0,
skipPointerEventsCheck
});
click(element, init, {
skipHover: true,
clickCount: 1,
skipPointerEventsCheck
});
_dom.fireEvent.dblClick(element, (0, _utils.getMouseEventOptions)('dblclick', init, 2));
}

View File

@@ -0,0 +1,3 @@
declare module '@testing-library/dom/dist/helpers' {
export function getWindowFromNode(node: Node): Window
}

View File

@@ -0,0 +1,2 @@
declare function focus(element: Element): void;
export { focus };

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.focus = focus;
var _utils = require("./utils");
function focus(element) {
if (!(0, _utils.isFocusable)(element)) return;
const isAlreadyActive = (0, _utils.getActiveElement)(element.ownerDocument) === element;
if (isAlreadyActive) return;
(0, _utils.eventWrapper)(() => element.focus());
}

View File

@@ -0,0 +1,4 @@
import { PointerOptions } from './utils';
declare function hover(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: PointerOptions): void;
declare function unhover(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: PointerOptions): void;
export { hover, unhover };

View File

@@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hover = hover;
exports.unhover = unhover;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
// includes `element`
function getParentElements(element) {
const parentElements = [element];
let currentElement = element;
while ((currentElement = currentElement.parentElement) != null) {
parentElements.push(currentElement);
}
return parentElements;
}
function hover(element, init, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to hover element as it has or inherits pointer-events set to "none".');
}
if ((0, _utils.isLabelWithInternallyDisabledControl)(element)) return;
const parentElements = getParentElements(element).reverse();
_dom.fireEvent.pointerOver(element, init);
for (const el of parentElements) {
_dom.fireEvent.pointerEnter(el, init);
}
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseOver(element, (0, _utils.getMouseEventOptions)('mouseover', init));
for (const el of parentElements) {
_dom.fireEvent.mouseEnter(el, (0, _utils.getMouseEventOptions)('mouseenter', init));
}
}
_dom.fireEvent.pointerMove(element, init);
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseMove(element, (0, _utils.getMouseEventOptions)('mousemove', init));
}
}
function unhover(element, init, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to unhover element as it has or inherits pointer-events set to "none".');
}
if ((0, _utils.isLabelWithInternallyDisabledControl)(element)) return;
const parentElements = getParentElements(element);
_dom.fireEvent.pointerMove(element, init);
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseMove(element, (0, _utils.getMouseEventOptions)('mousemove', init));
}
_dom.fireEvent.pointerOut(element, init);
for (const el of parentElements) {
_dom.fireEvent.pointerLeave(el, init);
}
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseOut(element, (0, _utils.getMouseEventOptions)('mouseout', init));
for (const el of parentElements) {
_dom.fireEvent.mouseLeave(el, (0, _utils.getMouseEventOptions)('mouseleave', init));
}
}
}

View File

@@ -0,0 +1,25 @@
import { click, dblClick } from './click';
import { type } from './type';
import { clear } from './clear';
import { tab } from './tab';
import { hover, unhover } from './hover';
import { upload } from './upload';
import { paste } from './paste';
import { keyboard, specialCharMap } from './keyboard';
declare const userEvent: {
click: typeof click;
dblClick: typeof dblClick;
type: typeof type;
clear: typeof clear;
tab: typeof tab;
hover: typeof hover;
unhover: typeof unhover;
upload: typeof upload;
selectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: import("./utils").PointerOptions | undefined) => void;
deselectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: import("./utils").PointerOptions | undefined) => void;
paste: typeof paste;
keyboard: typeof keyboard;
};
export default userEvent;
export { specialCharMap as specialChars };
export type { keyboardKey } from './keyboard';

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
Object.defineProperty(exports, "specialChars", {
enumerable: true,
get: function () {
return _keyboard.specialCharMap;
}
});
var _click = require("./click");
var _type = require("./type");
var _clear = require("./clear");
var _tab = require("./tab");
var _hover = require("./hover");
var _upload = require("./upload");
var _selectOptions = require("./select-options");
var _paste = require("./paste");
var _keyboard = require("./keyboard");
const userEvent = {
click: _click.click,
dblClick: _click.dblClick,
type: _type.type,
clear: _clear.clear,
tab: _tab.tab,
hover: _hover.hover,
unhover: _hover.unhover,
upload: _upload.upload,
selectOptions: _selectOptions.selectOptions,
deselectOptions: _selectOptions.deselectOptions,
paste: _paste.paste,
keyboard: _keyboard.keyboard
};
var _default = userEvent;
exports.default = _default;

View File

@@ -0,0 +1,17 @@
import { keyboardKey, keyboardState } from './types';
export declare function getKeyEventProps(keyDef: keyboardKey, state: keyboardState): {
key: string | undefined;
code: string | undefined;
altKey: boolean;
ctrlKey: boolean;
metaKey: boolean;
shiftKey: boolean;
/** @deprecated use code instead */
keyCode: number | undefined;
};
export declare function getMouseEventProps(state: keyboardState): {
altKey: boolean;
ctrlKey: boolean;
metaKey: boolean;
shiftKey: boolean;
};

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getKeyEventProps = getKeyEventProps;
exports.getMouseEventProps = getMouseEventProps;
function getKeyEventProps(keyDef, state) {
var _keyDef$keyCode, _keyDef$key;
return {
key: keyDef.key,
code: keyDef.code,
altKey: state.modifiers.alt,
ctrlKey: state.modifiers.ctrl,
metaKey: state.modifiers.meta,
shiftKey: state.modifiers.shift,
/** @deprecated use code instead */
keyCode: (_keyDef$keyCode = keyDef.keyCode) != null ? _keyDef$keyCode : // istanbul ignore next
((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 ? keyDef.key.charCodeAt(0) : undefined
};
}
function getMouseEventProps(state) {
return {
altKey: state.modifiers.alt,
ctrlKey: state.modifiers.ctrl,
metaKey: state.modifiers.meta,
shiftKey: state.modifiers.shift
};
}

View File

@@ -0,0 +1,19 @@
import { keyboardKey, keyboardOptions } from './types';
/**
* Get the next key from keyMap
*
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
* Everything else will be interpreted as a typed character - e.g. `a`.
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
* Keeping the key pressed can be written as `{key>}`.
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
* Modifiers like `{shift}` imply being kept pressed. This can be turned of per `{shift/}`.
*/
export declare function getNextKeyDef(text: string, options: keyboardOptions): {
keyDef: keyboardKey;
consumedLength: number;
releasePrevious: boolean;
releaseSelf: boolean;
repeat: number;
};

View File

@@ -0,0 +1,174 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getNextKeyDef = getNextKeyDef;
var bracketDict;
(function (bracketDict) {
bracketDict["{"] = "}";
bracketDict["["] = "]";
})(bracketDict || (bracketDict = {}));
var legacyModifiers;
(function (legacyModifiers) {
legacyModifiers["alt"] = "alt";
legacyModifiers["ctrl"] = "ctrl";
legacyModifiers["meta"] = "meta";
legacyModifiers["shift"] = "shift";
})(legacyModifiers || (legacyModifiers = {}));
var legacyKeyMap;
/**
* Get the next key from keyMap
*
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
* Everything else will be interpreted as a typed character - e.g. `a`.
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
* Keeping the key pressed can be written as `{key>}`.
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
* Modifiers like `{shift}` imply being kept pressed. This can be turned of per `{shift/}`.
*/
(function (legacyKeyMap) {
legacyKeyMap["ctrl"] = "Control";
legacyKeyMap["del"] = "Delete";
legacyKeyMap["esc"] = "Escape";
legacyKeyMap["space"] = " ";
})(legacyKeyMap || (legacyKeyMap = {}));
function getNextKeyDef(text, options) {
var _options$keyboardMap$;
const {
type,
descriptor,
consumedLength,
releasePrevious,
releaseSelf,
repeat
} = readNextDescriptor(text);
const keyDef = (_options$keyboardMap$ = options.keyboardMap.find(def => {
if (type === '[') {
var _def$code;
return ((_def$code = def.code) == null ? void 0 : _def$code.toLowerCase()) === descriptor.toLowerCase();
} else if (type === '{') {
var _def$key;
const key = mapLegacyKey(descriptor);
return ((_def$key = def.key) == null ? void 0 : _def$key.toLowerCase()) === key.toLowerCase();
}
return def.key === descriptor;
})) != null ? _options$keyboardMap$ : {
key: 'Unknown',
code: 'Unknown',
[type === '[' ? 'code' : 'key']: descriptor
};
return {
keyDef,
consumedLength,
releasePrevious,
releaseSelf,
repeat
};
}
function readNextDescriptor(text) {
let pos = 0;
const startBracket = text[pos] in bracketDict ? text[pos] : '';
pos += startBracket.length; // `foo{{bar` is an escaped char at position 3,
// but `foo{{{>5}bar` should be treated as `{` pressed down for 5 keydowns.
const startBracketRepeated = startBracket ? text.match(new RegExp(`^\\${startBracket}+`))[0].length : 0;
const isEscapedChar = startBracketRepeated === 2 || startBracket === '{' && startBracketRepeated > 3;
const type = isEscapedChar ? '' : startBracket;
return {
type,
...(type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type))
};
}
function readPrintableChar(text, pos) {
const descriptor = text[pos];
assertDescriptor(descriptor, text, pos);
pos += descriptor.length;
return {
consumedLength: pos,
descriptor,
releasePrevious: false,
releaseSelf: true,
repeat: 1
};
}
function readTag(text, pos, startBracket) {
var _text$slice$match, _text$slice$match$, _text$slice$match2;
const releasePreviousModifier = text[pos] === '/' ? '/' : '';
pos += releasePreviousModifier.length;
const descriptor = (_text$slice$match = text.slice(pos).match(/^\w+/)) == null ? void 0 : _text$slice$match[0];
assertDescriptor(descriptor, text, pos);
pos += descriptor.length;
const repeatModifier = (_text$slice$match$ = (_text$slice$match2 = text.slice(pos).match(/^>\d+/)) == null ? void 0 : _text$slice$match2[0]) != null ? _text$slice$match$ : '';
pos += repeatModifier.length;
const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
pos += releaseSelfModifier.length;
const expectedEndBracket = bracketDict[startBracket];
const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
if (!endBracket) {
throw new Error(getErrorMessage([!repeatModifier && 'repeat modifier', !releaseSelfModifier && 'release modifier', `"${expectedEndBracket}"`].filter(Boolean).join(' or '), text[pos], text));
}
pos += endBracket.length;
return {
consumedLength: pos,
descriptor,
releasePrevious: !!releasePreviousModifier,
repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
releaseSelf: hasReleaseSelf(startBracket, descriptor, releaseSelfModifier, repeatModifier)
};
}
function assertDescriptor(descriptor, text, pos) {
if (!descriptor) {
throw new Error(getErrorMessage('key descriptor', text[pos], text));
}
}
function getEnumValue(f, key) {
return f[key];
}
function hasReleaseSelf(startBracket, descriptor, releaseSelfModifier, repeatModifier) {
if (releaseSelfModifier) {
return releaseSelfModifier === '/';
}
if (repeatModifier) {
return false;
}
if (startBracket === '{' && getEnumValue(legacyModifiers, descriptor.toLowerCase())) {
return false;
}
return true;
}
function mapLegacyKey(descriptor) {
var _getEnumValue;
return (_getEnumValue = getEnumValue(legacyKeyMap, descriptor)) != null ? _getEnumValue : descriptor;
}
function getErrorMessage(expected, found, text) {
return `Expected ${expected} but found "${found != null ? found : ''}" in "${text}"
See https://github.com/testing-library/user-event/blob/main/README.md#keyboardtext-options
for more information about how userEvent parses your input.`;
}

View File

@@ -0,0 +1,18 @@
import { keyboardState, keyboardOptions, keyboardKey } from './types';
export { specialCharMap } from './specialCharMap';
export type { keyboardOptions, keyboardKey };
export declare function keyboard(text: string, options?: Partial<keyboardOptions & {
keyboardState: keyboardState;
delay: 0;
}>): keyboardState;
export declare function keyboard(text: string, options: Partial<keyboardOptions & {
keyboardState: keyboardState;
delay: number;
}>): Promise<keyboardState>;
export declare function keyboardImplementationWrapper(text: string, config?: Partial<keyboardOptions & {
keyboardState: keyboardState;
}>): {
promise: Promise<void>;
state: keyboardState;
releaseAllKeys: () => void;
};

View File

@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keyboard = keyboard;
exports.keyboardImplementationWrapper = keyboardImplementationWrapper;
Object.defineProperty(exports, "specialCharMap", {
enumerable: true,
get: function () {
return _specialCharMap.specialCharMap;
}
});
var _dom = require("@testing-library/dom");
var _keyboardImplementation = require("./keyboardImplementation");
var _keyMap = require("./keyMap");
var _specialCharMap = require("./specialCharMap");
function keyboard(text, options) {
var _options$delay;
const {
promise,
state
} = keyboardImplementationWrapper(text, options);
if (((_options$delay = options == null ? void 0 : options.delay) != null ? _options$delay : 0) > 0) {
return (0, _dom.getConfig)().asyncWrapper(() => promise.then(() => state));
} else {
// prevent users from dealing with UnhandledPromiseRejectionWarning in sync call
promise.catch(console.error);
return state;
}
}
function keyboardImplementationWrapper(text, config = {}) {
const {
keyboardState: state = createKeyboardState(),
delay = 0,
document: doc = document,
autoModify = false,
keyboardMap = _keyMap.defaultKeyMap
} = config;
const options = {
delay,
document: doc,
autoModify,
keyboardMap
};
return {
promise: (0, _keyboardImplementation.keyboardImplementation)(text, options, state),
state,
releaseAllKeys: () => (0, _keyboardImplementation.releaseAllKeys)(options, state)
};
}
function createKeyboardState() {
return {
activeElement: null,
pressed: [],
carryChar: '',
modifiers: {
alt: false,
caps: false,
ctrl: false,
meta: false,
shift: false
}
};
}

View File

@@ -0,0 +1,5 @@
import { keyboardKey } from './types';
/**
* Mapping for a default US-104-QWERTY keyboard
*/
export declare const defaultKeyMap: keyboardKey[];

View File

@@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.defaultKeyMap = void 0;
var _types = require("./types");
/**
* Mapping for a default US-104-QWERTY keyboard
*/
const defaultKeyMap = [// alphanumeric keys
...'0123456789'.split('').map(c => ({
code: `Digit${c}`,
key: c
})), ...')!@#$%^&*('.split('').map((c, i) => ({
code: `Digit${i}`,
key: c,
shiftKey: true
})), ...'abcdefghijklmnopqrstuvwxyz'.split('').map(c => ({
code: `Key${c.toUpperCase()}`,
key: c
})), ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(c => ({
code: `Key${c}`,
key: c,
shiftKey: true
})), // alphanumeric block - functional
{
code: 'Space',
key: ' '
}, {
code: 'AltLeft',
key: 'Alt',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 18
}, {
code: 'AltRight',
key: 'Alt',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 18
}, {
code: 'ShiftLeft',
key: 'Shift',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 16
}, {
code: 'ShiftRight',
key: 'Shift',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 16
}, {
code: 'ControlLeft',
key: 'Control',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 17
}, {
code: 'ControlRight',
key: 'Control',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 17
}, {
code: 'MetaLeft',
key: 'Meta',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 93
}, {
code: 'MetaRight',
key: 'Meta',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 93
}, {
code: 'OSLeft',
key: 'OS',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 91
}, {
code: 'OSRight',
key: 'OS',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 91
}, {
code: 'CapsLock',
key: 'CapsLock',
keyCode: 20
}, {
code: 'Backspace',
key: 'Backspace',
keyCode: 8
}, {
code: 'Enter',
key: 'Enter',
keyCode: 13
}, // function
{
code: 'Escape',
key: 'Escape',
keyCode: 27
}, // arrows
{
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
}, {
code: 'ArrowDown',
key: 'ArrowDown',
keyCode: 40
}, {
code: 'ArrowLeft',
key: 'ArrowLeft',
keyCode: 37
}, {
code: 'ArrowRight',
key: 'ArrowRight',
keyCode: 39
}, // control pad
{
code: 'Home',
key: 'Home',
keyCode: 36
}, {
code: 'End',
key: 'End',
keyCode: 35
}, {
code: 'Delete',
key: 'Delete',
keyCode: 46
}, {
code: 'PageUp',
key: 'PageUp',
keyCode: 33
}, {
code: 'PageDown',
key: 'PageDown',
keyCode: 34
} // TODO: add mappings
];
exports.defaultKeyMap = defaultKeyMap;

View File

@@ -0,0 +1,3 @@
import { keyboardState, keyboardOptions } from './types';
export declare function keyboardImplementation(text: string, options: keyboardOptions, state: keyboardState): Promise<void>;
export declare function releaseAllKeys(options: keyboardOptions, state: keyboardState): void;

View File

@@ -0,0 +1,167 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keyboardImplementation = keyboardImplementation;
exports.releaseAllKeys = releaseAllKeys;
var _dom = require("@testing-library/dom");
var _utils = require("../utils");
var _getNextKeyDef = require("./getNextKeyDef");
var plugins = _interopRequireWildcard(require("./plugins"));
var _getEventProps = require("./getEventProps");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
async function keyboardImplementation(text, options, state) {
var _state$repeatKey;
const {
document
} = options;
const getCurrentElement = () => getActive(document);
const {
keyDef,
consumedLength,
releasePrevious,
releaseSelf,
repeat
} = (_state$repeatKey = state.repeatKey) != null ? _state$repeatKey : (0, _getNextKeyDef.getNextKeyDef)(text, options);
const replace = applyPlugins(plugins.replaceBehavior, keyDef, getCurrentElement(), options, state);
if (!replace) {
const pressed = state.pressed.find(p => p.keyDef === keyDef); // Release the key automatically if it was pressed before.
// Do not release the key on iterations on `state.repeatKey`.
if (pressed && !state.repeatKey) {
keyup(keyDef, getCurrentElement, options, state, pressed.unpreventedDefault);
}
if (!releasePrevious) {
const unpreventedDefault = keydown(keyDef, getCurrentElement, options, state);
if (unpreventedDefault && hasKeyPress(keyDef, state)) {
keypress(keyDef, getCurrentElement, options, state);
} // Release the key only on the last iteration on `state.repeatKey`.
if (releaseSelf && repeat <= 1) {
keyup(keyDef, getCurrentElement, options, state, unpreventedDefault);
}
}
}
if (repeat > 1) {
state.repeatKey = {
// don't consume again on the next iteration
consumedLength: 0,
keyDef,
releasePrevious,
releaseSelf,
repeat: repeat - 1
};
} else {
delete state.repeatKey;
}
if (text.length > consumedLength || repeat > 1) {
if (options.delay > 0) {
await (0, _utils.wait)(options.delay);
}
return keyboardImplementation(text.slice(consumedLength), options, state);
}
return void undefined;
}
function getActive(document) {
var _getActiveElement;
return (_getActiveElement = (0, _utils.getActiveElement)(document)) != null ? _getActiveElement :
/* istanbul ignore next */
document.body;
}
function releaseAllKeys(options, state) {
const getCurrentElement = () => getActive(options.document);
for (const k of state.pressed) {
keyup(k.keyDef, getCurrentElement, options, state, k.unpreventedDefault);
}
}
function keydown(keyDef, getCurrentElement, options, state) {
const element = getCurrentElement(); // clear carried characters when focus is moved
if (element !== state.activeElement) {
state.carryValue = undefined;
state.carryChar = '';
}
state.activeElement = element;
applyPlugins(plugins.preKeydownBehavior, keyDef, element, options, state);
const unpreventedDefault = _dom.fireEvent.keyDown(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
state.pressed.push({
keyDef,
unpreventedDefault
});
if (unpreventedDefault) {
// all default behavior like keypress/submit etc is applied to the currentElement
applyPlugins(plugins.keydownBehavior, keyDef, getCurrentElement(), options, state);
}
return unpreventedDefault;
}
function keypress(keyDef, getCurrentElement, options, state) {
const element = getCurrentElement();
const unpreventedDefault = _dom.fireEvent.keyPress(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
if (unpreventedDefault) {
applyPlugins(plugins.keypressBehavior, keyDef, getCurrentElement(), options, state);
}
}
function keyup(keyDef, getCurrentElement, options, state, unprevented) {
const element = getCurrentElement();
applyPlugins(plugins.preKeyupBehavior, keyDef, element, options, state);
const unpreventedDefault = _dom.fireEvent.keyUp(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
if (unprevented && unpreventedDefault) {
applyPlugins(plugins.keyupBehavior, keyDef, getCurrentElement(), options, state);
}
state.pressed = state.pressed.filter(k => k.keyDef !== keyDef);
applyPlugins(plugins.postKeyupBehavior, keyDef, element, options, state);
}
function applyPlugins(pluginCollection, keyDef, element, options, state) {
const plugin = pluginCollection.find(p => p.matches(keyDef, element, options, state));
if (plugin) {
plugin.handle(keyDef, element, options, state);
}
return !!plugin;
}
function hasKeyPress(keyDef, state) {
var _keyDef$key;
return (((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 || keyDef.key === 'Enter') && !state.modifiers.ctrl && !state.modifiers.alt;
}

View File

@@ -0,0 +1,6 @@
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-arrowpad-section
*/
import { behaviorPlugin } from '../types';
export declare const keydownBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keydownBehavior = void 0;
var _utils = require("../../utils");
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-arrowpad-section
*/
const keydownBehavior = [{
// TODO: implement for contentEditable
matches: (keyDef, element) => (keyDef.key === 'ArrowLeft' || keyDef.key === 'ArrowRight') && (0, _utils.isElementType)(element, ['input', 'textarea']),
handle: (keyDef, element) => {
var _ref;
const {
selectionStart,
selectionEnd
} = (0, _utils.getSelectionRange)(element);
const direction = keyDef.key === 'ArrowLeft' ? -1 : 1;
const newPos = (_ref = selectionStart === selectionEnd ? (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) + direction : direction < 0 ? selectionStart : selectionEnd) != null ? _ref :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}];
exports.keydownBehavior = keydownBehavior;

View File

@@ -0,0 +1,5 @@
/**
* This file should cover the behavior for keys that produce character input
*/
import { behaviorPlugin } from '../types';
export declare const keypressBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,195 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keypressBehavior = void 0;
var _dom = require("@testing-library/dom");
var _shared = require("../shared");
var _utils = require("../../utils");
/**
* This file should cover the behavior for keys that produce character input
*/
const keypressBehavior = [{
matches: (keyDef, element) => {
var _keyDef$key;
return ((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'time',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _state$carryValue;
let newEntry = keyDef.key;
const textToBeTyped = ((_state$carryValue = state.carryValue) != null ? _state$carryValue : '') + newEntry;
const timeNewEntry = (0, _utils.buildTimeValue)(textToBeTyped);
if ((0, _utils.isValidInputTimeValue)(element, timeNewEntry)) {
newEntry = timeNewEntry;
}
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(newEntry, element);
const prevValue = (0, _utils.getValue)(element); // this check was provided by fireInputEventIfNeeded
// TODO: verify if it is even needed by this handler
if (prevValue !== newValue) {
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
(0, _shared.fireChangeForInputTimeIfValid)(element, prevValue, timeNewEntry);
state.carryValue = textToBeTyped;
}
}, {
matches: (keyDef, element) => {
var _keyDef$key2;
return ((_keyDef$key2 = keyDef.key) == null ? void 0 : _keyDef$key2.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'date',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _state$carryValue2;
let newEntry = keyDef.key;
const textToBeTyped = ((_state$carryValue2 = state.carryValue) != null ? _state$carryValue2 : '') + newEntry;
const isValidToBeTyped = (0, _utils.isValidDateValue)(element, textToBeTyped);
if (isValidToBeTyped) {
newEntry = textToBeTyped;
}
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(newEntry, element);
const prevValue = (0, _utils.getValue)(element); // this check was provided by fireInputEventIfNeeded
// TODO: verify if it is even needed by this handler
if (prevValue !== newValue) {
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
if (isValidToBeTyped) {
_dom.fireEvent.change(element, {
target: {
value: textToBeTyped
}
});
}
state.carryValue = textToBeTyped;
}
}, {
matches: (keyDef, element) => {
var _keyDef$key3;
return ((_keyDef$key3 = keyDef.key) == null ? void 0 : _keyDef$key3.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'number',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _ref, _state$carryValue3, _newValue$match, _newValue$match2;
if (!/[\d.\-e]/.test(keyDef.key)) {
return;
}
const oldValue = (_ref = (_state$carryValue3 = state.carryValue) != null ? _state$carryValue3 : (0, _utils.getValue)(element)) != null ? _ref :
/* istanbul ignore next */
'';
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(keyDef.key, element, oldValue); // the browser allows some invalid input but not others
// it allows up to two '-' at any place before any 'e' or one directly following 'e'
// it allows one '.' at any place before e
const valueParts = newValue.split('e', 2);
if (Number((_newValue$match = newValue.match(/-/g)) == null ? void 0 : _newValue$match.length) > 2 || Number((_newValue$match2 = newValue.match(/\./g)) == null ? void 0 : _newValue$match2.length) > 1 || valueParts[1] && !/^-?\d*$/.test(valueParts[1])) {
return;
}
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
const appliedValue = (0, _utils.getValue)(element);
if (appliedValue === newValue) {
state.carryValue = undefined;
} else {
state.carryValue = newValue;
}
}
}, {
matches: (keyDef, element) => {
var _keyDef$key4;
return ((_keyDef$key4 = keyDef.key) == null ? void 0 : _keyDef$key4.length) === 1 && ((0, _utils.isElementType)(element, ['input', 'textarea'], {
readOnly: false
}) && !(0, _utils.isClickableInput)(element) || (0, _utils.isContentEditable)(element)) && (0, _utils.getSpaceUntilMaxLength)(element) !== 0;
},
handle: (keyDef, element) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(keyDef.key, element);
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && ((0, _utils.isElementType)(element, 'textarea', {
readOnly: false
}) || (0, _utils.isContentEditable)(element)) && (0, _utils.getSpaceUntilMaxLength)(element) !== 0,
handle: (keyDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('\n', element);
const inputType = (0, _utils.isContentEditable)(element) && !state.modifiers.shift ? 'insertParagraph' : 'insertLineBreak';
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType
}
});
}
}];
exports.keypressBehavior = keypressBehavior;

View File

@@ -0,0 +1,6 @@
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-controlpad-section
*/
import { behaviorPlugin } from '../types';
export declare const keydownBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keydownBehavior = void 0;
var _utils = require("../../utils");
var _shared = require("../shared");
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-controlpad-section
*/
const keydownBehavior = [{
matches: (keyDef, element) => (keyDef.key === 'Home' || keyDef.key === 'End') && ((0, _utils.isElementType)(element, ['input', 'textarea']) || (0, _utils.isContentEditable)(element)),
handle: (keyDef, element) => {
// This could probably been improved by collapsing a selection range
if (keyDef.key === 'Home') {
(0, _utils.setSelectionRange)(element, 0, 0);
} else {
var _getValue$length, _getValue;
const newPos = (_getValue$length = (_getValue = (0, _utils.getValue)(element)) == null ? void 0 : _getValue.length) != null ? _getValue$length :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}
}, {
matches: (keyDef, element) => (keyDef.key === 'PageUp' || keyDef.key === 'PageDown') && (0, _utils.isElementType)(element, ['input']),
handle: (keyDef, element) => {
// This could probably been improved by collapsing a selection range
if (keyDef.key === 'PageUp') {
(0, _utils.setSelectionRange)(element, 0, 0);
} else {
var _getValue$length2, _getValue2;
const newPos = (_getValue$length2 = (_getValue2 = (0, _utils.getValue)(element)) == null ? void 0 : _getValue2.length) != null ? _getValue$length2 :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}
}, {
matches: (keyDef, element) => keyDef.key === 'Delete' && (0, _utils.isEditable)(element) && !(0, _utils.isCursorAtEnd)(element),
handle: (keDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('', element, state.carryValue, undefined, 'forward');
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType: 'deleteContentForward'
}
});
(0, _shared.carryValue)(element, state, newValue);
}
}];
exports.keydownBehavior = keydownBehavior;

View File

@@ -0,0 +1,11 @@
/**
* This file should contain behavior for functional keys as described here:
* https://w3c.github.io/uievents-code/#key-alphanumeric-functional
*/
import { behaviorPlugin } from '../types';
export declare const preKeydownBehavior: behaviorPlugin[];
export declare const keydownBehavior: behaviorPlugin[];
export declare const keypressBehavior: behaviorPlugin[];
export declare const preKeyupBehavior: behaviorPlugin[];
export declare const keyupBehavior: behaviorPlugin[];
export declare const postKeyupBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,131 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.preKeyupBehavior = exports.preKeydownBehavior = exports.postKeyupBehavior = exports.keyupBehavior = exports.keypressBehavior = exports.keydownBehavior = void 0;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
var _getEventProps = require("../getEventProps");
var _shared = require("../shared");
/**
* This file should contain behavior for functional keys as described here:
* https://w3c.github.io/uievents-code/#key-alphanumeric-functional
*/
const modifierKeys = {
Alt: 'alt',
Control: 'ctrl',
Shift: 'shift',
Meta: 'meta'
};
const preKeydownBehavior = [// modifierKeys switch on the modifier BEFORE the keydown event
...Object.entries(modifierKeys).map(([key, modKey]) => ({
matches: keyDef => keyDef.key === key,
handle: (keyDef, element, options, state) => {
state.modifiers[modKey] = true;
}
})), // AltGraph produces an extra keydown for Control
// The modifier does not change
{
matches: keyDef => keyDef.key === 'AltGraph',
handle: (keyDef, element, options, state) => {
var _options$keyboardMap$;
const ctrlKeyDef = (_options$keyboardMap$ = options.keyboardMap.find(k => k.key === 'Control')) != null ? _options$keyboardMap$ :
/* istanbul ignore next */
{
key: 'Control',
code: 'Control'
};
_dom.fireEvent.keyDown(element, (0, _getEventProps.getKeyEventProps)(ctrlKeyDef, state));
}
}];
exports.preKeydownBehavior = preKeydownBehavior;
const keydownBehavior = [{
matches: keyDef => keyDef.key === 'CapsLock',
handle: (keyDef, element, options, state) => {
state.modifiers.caps = !state.modifiers.caps;
}
}, {
matches: (keyDef, element) => keyDef.key === 'Backspace' && (0, _utils.isEditable)(element) && !(0, _utils.isCursorAtStart)(element),
handle: (keyDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('', element, state.carryValue, undefined, 'backward');
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType: 'deleteContentBackward'
}
});
(0, _shared.carryValue)(element, state, newValue);
}
}];
exports.keydownBehavior = keydownBehavior;
const keypressBehavior = [{
matches: (keyDef, element) => keyDef.key === 'Enter' && (0, _utils.isElementType)(element, 'input') && ['checkbox', 'radio'].includes(element.type),
handle: (keyDef, element) => {
const form = element.form;
if ((0, _utils.hasFormSubmit)(form)) {
_dom.fireEvent.submit(form);
}
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && ((0, _utils.isClickableInput)(element) || // Links with href defined should handle Enter the same as a click
(0, _utils.isElementType)(element, 'a') && Boolean(element.href)),
handle: (keyDef, element, options, state) => {
_dom.fireEvent.click(element, (0, _getEventProps.getMouseEventProps)(state));
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && (0, _utils.isElementType)(element, 'input'),
handle: (keyDef, element) => {
const form = element.form;
if (form && (form.querySelectorAll('input').length === 1 || (0, _utils.hasFormSubmit)(form))) {
_dom.fireEvent.submit(form);
}
}
}];
exports.keypressBehavior = keypressBehavior;
const preKeyupBehavior = [// modifierKeys switch off the modifier BEFORE the keyup event
...Object.entries(modifierKeys).map(([key, modKey]) => ({
matches: keyDef => keyDef.key === key,
handle: (keyDef, element, options, state) => {
state.modifiers[modKey] = false;
}
}))];
exports.preKeyupBehavior = preKeyupBehavior;
const keyupBehavior = [{
matches: (keyDef, element) => keyDef.key === ' ' && (0, _utils.isClickableInput)(element),
handle: (keyDef, element, options, state) => {
_dom.fireEvent.click(element, (0, _getEventProps.getMouseEventProps)(state));
}
}];
exports.keyupBehavior = keyupBehavior;
const postKeyupBehavior = [// AltGraph produces an extra keyup for Control
// The modifier does not change
{
matches: keyDef => keyDef.key === 'AltGraph',
handle: (keyDef, element, options, state) => {
var _options$keyboardMap$2;
const ctrlKeyDef = (_options$keyboardMap$2 = options.keyboardMap.find(k => k.key === 'Control')) != null ? _options$keyboardMap$2 :
/* istanbul ignore next */
{
key: 'Control',
code: 'Control'
};
_dom.fireEvent.keyUp(element, (0, _getEventProps.getKeyEventProps)(ctrlKeyDef, state));
}
}];
exports.postKeyupBehavior = postKeyupBehavior;

View File

@@ -0,0 +1,8 @@
import { behaviorPlugin } from '../types';
export declare const replaceBehavior: behaviorPlugin[];
export declare const preKeydownBehavior: behaviorPlugin[];
export declare const keydownBehavior: behaviorPlugin[];
export declare const keypressBehavior: behaviorPlugin[];
export declare const preKeyupBehavior: behaviorPlugin[];
export declare const keyupBehavior: behaviorPlugin[];
export declare const postKeyupBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.replaceBehavior = exports.preKeyupBehavior = exports.preKeydownBehavior = exports.postKeyupBehavior = exports.keyupBehavior = exports.keypressBehavior = exports.keydownBehavior = void 0;
var _utils = require("../../utils");
var arrowKeys = _interopRequireWildcard(require("./arrow"));
var controlKeys = _interopRequireWildcard(require("./control"));
var characterKeys = _interopRequireWildcard(require("./character"));
var functionalKeys = _interopRequireWildcard(require("./functional"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const replaceBehavior = [{
matches: (keyDef, element) => keyDef.key === 'selectall' && (0, _utils.isElementType)(element, ['input', 'textarea']),
handle: (keyDef, element, options, state) => {
var _state$carryValue;
(0, _utils.setSelectionRange)(element, 0, ((_state$carryValue = state.carryValue) != null ? _state$carryValue : element.value).length);
}
}];
exports.replaceBehavior = replaceBehavior;
const preKeydownBehavior = [...functionalKeys.preKeydownBehavior];
exports.preKeydownBehavior = preKeydownBehavior;
const keydownBehavior = [...arrowKeys.keydownBehavior, ...controlKeys.keydownBehavior, ...functionalKeys.keydownBehavior];
exports.keydownBehavior = keydownBehavior;
const keypressBehavior = [...functionalKeys.keypressBehavior, ...characterKeys.keypressBehavior];
exports.keypressBehavior = keypressBehavior;
const preKeyupBehavior = [...functionalKeys.preKeyupBehavior];
exports.preKeyupBehavior = preKeyupBehavior;
const keyupBehavior = [...functionalKeys.keyupBehavior];
exports.keyupBehavior = keyupBehavior;
const postKeyupBehavior = [...functionalKeys.postKeyupBehavior];
exports.postKeyupBehavior = postKeyupBehavior;

View File

@@ -0,0 +1,2 @@
import { keyboardState } from '../types';
export declare function carryValue(element: Element, state: keyboardState, newValue: string): void;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.carryValue = carryValue;
var _utils = require("../../utils");
function carryValue(element, state, newValue) {
const value = (0, _utils.getValue)(element);
state.carryValue = value !== newValue && value === '' && (0, _utils.hasUnreliableEmptyValue)(element) ? newValue : undefined;
}

View File

@@ -0,0 +1,3 @@
export declare function fireChangeForInputTimeIfValid(el: HTMLInputElement & {
type: 'time';
}, prevValue: unknown, timeNewEntry: string): void;

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fireChangeForInputTimeIfValid = fireChangeForInputTimeIfValid;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
function fireChangeForInputTimeIfValid(el, prevValue, timeNewEntry) {
if ((0, _utils.isValidInputTimeValue)(el, timeNewEntry) && prevValue !== timeNewEntry) {
_dom.fireEvent.change(el, {
target: {
value: timeNewEntry
}
});
}
}

View File

@@ -0,0 +1,17 @@
import { fireEvent } from '@testing-library/dom';
export declare function fireInputEvent(element: HTMLElement, { newValue, newSelectionStart, eventOverrides, }: {
newValue: string;
newSelectionStart: number;
eventOverrides: Partial<Parameters<typeof fireEvent>[1]> & {
[k: string]: unknown;
};
}): void;
declare const initial: unique symbol;
declare const onBlur: unique symbol;
declare global {
interface Element {
[initial]?: string;
[onBlur]?: EventListener;
}
}
export {};

View File

@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fireInputEvent = fireInputEvent;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
function fireInputEvent(element, {
newValue,
newSelectionStart,
eventOverrides
}) {
// apply the changes before firing the input event, so that input handlers can access the altered dom and selection
if ((0, _utils.isContentEditable)(element)) {
applyNative(element, 'textContent', newValue);
} else
/* istanbul ignore else */
if ((0, _utils.isElementType)(element, ['input', 'textarea'])) {
applyNative(element, 'value', newValue);
} else {
// TODO: properly type guard
throw new Error('Invalid Element');
}
setSelectionRangeAfterInput(element, newSelectionStart);
_dom.fireEvent.input(element, { ...eventOverrides
});
setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart);
}
function setSelectionRangeAfterInput(element, newSelectionStart) {
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
function setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart) {
const value = (0, _utils.getValue)(element); // don't apply this workaround on elements that don't necessarily report the visible value - e.g. number
// TODO: this could probably be only applied when there is keyboardState.carryValue
const isUnreliableValue = value === '' && (0, _utils.hasUnreliableEmptyValue)(element);
if (!isUnreliableValue && value === newValue) {
const {
selectionStart
} = (0, _utils.getSelectionRange)(element);
if (selectionStart === value.length) {
// The value was changed as expected, but the cursor was moved to the end
// TODO: this could probably be only applied when we work around a framework setter on the element in applyNative
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
}
}
const initial = Symbol('initial input value/textContent');
const onBlur = Symbol('onBlur');
/**
* React tracks the changes on element properties.
* This workaround tries to alter the DOM element without React noticing,
* so that it later picks up the change.
*
* @see https://github.com/facebook/react/blob/148f8e497c7d37a3c7ab99f01dec2692427272b1/packages/react-dom/src/client/inputValueTracking.js#L51-L104
*/
function applyNative(element, propName, propValue) {
const descriptor = Object.getOwnPropertyDescriptor(element, propName);
const nativeDescriptor = Object.getOwnPropertyDescriptor(element.constructor.prototype, propName);
if (descriptor && nativeDescriptor) {
Object.defineProperty(element, propName, nativeDescriptor);
} // Keep track of the initial value to determine if a change event should be dispatched.
// CONSTRAINT: We can not determine what happened between focus event and our first API call.
if (element[initial] === undefined) {
element[initial] = String(element[propName]);
}
element[propName] = propValue; // Add an event listener for the blur event to the capture phase on the window.
// CONSTRAINT: Currently there is no cross-platform solution to unshift the event handler stack.
// Our change event might occur after other event handlers on the blur event have been processed.
if (!element[onBlur]) {
var _element$ownerDocumen;
(_element$ownerDocumen = element.ownerDocument.defaultView) == null ? void 0 : _element$ownerDocumen.addEventListener('blur', element[onBlur] = () => {
const initV = element[initial]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[onBlur]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[initial];
if (String(element[propName]) !== initV) {
_dom.fireEvent.change(element);
}
}, {
capture: true,
once: true
});
}
if (descriptor) {
Object.defineProperty(element, propName, descriptor);
}
}

View File

@@ -0,0 +1,3 @@
export * from './carryValue';
export * from './fireChangeForInputTimeIfValid';
export * from './fireInputEvent';

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _carryValue = require("./carryValue");
Object.keys(_carryValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _carryValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _carryValue[key];
}
});
});
var _fireChangeForInputTimeIfValid = require("./fireChangeForInputTimeIfValid");
Object.keys(_fireChangeForInputTimeIfValid).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _fireChangeForInputTimeIfValid[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _fireChangeForInputTimeIfValid[key];
}
});
});
var _fireInputEvent = require("./fireInputEvent");
Object.keys(_fireInputEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _fireInputEvent[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _fireInputEvent[key];
}
});
});

View File

@@ -0,0 +1,22 @@
/**
* @deprecated This list of strings with special meaning is no longer necessary
* as we've introduced a standardized way to describe any keystroke for `userEvent`.
* @see https://testing-library.com/docs/ecosystem-user-event#keyboardtext-options
*/
export declare const specialCharMap: {
readonly arrowLeft: "{arrowleft}";
readonly arrowRight: "{arrowright}";
readonly arrowDown: "{arrowdown}";
readonly arrowUp: "{arrowup}";
readonly enter: "{enter}";
readonly escape: "{esc}";
readonly delete: "{del}";
readonly backspace: "{backspace}";
readonly home: "{home}";
readonly end: "{end}";
readonly selectAll: "{selectall}";
readonly space: "{space}";
readonly whitespace: " ";
readonly pageUp: "{pageUp}";
readonly pageDown: "{pageDown}";
};

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.specialCharMap = void 0;
/**
* @deprecated This list of strings with special meaning is no longer necessary
* as we've introduced a standardized way to describe any keystroke for `userEvent`.
* @see https://testing-library.com/docs/ecosystem-user-event#keyboardtext-options
*/
const specialCharMap = {
arrowLeft: '{arrowleft}',
arrowRight: '{arrowright}',
arrowDown: '{arrowdown}',
arrowUp: '{arrowup}',
enter: '{enter}',
escape: '{esc}',
delete: '{del}',
backspace: '{backspace}',
home: '{home}',
end: '{end}',
selectAll: '{selectall}',
space: '{space}',
whitespace: ' ',
pageUp: '{pageUp}',
pageDown: '{pageDown}'
};
exports.specialCharMap = specialCharMap;

View File

@@ -0,0 +1,77 @@
import { getNextKeyDef } from './getNextKeyDef';
/**
* @internal Do not create/alter this by yourself as this type might be subject to changes.
*/
export declare type keyboardState = {
/**
All keys that have been pressed and not been lifted up yet.
*/
pressed: {
keyDef: keyboardKey;
unpreventedDefault: boolean;
}[];
/**
Active modifiers
*/
modifiers: {
alt: boolean;
caps: boolean;
ctrl: boolean;
meta: boolean;
shift: boolean;
};
/**
The element the keyboard input is performed on.
Some behavior might differ if the activeElement changes between corresponding keyboard events.
*/
activeElement: Element | null;
/**
For HTMLInputElements type='number':
If the last input char is '.', '-' or 'e',
the IDL value attribute does not reflect the input value.
*/
carryValue?: string;
/**
Carry over characters to following key handlers.
E.g. ^1
*/
carryChar: string;
/**
Repeat keydown and keypress event
*/
repeatKey?: ReturnType<typeof getNextKeyDef>;
};
export declare type keyboardOptions = {
/** Document in which to perform the events */
document: Document;
/** Delay between keystrokes */
delay: number;
/** Add modifiers for given keys - not implemented yet */
autoModify: boolean;
/** Keyboard layout to use */
keyboardMap: keyboardKey[];
};
export declare enum DOM_KEY_LOCATION {
STANDARD = 0,
LEFT = 1,
RIGHT = 2,
NUMPAD = 3
}
export interface keyboardKey {
/** Physical location on a keyboard */
code?: string;
/** Character or functional key descriptor */
key?: string;
/** Location on the keyboard for keys with multiple representation */
location?: DOM_KEY_LOCATION;
/** keyCode for legacy support */
keyCode?: number;
/** Does the character in `key` require/imply AltRight to be pressed? */
altGr?: boolean;
/** Does the character in `key` require/imply a shiftKey to be pressed? */
shift?: boolean;
}
export interface behaviorPlugin {
matches: (keyDef: keyboardKey, element: Element, options: keyboardOptions, state: keyboardState) => boolean;
handle: (keyDef: keyboardKey, element: Element, options: keyboardOptions, state: keyboardState) => void;
}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DOM_KEY_LOCATION = void 0;
/**
* @internal Do not create/alter this by yourself as this type might be subject to changes.
*/
let DOM_KEY_LOCATION;
exports.DOM_KEY_LOCATION = DOM_KEY_LOCATION;
(function (DOM_KEY_LOCATION) {
DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["LEFT"] = 1] = "LEFT";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["RIGHT"] = 2] = "RIGHT";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["NUMPAD"] = 3] = "NUMPAD";
})(DOM_KEY_LOCATION || (exports.DOM_KEY_LOCATION = DOM_KEY_LOCATION = {}));

View File

@@ -0,0 +1,6 @@
interface pasteOptions {
initialSelectionStart?: number;
initialSelectionEnd?: number;
}
declare function paste(element: HTMLElement, text: string, init?: ClipboardEventInit, { initialSelectionStart, initialSelectionEnd }?: pasteOptions): void;
export { paste };

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.paste = paste;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
function isSupportedElement(element) {
return (0, _utils.isElementType)(element, 'input') && Boolean(_utils.editableInputTypes[element.type]) || (0, _utils.isElementType)(element, 'textarea');
}
function paste(element, text, init, {
initialSelectionStart,
initialSelectionEnd
} = {}) {
// TODO: implement for contenteditable
if (!isSupportedElement(element)) {
throw new TypeError(`The given ${element.tagName} element is currently unsupported.
A PR extending this implementation would be very much welcome at https://github.com/testing-library/user-event`);
}
if ((0, _utils.isDisabled)(element)) {
return;
}
(0, _utils.eventWrapper)(() => element.focus()); // by default, a new element has it's selection start and end at 0
// but most of the time when people call "paste", they expect it to paste
// at the end of the current input value. So, if the selection start
// and end are both the default of 0, then we'll go ahead and change
// them to the length of the current value.
// the only time it would make sense to pass the initialSelectionStart or
// initialSelectionEnd is if you have an input with a value and want to
// explicitely start typing with the cursor at 0. Not super common.
if (element.selectionStart === 0 && element.selectionEnd === 0) {
(0, _utils.setSelectionRange)(element, initialSelectionStart != null ? initialSelectionStart : element.value.length, initialSelectionEnd != null ? initialSelectionEnd : element.value.length);
}
_dom.fireEvent.paste(element, init);
if (element.readOnly) {
return;
}
text = text.substr(0, (0, _utils.getSpaceUntilMaxLength)(element));
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(text, element);
_dom.fireEvent.input(element, {
inputType: 'insertFromPaste',
target: {
value: newValue
}
});
(0, _utils.setSelectionRange)(element, // TODO: investigate why the selection caused by invalid parameters was expected
{
newSelectionStart,
selectionEnd: newSelectionStart
}, {});
}

View File

@@ -0,0 +1,4 @@
import { PointerOptions } from './utils';
declare const selectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: PointerOptions | undefined) => void;
declare const deselectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: PointerOptions | undefined) => void;
export { selectOptions, deselectOptions };

View File

@@ -0,0 +1,144 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.selectOptions = exports.deselectOptions = void 0;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
var _click = require("./click");
var _focus = require("./focus");
var _hover = require("./hover");
function selectOptionsBase(newValue, select, values, init, {
skipPointerEventsCheck = false
} = {}) {
if (!newValue && !select.multiple) {
throw (0, _dom.getConfig)().getElementError(`Unable to deselect an option in a non-multiple select. Use selectOptions to change the selection instead.`, select);
}
const valArray = Array.isArray(values) ? values : [values];
const allOptions = Array.from(select.querySelectorAll('option, [role="option"]'));
const selectedOptions = valArray.map(val => {
if (typeof val !== 'string' && allOptions.includes(val)) {
return val;
} else {
const matchingOption = allOptions.find(o => o.value === val || o.innerHTML === val);
if (matchingOption) {
return matchingOption;
} else {
throw (0, _dom.getConfig)().getElementError(`Value "${String(val)}" not found in options`, select);
}
}
}).filter(option => !(0, _utils.isDisabled)(option));
if ((0, _utils.isDisabled)(select) || !selectedOptions.length) return;
if ((0, _utils.isElementType)(select, 'select')) {
if (select.multiple) {
for (const option of selectedOptions) {
const withPointerEvents = skipPointerEventsCheck ? true : (0, _utils.hasPointerEvents)(option); // events fired for multiple select are weird. Can't use hover...
if (withPointerEvents) {
_dom.fireEvent.pointerOver(option, init);
_dom.fireEvent.pointerEnter(select, init);
_dom.fireEvent.mouseOver(option);
_dom.fireEvent.mouseEnter(select);
_dom.fireEvent.pointerMove(option, init);
_dom.fireEvent.mouseMove(option, init);
_dom.fireEvent.pointerDown(option, init);
_dom.fireEvent.mouseDown(option, init);
}
(0, _focus.focus)(select);
if (withPointerEvents) {
_dom.fireEvent.pointerUp(option, init);
_dom.fireEvent.mouseUp(option, init);
}
selectOption(option);
if (withPointerEvents) {
_dom.fireEvent.click(option, init);
}
}
} else if (selectedOptions.length === 1) {
const withPointerEvents = skipPointerEventsCheck ? true : (0, _utils.hasPointerEvents)(select); // the click to open the select options
if (withPointerEvents) {
(0, _click.click)(select, init, {
skipPointerEventsCheck
});
} else {
(0, _focus.focus)(select);
}
selectOption(selectedOptions[0]);
if (withPointerEvents) {
// the browser triggers another click event on the select for the click on the option
// this second click has no 'down' phase
_dom.fireEvent.pointerOver(select, init);
_dom.fireEvent.pointerEnter(select, init);
_dom.fireEvent.mouseOver(select);
_dom.fireEvent.mouseEnter(select);
_dom.fireEvent.pointerUp(select, init);
_dom.fireEvent.mouseUp(select, init);
_dom.fireEvent.click(select, init);
}
} else {
throw (0, _dom.getConfig)().getElementError(`Cannot select multiple options on a non-multiple select`, select);
}
} else if (select.getAttribute('role') === 'listbox') {
selectedOptions.forEach(option => {
(0, _hover.hover)(option, init, {
skipPointerEventsCheck
});
(0, _click.click)(option, init, {
skipPointerEventsCheck
});
(0, _hover.unhover)(option, init, {
skipPointerEventsCheck
});
});
} else {
throw (0, _dom.getConfig)().getElementError(`Cannot select options on elements that are neither select nor listbox elements`, select);
}
function selectOption(option) {
option.selected = newValue;
(0, _dom.fireEvent)(select, (0, _dom.createEvent)('input', select, {
bubbles: true,
cancelable: false,
composed: true,
...init
}));
_dom.fireEvent.change(select, init);
}
}
const selectOptions = selectOptionsBase.bind(null, true);
exports.selectOptions = selectOptions;
const deselectOptions = selectOptionsBase.bind(null, false);
exports.deselectOptions = deselectOptions;

View File

@@ -0,0 +1,6 @@
interface tabOptions {
shift?: boolean;
focusTrap?: Document | Element;
}
declare function tab({ shift, focusTrap }?: tabOptions): void;
export { tab };

View File

@@ -0,0 +1,145 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.tab = tab;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
var _focus = require("./focus");
var _blur = require("./blur");
function getNextElement(currentIndex, shift, elements, focusTrap) {
if ((0, _utils.isDocument)(focusTrap) && (currentIndex === 0 && shift || currentIndex === elements.length - 1 && !shift)) {
return focusTrap.body;
}
const nextIndex = shift ? currentIndex - 1 : currentIndex + 1;
const defaultIndex = shift ? elements.length - 1 : 0;
return elements[nextIndex] || elements[defaultIndex];
}
function tab({
shift = false,
focusTrap
} = {}) {
var _focusTrap$ownerDocum, _focusTrap;
const doc = (_focusTrap$ownerDocum = (_focusTrap = focusTrap) == null ? void 0 : _focusTrap.ownerDocument) != null ? _focusTrap$ownerDocum : document;
const previousElement = (0, _utils.getActiveElement)(doc);
if (!focusTrap) {
focusTrap = doc;
}
const focusableElements = focusTrap.querySelectorAll(_utils.FOCUSABLE_SELECTOR);
const enabledElements = Array.from(focusableElements).filter(el => el === previousElement || el.getAttribute('tabindex') !== '-1' && !(0, _utils.isDisabled)(el) && // Hidden elements are not tabable
(0, _utils.isVisible)(el));
if (enabledElements.length === 0) return;
const orderedElements = enabledElements.map((el, idx) => ({
el,
idx
})).sort((a, b) => {
// tabindex has no effect if the active element has tabindex="-1"
if (previousElement && previousElement.getAttribute('tabindex') === '-1') {
return a.idx - b.idx;
}
const tabIndexA = Number(a.el.getAttribute('tabindex'));
const tabIndexB = Number(b.el.getAttribute('tabindex'));
const diff = tabIndexA - tabIndexB;
return diff === 0 ? a.idx - b.idx : diff;
}).map(({
el
}) => el); // TODO: verify/remove type casts
const checkedRadio = {};
let prunedElements = [];
orderedElements.forEach(currentElement => {
// For radio groups keep only the active radio
// If there is no active radio, keep only the checked radio
// If there is no checked radio, treat like everything else
const el = currentElement;
if (el.type === 'radio' && el.name) {
// If the active element is part of the group, add only that
const prev = previousElement;
if (prev && prev.type === el.type && prev.name === el.name) {
if (el === prev) {
prunedElements.push(el);
}
return;
} // If we stumble upon a checked radio, remove the others
if (el.checked) {
prunedElements = prunedElements.filter(e => e.type !== el.type || e.name !== el.name);
prunedElements.push(el);
checkedRadio[el.name] = el;
return;
} // If we already found the checked one, skip
if (typeof checkedRadio[el.name] !== 'undefined') {
return;
}
}
prunedElements.push(el);
});
const index = prunedElements.findIndex(el => el === previousElement);
const nextElement = getNextElement(index, shift, prunedElements, focusTrap);
const shiftKeyInit = {
key: 'Shift',
keyCode: 16,
shiftKey: true
};
const tabKeyInit = {
key: 'Tab',
keyCode: 9,
shiftKey: shift
};
let continueToTab = true; // not sure how to make it so there's no previous element...
// istanbul ignore else
if (previousElement) {
// preventDefault on the shift key makes no difference
if (shift) _dom.fireEvent.keyDown(previousElement, { ...shiftKeyInit
});
continueToTab = _dom.fireEvent.keyDown(previousElement, { ...tabKeyInit
});
}
const keyUpTarget = !continueToTab && previousElement ? previousElement : nextElement;
if (continueToTab) {
if (nextElement === doc.body) {
/* istanbul ignore else */
if (previousElement) {
(0, _blur.blur)(previousElement);
}
} else {
(0, _focus.focus)(nextElement);
}
}
_dom.fireEvent.keyUp(keyUpTarget, { ...tabKeyInit
});
if (shift) {
_dom.fireEvent.keyUp(keyUpTarget, { ...shiftKeyInit,
shiftKey: false
});
}
}
/*
eslint
complexity: "off",
max-statements: "off",
*/

View File

@@ -0,0 +1,7 @@
import { typeOptions } from './typeImplementation';
export declare function type(element: Element, text: string, options?: typeOptions & {
delay?: 0;
}): void;
export declare function type(element: Element, text: string, options: typeOptions & {
delay: number;
}): Promise<void>;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.type = type;
var _dom = require("@testing-library/dom");
var _typeImplementation = require("./typeImplementation");
// this needs to be wrapped in the event/asyncWrapper for React's act and angular's change detection
// depending on whether it will be async.
function type(element, text, {
delay = 0,
...options
} = {}) {
// we do not want to wrap in the asyncWrapper if we're not
// going to actually be doing anything async, so we only wrap
// if the delay is greater than 0
if (delay > 0) {
return (0, _dom.getConfig)().asyncWrapper(() => (0, _typeImplementation.typeImplementation)(element, text, {
delay,
...options
}));
} else {
return void (0, _typeImplementation.typeImplementation)(element, text, {
delay,
...options
}) // prevents users from dealing with UnhandledPromiseRejectionWarning
.catch(console.error);
}
}

View File

@@ -0,0 +1,10 @@
export interface typeOptions {
delay?: number;
skipClick?: boolean;
skipAutoClose?: boolean;
initialSelectionStart?: number;
initialSelectionEnd?: number;
}
export declare function typeImplementation(element: Element, text: string, { delay, skipClick, skipAutoClose, initialSelectionStart, initialSelectionEnd, }: typeOptions & {
delay: number;
}): Promise<void>;

View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.typeImplementation = typeImplementation;
var _utils = require("../utils");
var _click = require("../click");
var _keyboard = require("../keyboard");
async function typeImplementation(element, text, {
delay,
skipClick = false,
skipAutoClose = false,
initialSelectionStart = undefined,
initialSelectionEnd = undefined
}) {
// TODO: properly type guard
// we use this workaround for now to prevent changing behavior
if (element.disabled) return;
if (!skipClick) (0, _click.click)(element); // The focused element could change between each event, so get the currently active element each time
const currentElement = () => (0, _utils.getActiveElement)(element.ownerDocument); // by default, a new element has its selection start and end at 0
// but most of the time when people call "type", they expect it to type
// at the end of the current input value. So, if the selection start
// and end are both the default of 0, then we'll go ahead and change
// them to the length of the current value.
// the only time it would make sense to pass the initialSelectionStart or
// initialSelectionEnd is if you have an input with a value and want to
// explicitly start typing with the cursor at 0. Not super common.
const value = (0, _utils.getValue)(currentElement());
const {
selectionStart,
selectionEnd
} = (0, _utils.getSelectionRange)(element);
if (value != null && (selectionStart === null || selectionStart === 0) && (selectionEnd === null || selectionEnd === 0)) {
(0, _utils.setSelectionRange)(currentElement(), initialSelectionStart != null ? initialSelectionStart : value.length, initialSelectionEnd != null ? initialSelectionEnd : value.length);
}
const {
promise,
releaseAllKeys
} = (0, _keyboard.keyboardImplementationWrapper)(text, {
delay,
document: element.ownerDocument
});
if (delay > 0) {
await promise;
}
if (!skipAutoClose) {
releaseAllKeys();
} // eslint-disable-next-line consistent-return -- we need to return the internal Promise so that it is catchable if we don't await
return promise;
}

View File

@@ -0,0 +1,9 @@
interface uploadInit {
clickInit?: MouseEventInit;
changeInit?: EventInit;
}
interface uploadOptions {
applyAccept?: boolean;
}
declare function upload(element: HTMLElement, fileOrFiles: File | File[], init?: uploadInit, { applyAccept }?: uploadOptions): void;
export { upload };

View File

@@ -0,0 +1,98 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.upload = upload;
var _dom = require("@testing-library/dom");
var _click = require("./click");
var _blur = require("./blur");
var _focus = require("./focus");
var _utils = require("./utils");
function upload(element, fileOrFiles, init, {
applyAccept = false
} = {}) {
var _input$files;
const input = (0, _utils.isElementType)(element, 'label') ? element.control : element;
if (!input || !(0, _utils.isElementType)(input, 'input', {
type: 'file'
})) {
throw new TypeError(`The ${input === element ? 'given' : 'associated'} ${input == null ? void 0 : input.tagName} element does not accept file uploads`);
}
if ((0, _utils.isDisabled)(element)) return;
(0, _click.click)(element, init == null ? void 0 : init.clickInit);
const files = (Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles]).filter(file => !applyAccept || isAcceptableFile(file, input.accept)).slice(0, input.multiple ? undefined : 1); // blur fires when the file selector pops up
(0, _blur.blur)(element); // focus fires when they make their selection
(0, _focus.focus)(element); // do not fire an input event if the file selection does not change
if (files.length === ((_input$files = input.files) == null ? void 0 : _input$files.length) && files.every((f, i) => {
var _input$files2;
return f === ((_input$files2 = input.files) == null ? void 0 : _input$files2.item(i));
})) {
return;
} // the event fired in the browser isn't actually an "input" or "change" event
// but a new Event with a type set to "input" and "change"
// Kinda odd...
const inputFiles = { ...files,
length: files.length,
item: index => files[index],
[Symbol.iterator]() {
let i = 0;
return {
next: () => ({
done: i >= files.length,
value: files[i++]
})
};
}
};
(0, _dom.fireEvent)(input, (0, _dom.createEvent)('input', input, {
target: {
files: inputFiles
},
bubbles: true,
cancelable: false,
composed: true
}));
_dom.fireEvent.change(input, {
target: {
files: inputFiles
},
...(init == null ? void 0 : init.changeInit)
});
}
function isAcceptableFile(file, accept) {
if (!accept) {
return true;
}
const wildcards = ['audio/*', 'image/*', 'video/*'];
return accept.split(',').some(acceptToken => {
if (acceptToken.startsWith('.')) {
// tokens starting with a dot represent a file extension
return file.name.endsWith(acceptToken);
} else if (wildcards.includes(acceptToken)) {
return file.type.startsWith(acceptToken.substr(0, acceptToken.length - 1));
}
return file.type === acceptToken;
});
}

View File

@@ -0,0 +1,31 @@
export declare function getMouseEventOptions(event: string, init?: MouseEventInit, clickCount?: number): {
detail: number;
buttons: number;
button: number;
clientX?: number | undefined;
clientY?: number | undefined;
movementX?: number | undefined;
movementY?: number | undefined;
relatedTarget?: EventTarget | null | undefined;
screenX?: number | undefined;
screenY?: number | undefined;
altKey?: boolean | undefined;
ctrlKey?: boolean | undefined;
metaKey?: boolean | undefined;
modifierAltGraph?: boolean | undefined;
modifierCapsLock?: boolean | undefined;
modifierFn?: boolean | undefined;
modifierFnLock?: boolean | undefined;
modifierHyper?: boolean | undefined;
modifierNumLock?: boolean | undefined;
modifierScrollLock?: boolean | undefined;
modifierSuper?: boolean | undefined;
modifierSymbol?: boolean | undefined;
modifierSymbolLock?: boolean | undefined;
shiftKey?: boolean | undefined;
view?: Window | null | undefined;
which?: number | undefined;
bubbles?: boolean | undefined;
cancelable?: boolean | undefined;
composed?: boolean | undefined;
};

View File

@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getMouseEventOptions = getMouseEventOptions;
function isMousePressEvent(event) {
return event === 'mousedown' || event === 'mouseup' || event === 'click' || event === 'dblclick';
} // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
const BUTTONS_NAMES = {
none: 0,
primary: 1,
secondary: 2,
auxiliary: 4
}; // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
const BUTTON_NAMES = {
primary: 0,
auxiliary: 1,
secondary: 2
};
function translateButtonNumber(value, from) {
var _Object$entries$find;
const [mapIn, mapOut] = from === 'button' ? [BUTTON_NAMES, BUTTONS_NAMES] : [BUTTONS_NAMES, BUTTON_NAMES];
const name = (_Object$entries$find = Object.entries(mapIn).find(([, i]) => i === value)) == null ? void 0 : _Object$entries$find[0]; // istanbul ignore next
return name && Object.prototype.hasOwnProperty.call(mapOut, name) ? mapOut[name] : 0;
}
function convertMouseButtons(event, init, property) {
if (!isMousePressEvent(event)) {
return 0;
}
if (typeof init[property] === 'number') {
return init[property];
} else if (property === 'button' && typeof init.buttons === 'number') {
return translateButtonNumber(init.buttons, 'buttons');
} else if (property === 'buttons' && typeof init.button === 'number') {
return translateButtonNumber(init.button, 'button');
}
return property != 'button' && isMousePressEvent(event) ? 1 : 0;
}
function getMouseEventOptions(event, init, clickCount = 0) {
var _init;
init = (_init = init) != null ? _init : {};
return { ...init,
// https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
detail: event === 'mousedown' || event === 'mouseup' || event === 'click' ? 1 + clickCount : clickCount,
buttons: convertMouseButtons(event, init, 'buttons'),
button: convertMouseButtons(event, init, 'button')
};
}

View File

@@ -0,0 +1 @@
export declare function isClickableInput(element: Element): boolean;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isClickableInput = isClickableInput;
var _isElementType = require("../misc/isElementType");
const CLICKABLE_INPUT_TYPES = ['button', 'color', 'file', 'image', 'reset', 'submit', 'checkbox', 'radio'];
function isClickableInput(element) {
return (0, _isElementType.isElementType)(element, 'button') || (0, _isElementType.isElementType)(element, 'input') && CLICKABLE_INPUT_TYPES.includes(element.type);
}

View File

@@ -0,0 +1 @@
export declare function buildTimeValue(value: string): string;

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildTimeValue = buildTimeValue;
function buildTimeValue(value) {
const onlyDigitsValue = value.replace(/\D/g, '');
if (onlyDigitsValue.length < 2) {
return value;
}
const firstDigit = parseInt(onlyDigitsValue[0], 10);
const secondDigit = parseInt(onlyDigitsValue[1], 10);
if (firstDigit >= 3 || firstDigit === 2 && secondDigit >= 4) {
let index;
if (firstDigit >= 3) {
index = 1;
} else {
index = 2;
}
return build(onlyDigitsValue, index);
}
if (value.length === 2) {
return value;
}
return build(onlyDigitsValue, 2);
}
function build(onlyDigitsValue, index) {
const hours = onlyDigitsValue.slice(0, index);
const validHours = Math.min(parseInt(hours, 10), 23);
const minuteCharacters = onlyDigitsValue.slice(index);
const parsedMinutes = parseInt(minuteCharacters, 10);
const validMinutes = Math.min(parsedMinutes, 59);
return `${validHours.toString().padStart(2, '0')}:${validMinutes.toString().padStart(2, '0')}`;
}

View File

@@ -0,0 +1,7 @@
export declare function calculateNewValue(newEntry: string, element: HTMLElement, value?: string, selectionRange?: {
selectionStart: number | null;
selectionEnd: number | null;
}, deleteContent?: 'backward' | 'forward'): {
newValue: string;
newSelectionStart: number;
};

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.calculateNewValue = calculateNewValue;
var _selectionRange = require("./selectionRange");
var _getValue2 = require("./getValue");
var _isValidDateValue = require("./isValidDateValue");
var _isValidInputTimeValue = require("./isValidInputTimeValue");
function calculateNewValue(newEntry, element, value = (() => {
var _getValue;
return (_getValue = (0, _getValue2.getValue)(element)) != null ? _getValue :
/* istanbul ignore next */
'';
})(), selectionRange = (0, _selectionRange.getSelectionRange)(element), deleteContent) {
const selectionStart = selectionRange.selectionStart === null ? value.length : selectionRange.selectionStart;
const selectionEnd = selectionRange.selectionEnd === null ? value.length : selectionRange.selectionEnd;
const prologEnd = Math.max(0, selectionStart === selectionEnd && deleteContent === 'backward' ? selectionStart - 1 : selectionStart);
const prolog = value.substring(0, prologEnd);
const epilogStart = Math.min(value.length, selectionStart === selectionEnd && deleteContent === 'forward' ? selectionEnd + 1 : selectionEnd);
const epilog = value.substring(epilogStart, value.length);
let newValue = `${prolog}${newEntry}${epilog}`;
const newSelectionStart = prologEnd + newEntry.length;
if (element.type === 'date' && !(0, _isValidDateValue.isValidDateValue)(element, newValue)) {
newValue = value;
}
if (element.type === 'time' && !(0, _isValidInputTimeValue.isValidInputTimeValue)(element, newValue)) {
if ((0, _isValidInputTimeValue.isValidInputTimeValue)(element, newEntry)) {
newValue = newEntry;
} else {
newValue = value;
}
}
return {
newValue,
newSelectionStart
};
}

View File

@@ -0,0 +1,2 @@
export declare function isCursorAtEnd(element: Element): boolean;
export declare function isCursorAtStart(element: Element): boolean;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isCursorAtEnd = isCursorAtEnd;
exports.isCursorAtStart = isCursorAtStart;
var _selectionRange = require("./selectionRange");
var _getValue2 = require("./getValue");
function isCursorAtEnd(element) {
var _getValue;
const {
selectionStart,
selectionEnd
} = (0, _selectionRange.getSelectionRange)(element);
return selectionStart === selectionEnd && (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) === ((_getValue = (0, _getValue2.getValue)(element)) != null ? _getValue :
/* istanbul ignore next */
'').length;
}
function isCursorAtStart(element) {
const {
selectionStart,
selectionEnd
} = (0, _selectionRange.getSelectionRange)(element);
return selectionStart === selectionEnd && (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) === 0;
}

View File

@@ -0,0 +1 @@
export declare function getValue(element: Element | null): string | null;

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getValue = getValue;
var _isContentEditable = require("./isContentEditable");
function getValue(element) {
// istanbul ignore if
if (!element) {
return null;
}
if ((0, _isContentEditable.isContentEditable)(element)) {
return element.textContent;
}
return element.value;
}

View File

@@ -0,0 +1,10 @@
declare enum unreliableValueInputTypes {
'number' = "number"
}
/**
* Check if an empty IDL value on the element could mean a derivation of displayed value and IDL value
*/
export declare function hasUnreliableEmptyValue(element: Element): element is HTMLInputElement & {
type: unreliableValueInputTypes;
};
export {};

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasUnreliableEmptyValue = hasUnreliableEmptyValue;
var _isElementType = require("../misc/isElementType");
var unreliableValueInputTypes;
/**
* Check if an empty IDL value on the element could mean a derivation of displayed value and IDL value
*/
(function (unreliableValueInputTypes) {
unreliableValueInputTypes["number"] = "number";
})(unreliableValueInputTypes || (unreliableValueInputTypes = {}));
function hasUnreliableEmptyValue(element) {
return (0, _isElementType.isElementType)(element, 'input') && Boolean(unreliableValueInputTypes[element.type]);
}

View File

@@ -0,0 +1,3 @@
export declare function isContentEditable(element: Element): element is HTMLElement & {
contenteditable: 'true';
};

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isContentEditable = isContentEditable;
//jsdom is not supporting isContentEditable
function isContentEditable(element) {
return element.hasAttribute('contenteditable') && (element.getAttribute('contenteditable') == 'true' || element.getAttribute('contenteditable') == '');
}

View File

@@ -0,0 +1,24 @@
import { isContentEditable } from './isContentEditable';
declare type GuardedType<T> = T extends (x: any) => x is infer R ? R : never;
export declare function isEditable(element: Element): element is GuardedType<typeof isContentEditable> | GuardedType<typeof isEditableInput> | (HTMLTextAreaElement & {
readOnly: false;
});
export declare enum editableInputTypes {
'text' = "text",
'date' = "date",
'datetime-local' = "datetime-local",
'email' = "email",
'month' = "month",
'number' = "number",
'password' = "password",
'search' = "search",
'tel' = "tel",
'time' = "time",
'url' = "url",
'week' = "week"
}
export declare function isEditableInput(element: Element): element is HTMLInputElement & {
readOnly: false;
type: editableInputTypes;
};
export {};

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.editableInputTypes = void 0;
exports.isEditable = isEditable;
exports.isEditableInput = isEditableInput;
var _isElementType = require("../misc/isElementType");
var _isContentEditable = require("./isContentEditable");
function isEditable(element) {
return isEditableInput(element) || (0, _isElementType.isElementType)(element, 'textarea', {
readOnly: false
}) || (0, _isContentEditable.isContentEditable)(element);
}
let editableInputTypes;
exports.editableInputTypes = editableInputTypes;
(function (editableInputTypes) {
editableInputTypes["text"] = "text";
editableInputTypes["date"] = "date";
editableInputTypes["datetime-local"] = "datetime-local";
editableInputTypes["email"] = "email";
editableInputTypes["month"] = "month";
editableInputTypes["number"] = "number";
editableInputTypes["password"] = "password";
editableInputTypes["search"] = "search";
editableInputTypes["tel"] = "tel";
editableInputTypes["time"] = "time";
editableInputTypes["url"] = "url";
editableInputTypes["week"] = "week";
})(editableInputTypes || (exports.editableInputTypes = editableInputTypes = {}));
function isEditableInput(element) {
return (0, _isElementType.isElementType)(element, 'input', {
readOnly: false
}) && Boolean(editableInputTypes[element.type]);
}

View File

@@ -0,0 +1,3 @@
export declare function isValidDateValue(element: HTMLInputElement & {
type: 'date';
}, value: string): boolean;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isValidDateValue = isValidDateValue;
function isValidDateValue(element, value) {
const clone = element.cloneNode();
clone.value = value;
return clone.value === value;
}

View File

@@ -0,0 +1,3 @@
export declare function isValidInputTimeValue(element: HTMLInputElement & {
type: 'time';
}, timeValue: string): boolean;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isValidInputTimeValue = isValidInputTimeValue;
function isValidInputTimeValue(element, timeValue) {
const clone = element.cloneNode();
clone.value = timeValue;
return clone.value === timeValue;
}

View File

@@ -0,0 +1 @@
export declare function getSpaceUntilMaxLength(element: Element): number | undefined;

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSpaceUntilMaxLength = getSpaceUntilMaxLength;
var _isElementType = require("../misc/isElementType");
var _getValue = require("./getValue");
var maxLengthSupportedTypes;
(function (maxLengthSupportedTypes) {
maxLengthSupportedTypes["email"] = "email";
maxLengthSupportedTypes["password"] = "password";
maxLengthSupportedTypes["search"] = "search";
maxLengthSupportedTypes["telephone"] = "telephone";
maxLengthSupportedTypes["text"] = "text";
maxLengthSupportedTypes["url"] = "url";
})(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
function getSpaceUntilMaxLength(element) {
const value = (0, _getValue.getValue)(element);
/* istanbul ignore if */
if (value === null) {
return undefined;
}
const maxLength = getSanitizedMaxLength(element);
return maxLength ? maxLength - value.length : undefined;
} // can't use .maxLength property because of a jsdom bug:
// https://github.com/jsdom/jsdom/issues/2927
function getSanitizedMaxLength(element) {
var _element$getAttribute;
if (!supportsMaxLength(element)) {
return undefined;
}
const attr = (_element$getAttribute = element.getAttribute('maxlength')) != null ? _element$getAttribute : '';
return /^\d+$/.test(attr) && Number(attr) >= 0 ? Number(attr) : undefined;
}
function supportsMaxLength(element) {
return (0, _isElementType.isElementType)(element, 'textarea') || (0, _isElementType.isElementType)(element, 'input') && Boolean(maxLengthSupportedTypes[element.type]);
}

View File

@@ -0,0 +1,16 @@
declare enum selectionSupportType {
'text' = "text",
'search' = "search",
'url' = "url",
'tel' = "tel",
'password' = "password"
}
export declare function hasSelectionSupport(element: Element): element is HTMLTextAreaElement | (HTMLInputElement & {
type: selectionSupportType;
});
export declare function getSelectionRange(element: Element): {
selectionStart: number | null;
selectionEnd: number | null;
};
export declare function setSelectionRange(element: Element, newSelectionStart: number, newSelectionEnd: number): void;
export {};

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSelectionRange = getSelectionRange;
exports.hasSelectionSupport = hasSelectionSupport;
exports.setSelectionRange = setSelectionRange;
var _isElementType = require("../misc/isElementType");
// https://github.com/jsdom/jsdom/blob/c2fb8ff94917a4d45e2398543f5dd2a8fed0bdab/lib/jsdom/living/nodes/HTMLInputElement-impl.js#L45
var selectionSupportType;
(function (selectionSupportType) {
selectionSupportType["text"] = "text";
selectionSupportType["search"] = "search";
selectionSupportType["url"] = "url";
selectionSupportType["tel"] = "tel";
selectionSupportType["password"] = "password";
})(selectionSupportType || (selectionSupportType = {}));
const InputSelection = Symbol('inputSelection');
function hasSelectionSupport(element) {
return (0, _isElementType.isElementType)(element, 'textarea') || (0, _isElementType.isElementType)(element, 'input') && Boolean(selectionSupportType[element.type]);
}
function getSelectionRange(element) {
if (hasSelectionSupport(element)) {
return {
selectionStart: element.selectionStart,
selectionEnd: element.selectionEnd
};
}
if ((0, _isElementType.isElementType)(element, 'input')) {
var _InputSelection;
return (_InputSelection = element[InputSelection]) != null ? _InputSelection : {
selectionStart: null,
selectionEnd: null
};
}
const selection = element.ownerDocument.getSelection(); // there should be no editing if the focusNode is outside of element
// TODO: properly handle selection ranges
if (selection != null && selection.rangeCount && element.contains(selection.focusNode)) {
const range = selection.getRangeAt(0);
return {
selectionStart: range.startOffset,
selectionEnd: range.endOffset
};
} else {
return {
selectionStart: null,
selectionEnd: null
};
}
}
function setSelectionRange(element, newSelectionStart, newSelectionEnd) {
const {
selectionStart,
selectionEnd
} = getSelectionRange(element);
if (selectionStart === newSelectionStart && selectionEnd === newSelectionEnd) {
return;
}
if (hasSelectionSupport(element)) {
element.setSelectionRange(newSelectionStart, newSelectionEnd);
}
if ((0, _isElementType.isElementType)(element, 'input')) {
;
element[InputSelection] = {
selectionStart: newSelectionStart,
selectionEnd: newSelectionEnd
};
} // Moving the selection inside <input> or <textarea> does not alter the document Selection.
if ((0, _isElementType.isElementType)(element, 'input') || (0, _isElementType.isElementType)(element, 'textarea')) {
return;
}
const range = element.ownerDocument.createRange();
range.selectNodeContents(element); // istanbul ignore else
if (element.firstChild) {
range.setStart(element.firstChild, newSelectionStart);
range.setEnd(element.firstChild, newSelectionEnd);
}
const selection = element.ownerDocument.getSelection(); // istanbul ignore else
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
}

View File

@@ -0,0 +1 @@
export declare function getActiveElement(document: Document | ShadowRoot): Element | null;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getActiveElement = getActiveElement;
var _isDisabled = require("../misc/isDisabled");
function getActiveElement(document) {
const activeElement = document.activeElement;
if (activeElement != null && activeElement.shadowRoot) {
return getActiveElement(activeElement.shadowRoot);
} else {
// Browser does not yield disabled elements as document.activeElement - jsdom does
if ((0, _isDisabled.isDisabled)(activeElement)) {
return document.ownerDocument ? // TODO: verify behavior in ShadowRoot
/* istanbul ignore next */
document.ownerDocument.body : document.body;
}
return activeElement;
}
}

View File

@@ -0,0 +1 @@
export declare function isFocusable(element: Element): element is HTMLElement;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isFocusable = isFocusable;
var _isLabelWithInternallyDisabledControl = require("../misc/isLabelWithInternallyDisabledControl");
var _selector = require("./selector");
function isFocusable(element) {
return !(0, _isLabelWithInternallyDisabledControl.isLabelWithInternallyDisabledControl)(element) && element.matches(_selector.FOCUSABLE_SELECTOR);
}

View File

@@ -0,0 +1 @@
export declare const FOCUSABLE_SELECTOR: string;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FOCUSABLE_SELECTOR = void 0;
const FOCUSABLE_SELECTOR = ['input:not([type=hidden]):not([disabled])', 'button:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[contenteditable=""]', '[contenteditable="true"]', 'a[href]', '[tabindex]:not([disabled])'].join(', ');
exports.FOCUSABLE_SELECTOR = FOCUSABLE_SELECTOR;

View File

@@ -0,0 +1,25 @@
export * from './click/getMouseEventOptions';
export * from './click/isClickableInput';
export * from './edit/buildTimeValue';
export * from './edit/calculateNewValue';
export * from './edit/cursorPosition';
export * from './edit/getValue';
export * from './edit/hasUnreliableEmptyValue';
export * from './edit/isContentEditable';
export * from './edit/isEditable';
export * from './edit/isValidDateValue';
export * from './edit/isValidInputTimeValue';
export * from './edit/maxLength';
export * from './edit/selectionRange';
export * from './focus/getActiveElement';
export * from './focus/isFocusable';
export * from './focus/selector';
export * from './misc/eventWrapper';
export * from './misc/isElementType';
export * from './misc/isLabelWithInternallyDisabledControl';
export * from './misc/isVisible';
export * from './misc/isDisabled';
export * from './misc/isDocument';
export * from './misc/wait';
export * from './misc/hasPointerEvents';
export * from './misc/hasFormSubmit';

View File

@@ -0,0 +1,330 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getMouseEventOptions = require("./click/getMouseEventOptions");
Object.keys(_getMouseEventOptions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getMouseEventOptions[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getMouseEventOptions[key];
}
});
});
var _isClickableInput = require("./click/isClickableInput");
Object.keys(_isClickableInput).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isClickableInput[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isClickableInput[key];
}
});
});
var _buildTimeValue = require("./edit/buildTimeValue");
Object.keys(_buildTimeValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _buildTimeValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _buildTimeValue[key];
}
});
});
var _calculateNewValue = require("./edit/calculateNewValue");
Object.keys(_calculateNewValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _calculateNewValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _calculateNewValue[key];
}
});
});
var _cursorPosition = require("./edit/cursorPosition");
Object.keys(_cursorPosition).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _cursorPosition[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _cursorPosition[key];
}
});
});
var _getValue = require("./edit/getValue");
Object.keys(_getValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getValue[key];
}
});
});
var _hasUnreliableEmptyValue = require("./edit/hasUnreliableEmptyValue");
Object.keys(_hasUnreliableEmptyValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasUnreliableEmptyValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasUnreliableEmptyValue[key];
}
});
});
var _isContentEditable = require("./edit/isContentEditable");
Object.keys(_isContentEditable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isContentEditable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isContentEditable[key];
}
});
});
var _isEditable = require("./edit/isEditable");
Object.keys(_isEditable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isEditable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isEditable[key];
}
});
});
var _isValidDateValue = require("./edit/isValidDateValue");
Object.keys(_isValidDateValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isValidDateValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isValidDateValue[key];
}
});
});
var _isValidInputTimeValue = require("./edit/isValidInputTimeValue");
Object.keys(_isValidInputTimeValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isValidInputTimeValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isValidInputTimeValue[key];
}
});
});
var _maxLength = require("./edit/maxLength");
Object.keys(_maxLength).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _maxLength[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _maxLength[key];
}
});
});
var _selectionRange = require("./edit/selectionRange");
Object.keys(_selectionRange).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _selectionRange[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _selectionRange[key];
}
});
});
var _getActiveElement = require("./focus/getActiveElement");
Object.keys(_getActiveElement).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getActiveElement[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getActiveElement[key];
}
});
});
var _isFocusable = require("./focus/isFocusable");
Object.keys(_isFocusable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isFocusable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isFocusable[key];
}
});
});
var _selector = require("./focus/selector");
Object.keys(_selector).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _selector[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _selector[key];
}
});
});
var _eventWrapper = require("./misc/eventWrapper");
Object.keys(_eventWrapper).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _eventWrapper[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _eventWrapper[key];
}
});
});
var _isElementType = require("./misc/isElementType");
Object.keys(_isElementType).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isElementType[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isElementType[key];
}
});
});
var _isLabelWithInternallyDisabledControl = require("./misc/isLabelWithInternallyDisabledControl");
Object.keys(_isLabelWithInternallyDisabledControl).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isLabelWithInternallyDisabledControl[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isLabelWithInternallyDisabledControl[key];
}
});
});
var _isVisible = require("./misc/isVisible");
Object.keys(_isVisible).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isVisible[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isVisible[key];
}
});
});
var _isDisabled = require("./misc/isDisabled");
Object.keys(_isDisabled).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isDisabled[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isDisabled[key];
}
});
});
var _isDocument = require("./misc/isDocument");
Object.keys(_isDocument).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isDocument[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isDocument[key];
}
});
});
var _wait = require("./misc/wait");
Object.keys(_wait).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _wait[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _wait[key];
}
});
});
var _hasPointerEvents = require("./misc/hasPointerEvents");
Object.keys(_hasPointerEvents).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasPointerEvents[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasPointerEvents[key];
}
});
});
var _hasFormSubmit = require("./misc/hasFormSubmit");
Object.keys(_hasFormSubmit).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasFormSubmit[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasFormSubmit[key];
}
});
});

View File

@@ -0,0 +1 @@
export declare function eventWrapper<T>(cb: () => T): T | undefined;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.eventWrapper = eventWrapper;
var _dom = require("@testing-library/dom");
function eventWrapper(cb) {
let result;
(0, _dom.getConfig)().eventWrapper(() => {
result = cb();
});
return result;
}

View File

@@ -0,0 +1 @@
export declare const hasFormSubmit: (form: HTMLFormElement | null) => form is HTMLFormElement;

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasFormSubmit = void 0;
const hasFormSubmit = form => !!(form && (form.querySelector('input[type="submit"]') || form.querySelector('button[type="submit"]')));
exports.hasFormSubmit = hasFormSubmit;

View File

@@ -0,0 +1,15 @@
/**
* Options that can be passed to any event that relies
* on pointer-events property
*/
export declare interface PointerOptions {
/**
* When set to `true` the event skips checking if any element
* in the DOM-tree has `'pointer-events: none'` set. This check is
* costly in general and very costly when rendering large DOM-trees.
* Can be used to speed up tests.
* Default: `false`
* */
skipPointerEventsCheck?: boolean;
}
export declare function hasPointerEvents(element: Element): boolean;

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasPointerEvents = hasPointerEvents;
var _helpers = require("@testing-library/dom/dist/helpers");
function hasPointerEvents(element) {
const window = (0, _helpers.getWindowFromNode)(element);
for (let el = element; (_el = el) != null && _el.ownerDocument; el = el.parentElement) {
var _el;
const pointerEvents = window.getComputedStyle(el).pointerEvents;
if (pointerEvents && !['inherit', 'unset'].includes(pointerEvents)) {
return pointerEvents !== 'none';
}
}
return true;
}

View File

@@ -0,0 +1 @@
export declare function isDisabled(element: Element | null): boolean;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isDisabled = isDisabled;
// This should probably be extended with checking the element type
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled
function isDisabled(element) {
return Boolean(element && element.disabled);
}

View File

@@ -0,0 +1 @@
export declare function isDocument(el: Document | Element): el is Document;

Some files were not shown because too many files have changed in this diff Show More