/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
describe('ReactDOMTestSelectors', () => {
let React;
let createRoot;
let act;
let createComponentSelector;
let createHasPseudoClassSelector;
let createRoleSelector;
let createTextSelector;
let createTestNameSelector;
let findAllNodes;
let findBoundingRects;
let focusWithin;
let getFindAllNodesFailureDescription;
let observeVisibleRects;
let container;
beforeEach(() => {
jest.resetModules();
React = require('react');
act = require('internal-test-utils').act;
if (__EXPERIMENTAL__ || global.__WWW__) {
const ReactDOM = require('react-dom/unstable_testing');
createComponentSelector = ReactDOM.createComponentSelector;
createHasPseudoClassSelector = ReactDOM.createHasPseudoClassSelector;
createRoleSelector = ReactDOM.createRoleSelector;
createTextSelector = ReactDOM.createTextSelector;
createTestNameSelector = ReactDOM.createTestNameSelector;
findAllNodes = ReactDOM.findAllNodes;
findBoundingRects = ReactDOM.findBoundingRects;
focusWithin = ReactDOM.focusWithin;
getFindAllNodesFailureDescription =
ReactDOM.getFindAllNodesFailureDescription;
observeVisibleRects = ReactDOM.observeVisibleRects;
createRoot = ReactDOM.createRoot;
}
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
});
describe('findAllNodes', () => {
// @gate www || experimental
it('should support searching from the document root', async () => {
function Example() {
return (
);
}
const root = createRoot(container);
await act(() => {
root.render();
});
const matches = findAllNodes(container, [
createComponentSelector(Example),
createTestNameSelector('match'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('match');
});
// @gate www || experimental
it('should support searching from a previous match if the match had a data-testname', async () => {
function Outer() {
return (
);
}
function Inner() {
return ;
}
const root = createRoot(container);
await act(() => {
root.render();
});
let matches = findAllNodes(container, [
createComponentSelector(Outer),
createTestNameSelector('outer'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('outer');
matches = findAllNodes(matches[0], [
createComponentSelector(Inner),
createTestNameSelector('inner'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('inner');
});
// @gate www || experimental
it('should not support searching from a previous match if the match did not have a data-testname', async () => {
function Outer() {
return (
);
}
function Inner() {
return ;
}
const root = createRoot(container);
await act(() => {
root.render();
});
const matches = findAllNodes(container, [createComponentSelector(Outer)]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('outer');
expect(() => {
findAllNodes(matches[0], [
createComponentSelector(Inner),
createTestNameSelector('inner'),
]);
}).toThrow(
'Invalid host root specified. Should be either a React container or a node with a testname attribute.',
);
});
// @gate www || experimental
it('should support an multiple component types in the selector array', async () => {
function Outer() {
return (
<>
>
);
}
function Middle() {
return (
<>
>
);
}
function Inner() {
return (
<>
>
);
}
const root = createRoot(container);
await act(() => {
root.render();
});
let matches = findAllNodes(document.body, [
createComponentSelector(Outer),
createComponentSelector(Middle),
createTestNameSelector('match'),
]);
expect(matches).toHaveLength(2);
expect(matches.map(m => m.id).sort()).toEqual(['match2', 'match3']);
matches = findAllNodes(document.body, [
createComponentSelector(Outer),
createComponentSelector(Middle),
createComponentSelector(Inner),
createTestNameSelector('match'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('match3');
matches = findAllNodes(document.body, [
createComponentSelector(Outer),
createComponentSelector(Inner),
createTestNameSelector('match'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('match3');
});
// @gate www || experimental
it('should find multiple matches', async () => {
function Example1() {
return (
);
}
const root = createRoot(container);
await act(() => {
root.render();
});
const matches = findAllNodes(document.body, [
createComponentSelector(Example),
createRoleSelector('article'),
createHasPseudoClassSelector([
createRoleSelector('heading'),
createTextSelector('Should match'),
]),
createRoleSelector('button'),
]);
expect(matches).toHaveLength(1);
expect(matches[0].id).toBe('match');
});
// @gate www || experimental
it('should throw if no container can be found', () => {
expect(() => findAllNodes(document.body, [])).toThrow(
'Could not find React container within specified host subtree.',
);
});
// @gate www || experimental
it('should throw if an invalid host root is specified', async () => {
const ref = React.createRef();
function Example() {
return ;
}
const root = createRoot(container);
await act(() => {
root.render();
});
expect(() => findAllNodes(ref.current, [])).toThrow(
'Invalid host root specified. Should be either a React container or a node with a testname attribute.',
);
});
});
describe('getFindAllNodesFailureDescription', () => {
// @gate www || experimental
it('should describe findAllNodes failures caused by the component type selector', async () => {
function Outer() {
return ;
}
function Middle() {
return ;
}
function NotRendered() {
return ;
}
const root = createRoot(container);
await act(() => {
root.render();
});
const description = getFindAllNodesFailureDescription(document.body, [
createComponentSelector(Outer),
createComponentSelector(Middle),
createComponentSelector(NotRendered),
createTestNameSelector('match'),
]);
expect(description).toEqual(
`findAllNodes was able to match part of the selector:
>
No matching component was found for:
> [data-testname="match"]`,
);
});
// @gate www || experimental
it('should return null if findAllNodes was able to find a match', async () => {
function Example() {
return (
);
}
const root = createRoot(container);
await act(() => {
root.render();
});
const description = getFindAllNodesFailureDescription(document.body, [
createComponentSelector(Example),
]);
expect(description).toBe(null);
});
});
describe('findBoundingRects', () => {
// Stub out getBoundingClientRect for the specified target.
// This API is required by the test selectors but it isn't implemented by jsdom.
function setBoundingClientRect(target, {x, y, width, height}) {
target.getBoundingClientRect = function () {
return {
width,
height,
left: x,
right: x + width,
top: y,
bottom: y + height,
};
};
}
// @gate www || experimental
it('should return a single rect for a component that returns a single root host element', async () => {
const ref = React.createRef();
function Example() {
return (