Shared Cypress Assets in an Nx Workspace
Tags: cypress, nx
Published: 2022-05-17 17:28:50

---
title: Shared Cypress Assets in an Nx Workspace
published: true
date: 2022-05-16 00:00:00 UTC
tags: cypress, nx
canonical_url: https://www.prestonlamb.com/blog/shared-cypress-assets-in-an-nx-workspace
---

## [tldr;](#tldr)

Many times Nx workspaces have multiple applications, and being able to share custom Cypress commands between the end to end test projects. I spent several months manually copying these commands between the different projects, and would inevitably forget to update one when a change was made. Finally I decided to solve the problem and find a way to share the Cypress assets among the projects. This article will cover one way to do this.

To follow along, you'll need an Nx workspace with at least one end to end project.

## [Create the Shared Assets Library](#create-the-shared-assets-library)

Let's get started by creating a shared end to end assets library. This will be similar to creating any other library in an Nx workspace. The only potential difference will be the type of library you create. Nx provides a way to create Angular specific libraries, for example. In this case, we don't want to create a framework specific library. That can be accomplished with the following command:

```sh
$ nx g @nrwl/workspace:library e2e-assets --directory shared
```

This command creates a generic Nx workspace library called `e2e-assets` and puts it in the `libs/shared` directory. In this library we'll add custom Cypress commands which we can then use in all our end to end projects.

## [Create Custom Command File](#create-custom-command-file)

Let's now add a file that contains a custom Cypress command in our new shared library. This custom command will make it easier to select elements by the `data-cy` HTML attribute. Create a file in the `e2e-assets/src/lib` folder called `data-cy.commands.ts`. Here are the contents of that file:

```ts
// data-cy.commands.ts

// load the global Cypress types
/// <reference types="cypress" />

declare namespace Cypress {
  interface Chainable {
    /**
     * Custom command to get elements by data-cy attribute.
     * @example cy.dataCy('greeting')
     */
    dataCy(selector: string): Chainable<Element>;
  }
}

Cypress.Commands.add('dataCy', (value) => cy.get(`[data-cy=${value}]`));
```

The first two lines of the file load the types for Cypress and prevent errors from showing in your IDE. The next portion of the file essentially extends the Cypress object for your tests by declaring the new custom command. If you leave this out and try to use the `dataCy` command you will get an error and the command will not work. The last line of the file is the code to actually add the custom command. The first argument to the `Cypress.Commands.add` method is the name of the new command, and the second argument is a callback function that provides the functionality for the custom command.

This custom command returns the `cy.get` method and selects an element by the `data-cy` HTML attribute. We'll look at how it's used later.

## [Importing the Custom Command](#importing-the-custom-command)

The next step is to import the new custom command so it can be used in our Cypress tests. The first import belongs in the `e2e-assets/src/index.ts` file:

```ts
// e2e-assets/src/index.ts

import './lib/data-cy.commands';
```

This makes the new command available when we import this library into our Cypress tests, which we'll do next. The second import belongs in the `app-e2e/src/support/index.ts` file. By importing the library here, this new command will be available in the `app-e2e` Cypress tests.

```ts
// app-e2e/src/support/index.ts
import '@my-org/shared/e2e-assets';
```

Now that you have imported your shared e2e assets library, you can use the new command.

## [Using The New Custom Command](#using-the-new-custom-command)

Here's an example of using the new custom command in a Cypress test:

```ts
// app-e2e/src/integration/test.ts

it('should show the homepage', () => {
  cy.dataCy('homepage').should('be.visible');
});
```

If you didn't use this custom command, your test would like this:

```ts
// app-e2e/src/integration/test.ts

it('should show the homepage', () => {
  cy.get('[data-cy=homepage]').should('be.visible');
});
```

There's not a big difference here, but speaking from experience it gets tiresome to repeat the attribute selector each time you need access to an element based on the `data-cy` attribute. This simple custom command is much easier to use.

## [Conclusion](#conclusion)

One of the biggest benefits of Nx workspaces is the ability to share code between projects. It's natural to share code between Angular or React apps, but the same process can be used to share code for your Cypress end to end tests. It becomes especially useful the more apps you have in your workspace, and for more complicated Cypress commands