enhanced-crs
v1.0.3
Published
cypress plugin to locate react elements by component, props and state
Maintainers
Readme
Enhanced CRS (Enhanced CypressReactSelector)
This is a fork of abhinaba-ghosh/enhanced-crs
The main goal of this fork is allowing users to select elements in a React application that lives into an iFrame
Enhanced CRS is a lightweight plugin to help you to locate web elements in your REACT app using components, props and states. This extension allow you to select elements in a way that is native to React. Designed to help developers in component, integration and E2E testing.
Internally, Enhanced CRS uses a library called enhanced-resq to query React's VirtualDOM in order to retrieve the nodes.
Table of Contents
- Install and configure
- Highlights
- Type Definition
- How to use React Selector?
- Get React Properties from element
- Timeouts
- Fetch indexed node
- Use fluent chained queries
- Sample Tests
- Community Projects
- Tool You Need
- Tell me your issues
- Contribution
Install and configure
Add as a dependency:
npm i --save enhanced-crs
or
yarn add enhanced-crsInclude the commands
Update Cypress/support/index.js file to include the cypress-react-selector commands by adding:
import 'enhanced-crs';TSConfig Settings for types
{
"compilerOptions": {
"sourceType": "module",
"types": ["node", "cypress", "enhanced-crs"]
}
}Highlights
- cypress-react-selector supports NodeJS 8 or higher
- It supports React 16 or higher
- Retries each interaction until timeout to handle asynchronous calls
- Supports shadow DOM
- Supports wildcard selection for component names
- Supports nested Props
- Supports assertion on real-time react properties (props and states)
How to use React Selector?
Lets take this example REACT APP:
// imports
const MyComponent = ({ someBooleanProp }) => (
<div>My Component {someBooleanProp ? 'show this' : ''} </div>
);
const App = () => (
<div id="root">
<MyComponent />
<MyComponent someBooleanProp={true} />
</div>
);
ReactDOM.render(<App />, document.getElementById('root'));Wait for application to be ready to run tests
cypress-react-selector needs the react root css-selector information to identify
- Whether React has loaded
- Retry React identification queries if state changes in run time/React loads asynchronously
In order to make sure that the React component tree has loaded, add the waitForReact call immediately after loading a page. Here is an example where it's done in the fixture's before hook.
before(() => {
cy.visit('http://localhost:3000/myApp');
cy.waitForReact(1000, '#root'); // 1000 is the timeout in milliseconds, you can provide as per AUT
});If you want to load a React application living into an iFrame you can do it by typing:
before(() => {
cy.visit('http://localhost:3000/myApp');
cy.waitForReactInIframe(1000, 'html > body > div > iframe', '#root'); // 1000 is the timeout in milliseconds, you can provide as per AUT
});NOTE : The Best Configuration for React root is to declare it as an env variable
We always recommend declaring the react root and the iframe element as a env variable in the cypress.json file. It is a best approach rather than passing react root information to waitForReact method every time.
As an example:
{
"env": {
"cypress-react-selector": {
"root": "#root",
"iframeElement": "html > body > div > iframe"
}
}
}If you choose to declare the root selector as a configuration, then you will have the freedom to call waitForReact method without passing the root parameter.
before(() => {
cy.visit('http://localhost:3000/myApp');
cy.waitForReact();
});before(() => {
cy.visit('http://localhost:3000/myApp');
cy.waitForReactIframe();
});Find Element by React Component
You should have React Develop Tool installed to spy and find out the component name as sometimes components can go though modifications. Once the React gets loaded, you can easily identify an web element by react component name:
cy.react('MyComponent');
// you can have your assertions chained like
it('it should validate react selection with component name', () => {
cy.react('MyComponent').should('have.length', '1');
});Element filtration by Props and States
You can filter the REACT components by its props and states like below:
cy.react(componentName, reactOpts);
// ReactOpts:
//{
// props: { someProp: someValue },
// state: { someState: someValue },
// exact: boolean
//}
// for the example APP
cy.react('MyComponent', { props: { name: 'John' } });Deep Matching with exact flag
If you are in need of matching exactly every property and value in the object (or nested objects), you can pass the exact flag to the cy.react or cy.getReact function:
cy.react('MyComponent', { props: { name: 'John' }, exact: true });Make sure all the props and/or state are listed while using this flag, if not matched it will return undefined
Wildcard selection
You can select your components by partial name use a wildcard selectors:
// Partial Match
cy.react('My*', { props: { name: 'John' } });
// Entire Match
cy.react('*', { props: { name: 'John' } }); // return all components matched with the propFind element by nested props
Let's suppose you have an Form component
<Form>
<Field name="email" type="email" component={MyTextInput} />
<ErrorMessage name="email" component="div" />
<br />
<Field type="password" name="password" component={MyTextInput} />
<ErrorMessage name="password" component="div" />
<br />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>And MyTextInput component is developed as:
const MyTextInput = (props) => {
const { field, type } = props;
return (
<input {...field} type={type} placeholder={'ENTER YOUR ' + field.name} />
);
};then you can use cypress-react-selector to identify the element with nested props
it('enter data into the fields', () => {
cy.react('MyTextInput', { props: { field: { name: 'email' } } }).type(
'[email protected]'
);
cy.react('MyTextInput', { props: { field: { name: 'password' } } }).type(
'whyMe?'
);
});Get React Properties from element
Let's take same Form example
Get Props
You can get the React properties from a React element and validate the properties run time.
// set the email in the form
cy.react('MyTextInput', { props: { field: { name: 'email' } } }).type(
'[email protected]'
);
// validate the property runtime
cy.getReact('MyTextInput', { props: { field: { name: 'email' } } })
.getProps('fields.value')
.should('eq', '[email protected]');
// to get all the props, simply do not pass anything in getProps() method
cy.getReact('MyTextInput', { props: { field: { name: 'email' } } }).getProps();
Get current state
cy.getReact('MyTextInput', {
props: { field: { name: 'email' } },
}).getCurrentState(); // can return string | boolean | any[] | {}Timeouts
You can configure the timeouts in the cypress.json configuration file. Alternatively, you can also pass the timeout as a object literal in the react commands like,
cy.react('MyComponent', { options: { timeout: 50000 } });Fetch indexed node
cy.reactreturns DOM element, so you can fetch the indexed node by .eq(index), like:
cy.react('MyComponent').eq(0).click();cy.getReact()return RESQ node, so you can't fetch it through.eq(). You need to use.nthNode(index), like:
cy.getReact('MyComponent')
.nthNode(0)
.getProps('name')
.should('eq', 'First Item');Use fluent chained queries
You can chain react-selector queries like:
- combine with cypress native element
.find()-
cy.react('FormComponent').find('input').type('buy milk');
cy.react('FormComponent').find('button').click();- fetch
HTMLElementsby chainedreactqueries
cy.react('MyComponent', { props: { name: 'Bob' } })
.react('MyAge')
.should('have.text', '50');- fetch
react props and statesby chainedgetReactquery
cy.getReact('MyComponent', { props: { name: 'Bob' } })
.getReact('MyAge')
.getProps('age')
.should('eq', '50');:warning: Fluent commands are not working in some special cases. It is being tracked here
Sample Tests
Community Projects
Credit goes to Gleb Bahmutov for drafting how
cypress-react-selectorcan be used inreact component testinghereCredit goes to gregfenton for presenting a
formik formexample that usescypress-react-selector. Checkout the work here
[If you have a cool project, feel free to portray here]
Tool You Need
React-Dev-Tool — You can inspect the DOM element by simply pressing the f12. But, to inspect REACT components and props, you need to install the chrome plugin.
Tell me your issues
you can raise any issue here
Contribution
Any pull request is welcome.
Before you go
If it works for you , give a Star! :star:
