react-select-event
Version:
Simulate react-select events for react-testing-library
177 lines (151 loc) • 6.05 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var dom = require('@testing-library/dom');
/**
* A simple compatibility method for react's "act".
* If a recent version of @testing-library/react is already installed,
* we just use their implementation - it's complete and has useful warnings.
* Otherwise, we just default to a noop.
*
* We need this because react-select-event doesn't actually pin a
* dependency version for @testing-library/react!
*/
let act;
try {
act = require("@testing-library/react").act;
} catch (_) {
// istanbul ignore next
act = callback => {
callback();
};
}
var act$1 = act;
/** Simulate user events on react-select dropdowns */
function getReactSelectContainerFromInput(input) {
return input.parentNode.parentNode.parentNode.parentNode.parentNode;
}
/**
* Utility for opening the select's dropdown menu.
* @param {HTMLElement} input The input field (eg. `getByLabelText('The label')`)
*/
const openMenu = input => {
dom.fireEvent.focus(input);
dom.fireEvent.keyDown(input, {
key: "ArrowDown",
keyCode: 40,
code: 40
});
}; // type text in the input field
const type = (input, text) => {
dom.fireEvent.change(input, {
target: {
value: text
}
});
}; // press the "clear" button, and reset various states
const clear = async (input, clearButton) => {
await act$1(async () => {
dom.fireEvent.mouseDown(clearButton);
dom.fireEvent.click(clearButton); // react-select will prevent the menu from opening, and asynchronously focus the select field...
await dom.waitFor(() => {});
input.blur();
});
};
/**
* Utility for selecting a value in a `react-select` dropdown.
* @param {HTMLElement} input The input field (eg. `getByLabelText('The label')`)
* @param {Matcher|Matcher[]} optionOrOptions The display name(s) for the option(s) to select
* @param {Object} config Optional config options
* @param {HTMLElement | (() => HTMLElement)} config.container A container for the react-select and its dropdown (defaults to the react-select container)
* Useful when rending the dropdown to a portal using react-select's `menuPortalTarget`.
* Can be specified as a function if it needs to be lazily evaluated.
*/
const select = async (input, optionOrOptions, config = {}) => {
const options = Array.isArray(optionOrOptions) ? optionOrOptions : [optionOrOptions]; // Select the items we care about
for (const option of options) {
await openMenu(input);
let container;
if (typeof config.container === "function") {
// when specified as a function, the container needs to be lazily evaluated, so
// we have to wait for it to be visible:
await dom.waitFor(config.container);
container = config.container();
} else if (config.container) {
container = config.container;
} else {
container = getReactSelectContainerFromInput(input);
} // only consider visible, interactive elements
const matchingElements = await dom.findAllByText(container, option, {
// @ts-ignore invalid rtl types :'(
ignore: "[aria-live] *,[style*='visibility: hidden']"
});
act$1(() => {
// When the target option is already selected, the react-select display text
// will also match the selector. In this case, the actual dropdown element is
// positioned last in the DOM tree.
const optionElement = matchingElements[matchingElements.length - 1];
dom.fireEvent.click(optionElement);
});
}
};
/**
* Utility for creating and selecting a value in a Creatable `react-select` dropdown.
* @async
* @param {HTMLElement} input The input field (eg. `getByLabelText('The label')`)
* @param {String} option The display name for the option to type and select
* @param {Object} config Optional config options
* @param {HTMLElement} config.container A container for the react-select and its dropdown (defaults to the react-select container)
* Useful when rending the dropdown to a portal using react-select's `menuPortalTarget`
* @param {boolean} config.waitForElement Whether create should wait for new option to be populated in the select container
* @param {String|RegExp} config.createOptionText Custom label for the "create new ..." option in the menu (string or regexp)
*/
const create = async (input, option, {
waitForElement = true,
...config
} = {}) => {
const createOptionText = config.createOptionText || /^Create "/;
openMenu(input);
type(input, option);
dom.fireEvent.change(input, {
target: {
value: option
}
});
await select(input, createOptionText, config);
if (waitForElement) {
await dom.findByText(getReactSelectContainerFromInput(input), option);
}
};
/**
* Utility for clearing the first value of a `react-select` dropdown.
* @param {HTMLElement} input The input field (eg. `getByLabelText('The label')`)
*/
const clearFirst = async input => {
const container = getReactSelectContainerFromInput(input); // The "clear" button is the first svg element that is hidden to screen readers
const clearButton = container.querySelector('svg[aria-hidden="true"]');
await clear(input, clearButton);
};
/**
* Utility for clearing all values in a `react-select` dropdown.
* @param {HTMLElement} input The input field (eg. `getByLabelText('The label')`)
*/
const clearAll = async input => {
const container = getReactSelectContainerFromInput(input); // The "clear all" button is the penultimate svg element that is hidden to screen readers
// (the last one is the dropdown arrow)
const elements = container.querySelectorAll('svg[aria-hidden="true"]');
const clearAllButton = elements[elements.length - 2];
await clear(input, clearAllButton);
};
const selectEvent = {
select,
create,
clearFirst,
clearAll,
openMenu
};
exports.clearAll = clearAll;
exports.clearFirst = clearFirst;
exports.create = create;
exports['default'] = selectEvent;
exports.openMenu = openMenu;
exports.select = select;