Test your REACT app efficiently with Protractor
ReactJS is one of the most widely use Front-End libraries on the web. Alongside React, many developers use styling tools that will minify or re-write the class attribute values attached to the HTML elements via className props in JSX. These minifications and overwrites make it difficult to select the generated HTML using the WebDriver’s query commands like findElement or findElements since it’s not guaranteed that the class name will remain the same.
The Missing Piece
JEST and Enzyme greatly help developers to unit test REACT apps with the famous snapshot testing. But, automation testers have been struggling a lot while it comes to the functional testing of the REACT web pages.
As I already said, REACT in combination with different styling libraries (like Material UI), produces dynamic classes and HTML structure. The part which likely to be Non-Dynamic in React is REACT Component and Props.
Protractor has been performing outstanding over the last decade,it has not been the first choice while it comes to REACT testing. It was just missing a part to locate REACT elements efficiently.
Protractor-React-Selector is a lightweight plugin that will help you to find web elements by REACT components and props. Let see in the next section how to use it…
Installation
Install this module locally with the following command to be used as a (dev-)dependency:
npm install --save protractor-react-selector
Configuration
protractor-react-selector can be used as a plugin in your protractor configuration file with the following code. You also need to turn off the Angular Enable Flag.
exports.config = {// ... the rest of your configplugins: [ { // The module name package: "protractor-react-selector" }
]onPrepare: async () => { await browser.waitForAngularEnabled(false);
//you can do magic here. See the Pro-tip in later section
}
};
That’s it. You are now very much set to use the React Selector to find React Elements on the web page.
How to use React Selector
Once you are set with the configuration, you can use the react selector just like any other native locators. But, lets first checkout the APIs:
Format: browser.waitForReact(timeOut?:number,reactRoot?:string)
- timeOut — optional (defaults to10000 ms)
- reactRoot -optional (defaults to ‘#root’)
Format: by.react(react_component,props,state)
- component name — required
- props — optional
- state — optional
Let's take this example REACT APP:
Wait for application to be ready to run tests
To wait until the React’s component tree is loaded, add the waitForReact
method to fixture's before
hook.
this will wait to load react inside your app. waitForReact automatically find out the react root of your application.
The default timeout for waitForReact is 10000
ms. You can specify a custom timeout value:
await browser.waitForReact(30000);
Wait to Load React for different react roots
It may even possible that you have different REACT roots (different REACT instances in the same application). In this case, you can specify the CSS Selector
of the target root.
const App = () => (
<div id='mount'>
<MyComponent />
<MyComponent someBooleanProp={true} />
</div>
)
There is some application which displays react components asynchronously. You need to pass root selector information to the react selector.
await browser.waitForReact(10000, '#mount')
Element filtration by Props and States
You can filter the REACT components by its props and states like below:
const myElement = element(
by.react('MyComponent', { someBooleanProp: true }, { someBooleanState: true })
);
Wildcard selection
You can select your components by partial name use wildcard selectors:
// Partial Match
const myElement = element(by.react('My*', { someBooleanProp: true }));// Entire Match
const myElement = element(by.react('*', { someBooleanProp: true })); // return all components matched with the prop
Sample Tests
Checkout sample tests here
How to find React Components and Props
Finding REACT elements are very easy with React-Dev-Tool chrome plugin. This plugin will help you to inspect REACT components and props.
After installing REACT DEV TOOL, you simply navigate to the target react page and press f12. There will be a special section visible called — Components (As shown in the above picture). If you hover over/ select a segment of the page using arrow selector, respective Component Name and Props Objects will be shown in the right corner of the dev tool. Then you just need to use those component name and props with Protractor-React-Selector.
Suppose, I want to click the “freeCodeCamp” link shown in the above picture, then I can use React-Selector as:
const link=element(by.react('u',{name: "freeCodeCamp"}));
link.click();
Pro-Tip:
If you are testing a REACT application, you might already be using:
onPrepare: async () => {
await browser.waitForAngularEnabled(false);
}
By using the above command, you are instructing webdriver Not to wait for finishing Angular and the outstanding $http or $timeout calls before continuing. So, Protractor runs commands one after another without waiting for the outstanding XHR calls to be completed. The effect? You end up with writing a lot many Expected Conditions or hardcoded waits to save your tests from flakiness.
Fortunately, there is an efficient way to avoid this. You can count on Protractor-Testability-Plugin. The author says:
This plugins enables testing projects and libraries not built against AngularJS services in sync with protractor. Even when using AngularJS there are situations when this plugin my become useful, like when using webworkers or comunicating via websockets.
This means that your TDD/BDD tests will be a lot cleaner as you will not have to add aditional waitings and tweak timeouts to get your tests passing consistently as protractor will know when there’s a task pending and it will wait automatically between each step for it to get completed.
To use the plugin you can first save the dependency by running below command:
npm i — save-dev protractor-testability-plugin
Then you just need to remove the waitForAngular statement. So, after combining this plugin with react selector plugin your protractor conf should look like:
exports.config = {// ... the rest of your configplugins: [ { // The module name package: "protractor-react-selector" },{
package: 'protractor-testability-plugin' } ]onPrepare: () => { // delete the waitforAngular statement
}
};
Parking Lot
I hope this will completely mitigate your hard times finding dynamic REACT elements. Protractor-React-Selector can be used with any protractor framework like Jasmine, Mocha, Cucumber, etc.
Happy Bugging!
In case you like to investigate how Cypress.io handles React application, you can check out this blog. I swear, its fun!
And …If you want to pat on my back, just hit the clap button!