High Quality React apps with Nx, Cypress & Storybook
Tags: react, cypress, testing, storybook
Published: 2020-06-08 19:46:09

---
title: High Quality React apps with Nx, Cypress & Storybook
canonical_url: 'https://cypress.io/blog/2020/04/14/high-quality-react-apps-with-nx-cypress/'
published: true
description: Learn how to use Nx, Cypress & Storybook to build high quality apps
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/isq9p0r5rnlv33t6xc3j.png
tags: 
  - reactjs
  - cypress
  - testing
  - storybook
---

_This post has originally been published on [https://cypress.io/blog/2020/04/14/high-quality-react-apps-with-nx-cypress/](https://cypress.io/blog/2020/04/14/high-quality-react-apps-with-nx-cypress/). Go to [https://cypress.io/blog](https://cypress.io/blog) for more content_

---

It all started with sprinkling some jQuery here and there to make our server-side rendered pages more dynamic and appealing. Since then we’ve come a long way. Nowadays, entire platforms are being built on the frontend, with JavaScript/TypeScript and your framework of choice. Those are no more only simple web pages, but rather sophisticated, feature-rich applications built for the browser.

![Frontend architecture](https://dev-to-uploads.s3.amazonaws.com/i/4ke4ohwcso9ebqg07czi.png)

As a result, we need to have a different approach to developing such software. Also on the frontend we need to think about topics such as application architecture, state management, modularisation, scaling development across multiple teams and most importantly, automation and quality assurance.

[Nrwl’s Nx](https://nx.dev) has been my preferred choice over the last couple of years when it comes to tackling such projects. Nx is a set of extensible dev tools for monorepos. While monorepos have their origin in large enterprises such as Google or Facebook, they’ve recently become more and more popular also for smaller projects. Why? Increased team velocity and less code/version management overhead. By having all the relevant code co-located in one git repository, it is easy to make project-wide refactoring, implement cross-project features, which otherwise would be much more tedious and time consuming. Monorepos also come with a cost though which is why you need great tooling to support you! And this is where Nx and Cypress come into play. 


### TL;DR

Want a video walkthrough instead? Here you go
{% youtube mfJBLhjYMdo %}

## React App with Cypress tests in under a minute*
> * = npm installs excluded 😉

Nx supports Angular, React and Node out of the box and is potentially open to other frameworks via [its plugin system](https://nx.dev/web/guides/nx-plugin). You can even have multiple different types of projects in the same workspace. But for now, going forward we’ll use use React as an example.

To get started let’s create a new workspace:


```
$ npx create-nx-workspace mynxworkspace
```

After the workspace has been initialized, you’ll see a series preconfigured setups to choose from. We’ll choose React for this article:

![Nx Workspace Wizard](https://dev-to-uploads.s3.amazonaws.com/i/4qakv7p9ngj2v3w3jq32.png)

The wizard continues asking for a couple of configs and workspace settings, like the app name to be generated, the styling framework to use etc. After that we should get the following Nx workspace:

![Cypress e2e setup generated for our myfirstreactapp](https://dev-to-uploads.s3.amazonaws.com/i/alz0e6mgpwa1cbjsinkh.png)

Nx workspaces are structured into two main categories: apps & libs. As you can see we have the `myfirstreactapp` generated in the `apps` folder, while the `libs` folder is still empty. Notice the `myfirstreactapp-e2e`. That’s a fully functional Cypress setup to test our `myfirstreactapp`.

Let’s launch the app with

```
$ nx run myfirstreactapp:serve
```

or simply

```
$ npm start
```

as `myfirstreactapp` is the default project.

![The generated React app in action](https://dev-to-uploads.s3.amazonaws.com/i/io02pyqbojg35jzlzobw.png)

If we open the `myfirstreactapp-e2e` folder, we se a fully functional Cypress setup with a pre-generated `app.spec.ts` Cypress test.

![Nx generates an e2e setup for each app with a first spec for the app](https://dev-to-uploads.s3.amazonaws.com/i/niu5rca4dbmyeo33ev4z.png)

These Cypress tests can simply be executed with

```
$ nx run myfirstreactapp-e2e:e2e
```

To run them in watch mode, simply append `--watch` to it and you’ll get the Cypress test runner we all learned to love 😊 

![Cypress e2e tests in action](https://dev-to-uploads.s3.amazonaws.com/i/isq9p0r5rnlv33t6xc3j.png)

## Cypress Code completion thanks to TypeScript

Nx loves TypeScript! Thus, all projects and Cypress tests are generated and preconfigured to use TypeScript. No more guessing, but rather code completion for Cypress commands.

![Full autocomplete support](https://dev-to-uploads.s3.amazonaws.com/i/evqd5rivq32snh40pnsi.png)

## Sharing Cypress Commands across apps & libs

If you haven’t checked out the [Cypress Best Practices](https://docs.cypress.io/guides/references/best-practices.html) page, you definitely should. It is the first thing I suggest people to go read. Especially when it comes to [selecting elements](https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements), which - if done wrongly - can lead to very fragile tests.

![From the Cypress Best Practices docs](https://dev-to-uploads.s3.amazonaws.com/i/6rh9l36v2iyho04jsmdg.png)

Hence, rather than writing a selector like..

```typescript
cy.get('h1').contains('Welcome to myfirstreactapp!');
```

..I add a `data-cy` selector on the element I’d like to test. So in my `app.tsx` component, let’s add `data-cy=``"``page-title``"`

![Add Cypress data-cy hook](https://dev-to-uploads.s3.amazonaws.com/i/v0k9etw1dx4olpunkgri.png)

In our `app.spec.ts` we can then use the following selector:

```typescript
cy.get('[data-cy="page-title"]').contains('Welcome to myfirstreactapp!');
```

Always writing the entire `..get(``'``[data-cy…` selector is repetitive, might be error prone and tedious. A perfect case for making it become a [custom Cypress command](https://docs.cypress.io/api/cypress-api/custom-commands.html). Normally you would simply place them in Cypress’s `support/commands.ts` file but since an Nx workspace might potentially host multiple apps and libraries and thus have multiple Cypress based setups as well, I definitely want to share these Cypress commands among these.

That’s where Nx libs come into play. Libs are where most of the work happens. It is where you implement the domain/business features and import them into one or even multiple apps. Let’s create a library called `e2e-utils` and place it under a `shared` folder.

```
$ nx generate @nrwl/workspace:library --name=e2e-utils --directory=shared
```

We generate a `@nrwl/workspace` library, which is a plain TypeScript library since we won’t need any React specific things in there. Note, you don’t have to know all these commands by heart. If you happen to use Visual Studio Code, you can install [NxConsole](https://nx.dev/nx-console) which provides a nice UI driven approach for generating new libraries.

![NxConsole extension for VSCode to generate new libraries](https://dev-to-uploads.s3.amazonaws.com/i/hkwe0h167y2c3fqysvfj.png)

In the newly generated `libs/shared/e2e-utils` library, we create a new folder `commands` and an according `index.ts` inside it. We use that file to host our [custom Cypress commands](https://docs.cypress.io/api/cypress-api/custom-commands.html) that should be shared with the entire workspace.

![Cypress Commands](https://dev-to-uploads.s3.amazonaws.com/i/5hb1s0eicb7fxygr533o.png)

Copy the following into your `commands/index.ts` file:

```typescript
/// <reference types="Cypress" />
declare namespace Cypress {
    interface Chainable<Subject = any> {
    getEl<E extends Node = HTMLElement>(
        identifier: string
    ): Chainable<JQuery<E>>;
    }
}

Cypress.Commands.add(
    'getEl',
    { prevSubject: 'optional' },
    (subject: Cypress.Chainable, identifier: string) => {
    if (subject) {
        return subject.find(`[data-cy="${identifier}"]`);
    } else {
        return cy.get(`[data-cy="${identifier}"]`);
    }
    }
);
```
    

As you can see, we extend the `cy` object with a new function `getEl` that automatically uses the `data-cy` attribute.
Let’s also export the file from our library, by adding the following to the `libs/shared/e2e-utils/src/index.ts`:

```typescript
import './lib/commands';
```

At this point we’re able to import it in our e2e tests for the `myfirstreactapp`  app. Open `myfirstreactapp-e2e/src/support/index.ts` and import it accordingly:

![Shared command import](https://dev-to-uploads.s3.amazonaws.com/i/nl0195rn91tl990t8rkf.png)

> Note, Nx uses path mappings (in the `tsconfig.json`) to map our library exports to a proper scoped name `@mynxworkspace/shared/e2e-utils`. That would potentially even allow us to publish our library and import it via NPM.

Finally we can refactor our `app.spec.ts` to use the new `cy.getEl(…)` function:

```typescript
cy.getEl('page-title').contains('Welcome to myfirstreactapp!');
// cy.get('[data-cy="page-title"]').contains('Welcome to myfirstreactapp!');
```

With this setup, it is easy to place shareable commands into the `e2e-utils` library and they’ll be ready to be used across the various Cypress setups in your workspace.

# Cypress based component tests with Storybook

I love to use [Storybook](https://storybook.js.org/) when creating shared UI components. It gives developers an easy way to visually test their components and fellow team members to check out what’s available. In an Nx workspace this makes even more sense because you might potentially have multiple teams working on it. 
Storybook allows us to develop a component in isolation and provides an excellent documentation for UI components. Wouldn’t it be cool to also automatically test those Storybook’s with Cypress? Luckily Nx has got your back here as well.

To get started, let’s generate a React component library:

```
$ nx generate @nrwl/react:library --name=greeter --directory=shared --style=scss
```

This should generate a new React library under `shared/greeter`:

![Generating greeter library](https://dev-to-uploads.s3.amazonaws.com/i/92ck0vxbrtdsmpaydssj.png)

The component - intentionally - is super simple:

```typescript
import React from 'react';
import './shared-greeter.scss';
export interface SharedGreeterProps {
    name: string;
}
export const SharedGreeter = (props: SharedGreeterProps) => {
    return (
    <div>
        <h1>Hi there, {props.name}</h1>
    </div>
    );
};
export default SharedGreeter;
```

As the next step, let’s add Storybook support, first of all, installing Nrwl’s Storybook dependency:

```
$ npm i @nrwl/storybook --save-dev
```

Next we can again use one of the Nx code generators (called schematics) to generate the storybook configuration for our `greeter` component library:

```
$ nx generate @nrwl/react:storybook-configuration --name=shared-greeter --configureCypress
```

Note the `--configureCypress`! The above command generates the storybook configuration for our greeter library, as well as a `shared-greeter-e2e` Cypress setup

![Generated Cypress component based e2e](https://dev-to-uploads.s3.amazonaws.com/i/tt4zmxty9qvzaipbqium.png)

Also the `--generateStories` automatically generates Storybook stories for your existing library components. In fact if you open the library you should see a `shared-greeter.stories.tsx` file being generated. Open it quickly to inspect its structure. It should look similar to:

```typescript
import { text } from '@storybook/addon-knobs';
import React from 'react';
import { SharedGreeter, SharedGreeterProps } from './shared-greeter';

export default {
    component: SharedGreeter,
    title: 'Shared Greeter'
};

export const primary = () => {
    const sharedGreeterProps: SharedGreeterProps = {
    personName: text('Person Name', 'Juri')
    };
    return <SharedGreeter personName={sharedGreeterProps.personName} />;
};
```

Then we can run it with:

```
$ nx run shared-greeter:storybook
```

![Run Storybook for greeter component](https://dev-to-uploads.s3.amazonaws.com/i/1aywikqlbv13ppjavfrt.png)

There’s one interesting property of Storybook. You can navigate to `/iframe.html` and and control it via the URL. In our case, the story id would be `shared-greeter``--``primary` and we can control the “Person Name” via the `knob-Person Name` query param. For example:

```
/iframe.html?id=shared-greeter--primary&knob-Person Name=Juri
```

We can leverage this knowledge in our Cypress tests! By having provided `--configureCypress` when adding the Storybook configuration to our libary, Nx has automatically generated a Cypress setup for it. Open the `apps/shared-greeter-e2e` project and create a new test `greeter.spec.ts` inside the `integration` folder (create it if it isn’t there).

```typescript
describe('greeter component', () => {

    it('should display greeting message', () => {
    cy.visit('/iframe.html?id=shared-greeter--primary&knob-Person Name=Juri');
    cy.getEl('greeting').contains('Hi there, Juri!');
    });

    it('should display the person name properly', () => {
    cy.visit('/iframe.html?id=shared-greeter--primary&knob-Person Name=John');
    cy.getEl('greeting').contains('Hi there, John!');
    });

});
```
    
> Note, following best practices, similarly as in the previous section, we add the `data-cy=``"``greeting``"` `` onto our  `SharedGreeter` component and import our shared `e2e-utils` Cypress commands.

From within the Cypress test we exercise our Story with different inputs and see whether our component reacts properly.

We can run the tests in the same way we did for the app previously, but now obviously passing our library project (feel free to pass `--watch` as a param):

```
$ nx run shared-greeter-e2e:e2e
```

![Run Storybook test](https://dev-to-uploads.s3.amazonaws.com/i/lsgjclk5gmjvq9lskuid.png)

> Also, check out Isaac Mann’s talk about “[E2E Testing at Half the Cost](https://youtu.be/C88th0SbepE)” which talks about Cypress and Storybook integration.


# Running on CI

Automated tests are only useful if you can run them in an automated fashion on your CI server. Cypress has already an in-depth guide on [Continuous Integration](https://docs.cypress.io/guides/guides/continuous-integration.html) which is especially helpful to configure your CI environment to be able to run Cypress tests. Nx is fully optimized to be able to run in CI environments as well. As such it comes with a series of so-called “affected” commands. Internally Nx builds a graph of the workspace apps and libraries. You can generate it by running `npm run dep-graph`. Right now the graph looks as follows:

![Nx dependency graph for our sample workspace](https://dev-to-uploads.s3.amazonaws.com/i/v2c6579yd2vc40cas8pr.png)

Let’s create another react app and import the `SharedGreeter` component. The graph changes to the following:

![See changes on dep-graph when libraries evolve](https://dev-to-uploads.s3.amazonaws.com/i/q0ehuhuu028783mao78z.png)

We also get a Cypress test setup for our 2nd react app, which also happens to import our greeter component. In a normal workspace, CI would run all of the tests. Clearly as our app grows (in particular in a monorepo scenario) this is not scalable. Nx however, is able to use this graph to calculate the libraries that have been touched and thus only run the necessary tests. Assume someone creates a PR, changing the SharedGreeter component. In such scenario, running

```
$ npm run affected:e2e
```

..would only execute the Cypress tests for our GreeterComponent as well as of `my2ndreactapp` as they might both potentially be affected by the change. Running `npm run affected:dep-graph` visualizes this:

![Affected dep graph](https://dev-to-uploads.s3.amazonaws.com/i/bcgkfnyjb8lccy5aq2ua.png)

This greatly improves the running time and helps to avoid unnecessarily executing commands for libraries/apps that are not affected by the changes.

Note this doesn’t only apply to e2e tests, but also to unit tests, linting and building.

# More speed: never test the same code twice, with Nx Cloud

Nx’s affected commands already help a lot to reduce CI time. But still, based on your changes & workspace library structure, you might still end up affecting a lot of libraries and thus running lots of builds/tests.

However, you could even improve this further by never running the same command twice. How? With **computation caching**! Starting from v9.2 Nx has a built-in computation caching mechanism. Whenever your run a command, Nx analyzes the involved source files and configuration and caches the result. If you happen to run the same command again, without any changes to your src files, Nx simply outputs the previous result from the cache. You can [read more about it here](https://blog.nrwl.io/how-to-never-build-or-test-the-same-code-twice-2dc58e413279?source=collection_home---4------0-----------------------).

This greatly speeds up your local runs. But you can even make this cache distributed and remote by subscribing and configuring [Nx Cloud](https://nx.app). That way you can share your cache with co-workers or your CI server.
Nx Cloud keeps track of all the executed commands, indexes the involved environment and library contents as well as the execution result. Whenever some of your work mates executed a particular set of Cypress tests and you happen to run them as well, instead of loosing precious time, waiting for the test run to finish,  you’ll get the cached result from your co-worker.

This also works for CI! Here’s what it looks like when the build pipeline has already executed the tests and I re-run them locally again on my machine:

{% twitter 1240393333610024965 %}

All this doesn’t need any particular configuration but can lead to significant time savings. Here’s a graph of running Cypress e2e tests on CI. On day 18 Nx Cloud has been activated, immediately leading to drastic time savings from around ~30 minutes down to ~15 minutes in a couple of days.

![Savings of activating NxCloud cache on e2e tests](https://dev-to-uploads.s3.amazonaws.com/i/lqnv9mf38juxlwnl5905.png)

Curious? Get access to Nx Cloud on https://nx.app and make your Cypress tests blazingly fast!

# Conclusion

In this article we learned about how we can leverage Nx together with Cypress to automate our testing setup. We’ve seen

- how to setup a new React based workspace with Cypress e2e tests for our apps
- how to generate Nx libraries with Storybook support
- how to share custom Cypress commands
- how to leverage Storybook to create Cypress based tests for our React components
- how TypeScript can help explore the Cypress API via code completion support
- how to speed up Cypress test runs with Nx’s affected commands
- how to never run Cypress tests twice with the support of Nx Cloud

You can check out the source code used in this article at https://github.com/juristr/nx-react-cypress-blogpost.