• Stars
    star
    262
  • Rank 156,136 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

⚑ cypress plugin to locate react elements by component, props and state

cypress-react-selector

Build Status NPM release code style: prettier NPM Downloads

⚠️ cypress-react-selector 3.x supports cypress 10+

cypress-react-selector 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, cypress-react-selector uses a library called resq to query React's VirtualDOM in order to retrieve the nodes.

Table of Contents

Install and configure

Add as a dependency:

npm i --save-dev cypress-react-selector

Include the commands

Update cypress/support/e2e.js and cypress/support/component.js file to include the cypress-react-selector commands by adding:

import 'cypress-react-selector';

Update component-index.html (Component Testing only)

In order for waitForReact() to work appropriately in component testing, replace the div inside component-index.html with this:

<div id="__cy_root" data-cy-root></div>

TSConfig Settings for types

Add the following to tsconfig.json:

{
  "compilerOptions": {
    "sourceType": "module",
    "types": ["node", "cypress", "cypress-react-selector"]
  }
}

Configuring >= Cypress 10

The following is a sample cypress.config.js file that sets up Cypress e2e and component testing to work with this plugin:

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  video: false,
  screenshotOnRunFailure: false,
  env: {
    'cypress-react-selector': {
      root: '#__cy_root',
    },
  },
  e2e: {
    setupNodeEvents() {},
    specPattern: 'cypress/e2e/**/*.cy.{js,ts,jsx,tsx}',
    excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'],
  },
  component: {
    setupNodeEvents() {},
    specPattern: 'cypress/component/**/*.cy.{js,ts,jsx,tsx}',
    excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'],
    devServer: {
      framework: 'create-react-app',
      bundler: 'webpack',
    },
  },
});

Note: if you're not using component testing, you can remove that from the config.

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
});

NOTE : The Best Configuration for React root is to declare it as an env variable

We always recommend to declare the react root as a env variable in the cypress.config.js 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"
    }
  }
}

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();
});

NOTE: If you are using Webpack with your project, you may need to manually pass in the resq module path.

There's an optional parameter in waitForReact that can be passed in at runtime.

This should be the path of the resq entrypoint

before(() => {
  cy.visit('http://localhost:3000/myApp');
  cy.waitForReact(1000, '#root', 'node_modules/resq/dist/index.js'); // Manually passing in the resq module path
});

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 prop

Find 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-props

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.config.js 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

  1. cy.react returns DOM element, so you can fetch the indexed node by .eq(index), like:
cy.react('MyComponent').eq(0).click();
  1. 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 HTMLElements by chained react queries
cy.react('MyComponent', { props: { name: 'Bob' } })
  .react('MyAge')
  .should('have.text', '50');
  • fetch react props and states by chained getReact query
cy.getReact('MyComponent', { props: { name: 'Bob' } })
  .getReact('MyAge')
  .getProps('age')
  .should('eq', '50');

⚠️ Fluent commands are not working in some special cases. It is being tracked here

Sample Tests

  • Checkout component testing use cases here
  • Checkout e2e tests here

Community Projects

  • Credit goes to Gleb Bahmutov for drafting how cypress-react-selector can be used in react component testing here

  • Credit goes to gregfenton for presenting a formik form example that uses Cypress-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! ⭐

More Repositories

1

axe-playwright

β™Ώ: Custom commands for Playwright to run accessibility (a11y) checks with axe-core
TypeScript
186
star
2

playwright-lighthouse

🎭: Playwright Lighthouse Audit
JavaScript
176
star
3

any-text

Get text content from any file
JavaScript
50
star
4

testcafe-lighthouse

πŸ” Run Lighthouse performance audit with TestCafe
JavaScript
10
star
5

cypress-e2e-boilerplate

πŸ”₯ Cypress starter pack for UI + API automation [Cypress + Typescript + Allure +Docker + Jenkins]
JavaScript
8
star
6

protractor-cucumber-serenity-typescript-starter

β›„ Protractor cucumber boilerplate with serenity reporting with extensive react support
TypeScript
7
star
7

cucumber-quick

πŸ‡ vscode extension to run cucumber scenarios quickly and easily
TypeScript
4
star
8

testcafe-typescript-starter

β˜• Testcafe typescript boilerplate with allure report
TypeScript
4
star
9

testcafe-graphql-mock

custom plugin for testcafe to mock graphql server
JavaScript
3
star
10

google-cloud-build-postman

☁️ Continuous deployment and testing of Google cloud functions with Postman and Cloud Build
JavaScript
2
star
11

storybook-deadlink-checker

A storybook addon that checks all of the hyperlinks in a storybook markdown file to determine if they are alive or dead
JavaScript
2
star
12

protractor-react-selector

React element locator for protractor using component, props and states
JavaScript
2
star
13

cypress-serenity-experiment

cypressic experiment with serenity-js πŸ‘»
TypeScript
1
star
14

cypress-react-mobx-component-testing

Test React MobX store with cypress.io
JavaScript
1
star
15

chai-retry

A retry plugin for chai assertions
JavaScript
1
star
16

postman-coverage

derive postman collection coverage against openapi schema
TypeScript
1
star
17

playwright-lighthouse-audit-example

Example to perform Lighthouse audit using Playwright
TypeScript
1
star
18

specflow-bdd-framework

C# + Specflow + Extent Report
HTML
1
star
19

cucumber-bdd-api-framework

TypeScript + Cucumber.io + Request + Cucumber-HTML-Report
TypeScript
1
star
20

protractor-css-booster

A protractor plugin which enable you to use special custom CSS locators
TypeScript
1
star