react-testing-library
Version:
Simple and complete React DOM testing utilities that encourage good testing practices.
190 lines (157 loc) • 6.17 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var _extends = _interopDefault(require('@babel/runtime/helpers/extends'));
var domTestingLibrary = require('dom-testing-library');
var React = _interopDefault(require('react'));
var ReactDOM = _interopDefault(require('react-dom'));
var testUtils = require('react-dom/test-utils');
// so for versions that don't have act from test utils
// we do this little polyfill. No warnings, but it's
// better than nothing.
function actPolyfill(cb) {
ReactDOM.unstable_batchedUpdates(cb);
ReactDOM.render(React.createElement("div", null), document.createElement('div'));
}
var act = testUtils.act || actPolyfill;
function rtlAct() {
return act.apply(void 0, arguments);
}
var mountedContainers = new Set();
function render(ui, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
container = _ref.container,
_ref$baseElement = _ref.baseElement,
baseElement = _ref$baseElement === void 0 ? container : _ref$baseElement,
queries = _ref.queries,
_ref$hydrate = _ref.hydrate,
hydrate = _ref$hydrate === void 0 ? false : _ref$hydrate;
if (!container) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
container = document.body.appendChild(document.createElement('div'));
} // we'll add it to the mounted containers regardless of whether it's actually
// added to document.body so the cleanup method works regardless of whether
// they're passing us a custom container or not.
mountedContainers.add(container);
if (hydrate) {
rtlAct(function () {
ReactDOM.hydrate(ui, container);
});
} else {
rtlAct(function () {
ReactDOM.render(ui, container);
});
}
return _extends({
container: container,
baseElement: baseElement,
// eslint-disable-next-line no-console
debug: function debug(el) {
if (el === void 0) {
el = baseElement;
}
return console.log(domTestingLibrary.prettyDOM(el));
},
unmount: function unmount() {
return ReactDOM.unmountComponentAtNode(container);
},
rerender: function rerender(rerenderUi) {
render(rerenderUi, {
container: container,
baseElement: baseElement
}); // Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: function asFragment() {
/* istanbul ignore if (jsdom limitation) */
if (typeof document.createRange === 'function') {
return document.createRange().createContextualFragment(container.innerHTML);
}
var template = document.createElement('template');
template.innerHTML = container.innerHTML;
return template.content;
}
}, domTestingLibrary.getQueriesForElement(baseElement, queries));
}
function TestHook(_ref2) {
var callback = _ref2.callback;
callback();
return null;
}
function testHook(callback) {
var _render = render(React.createElement(TestHook, {
callback: callback
})),
unmount = _render.unmount,
rerenderComponent = _render.rerender;
return {
unmount: unmount,
rerender: function rerender() {
rerenderComponent(React.createElement(TestHook, {
callback: callback
}));
}
};
}
function cleanup() {
mountedContainers.forEach(cleanupAtContainer);
} // maybe one day we'll expose this (perhaps even as a utility returned by render).
// but let's wait until someone asks for it.
function cleanupAtContainer(container) {
if (container.parentNode === document.body) {
document.body.removeChild(container);
}
ReactDOM.unmountComponentAtNode(container);
mountedContainers.delete(container);
} // react-testing-library's version of fireEvent will call
// dom-testing-library's version of fireEvent wrapped inside
// an "act" call so that after all event callbacks have been
// been called, the resulting useEffect callbacks will also
// be called.
function fireEvent() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var returnValue;
rtlAct(function () {
returnValue = domTestingLibrary.fireEvent.apply(void 0, args);
});
return returnValue;
}
Object.keys(domTestingLibrary.fireEvent).forEach(function (key) {
fireEvent[key] = function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var returnValue;
rtlAct(function () {
returnValue = domTestingLibrary.fireEvent[key].apply(domTestingLibrary.fireEvent, args);
});
return returnValue;
};
}); // React event system tracks native mouseOver/mouseOut events for
// running onMouseEnter/onMouseLeave handlers
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
fireEvent.mouseEnter = fireEvent.mouseOver;
fireEvent.mouseLeave = fireEvent.mouseOut;
fireEvent.select = function (node, init) {
// React tracks this event only on focused inputs
node.focus(); // React creates this event when one of the following native events happens
// - contextMenu
// - mouseUp
// - dragEnd
// - keyUp
// - keyDown
// so we can use any here
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224
fireEvent.keyUp(node, init);
}; // just re-export everything from dom-testing-library
/* eslint func-name-matching:0 */
Object.keys(domTestingLibrary).forEach(function (key) { exports[key] = domTestingLibrary[key]; });
exports.render = render;
exports.testHook = testHook;
exports.cleanup = cleanup;
exports.fireEvent = fireEvent;
exports.act = rtlAct;