Skip to content

Commit

Permalink
fix(gatsby-cypress): Add TS types & improve README (#37984)
Browse files Browse the repository at this point in the history
  • Loading branch information
LekoArts committed Apr 28, 2023
1 parent af204d2 commit 3b31e35
Show file tree
Hide file tree
Showing 22 changed files with 245 additions and 284 deletions.
285 changes: 122 additions & 163 deletions docs/docs/how-to/testing/end-to-end-testing.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion e2e-tests/development-runtime/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ Cypress.Commands.add(
win.___navigate(pathname)
})

// @ts-expect-error - gatsby-cypress doesn't have types
return cy.waitForAPI(`onRouteUpdate`).then(() => subject)
}
)
Expand Down
1 change: 0 additions & 1 deletion e2e-tests/production-runtime/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ Cypress.Commands.add(
win.___navigate(pathname)
})

// @ts-expect-error - gatsby-cypress doesn't have types
return cy.waitForAPI(`onRouteUpdate`).then(() => subject)
}
)
Expand Down
8 changes: 0 additions & 8 deletions examples/using-cypress/cypress.config.js

This file was deleted.

8 changes: 8 additions & 0 deletions examples/using-cypress/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "cypress"

export default defineConfig({
e2e: {
baseUrl: `http://localhost:8000`,
specPattern: `cypress/e2e`
}
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe("Accessibility tests", () => {
beforeEach(() => {
cy.visit("/").get("main")
cy.visit("/").waitForRouteChange().get("main")
cy.injectAxe()
})
it("Has no detectable accessibility violations on load", () => {
Expand All @@ -9,6 +9,7 @@ describe("Accessibility tests", () => {
it("Navigates to page 2 and checks for accessibility violations", () => {
cy.findByText(/go to page 2/i)
.click()
.waitForRouteChange()
.checkA11y()
})
it("Focuses on the footer link and asserts its attributes", () => {
Expand Down
7 changes: 7 additions & 0 deletions examples/using-cypress/cypress/e2e/smoke.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
describe("Smoke tests", () => {
it("Visits the index page and navigates", () => {
cy.visit("/").waitForRouteChange()
cy.findByText(/go to page 2/i)
.click()
})
})
25 changes: 0 additions & 25 deletions examples/using-cypress/cypress/support/commands.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import "./commands"
import "gatsby-cypress/commands"
import "cypress-axe"
import "@testing-library/cypress/add-commands"
6 changes: 6 additions & 0 deletions examples/using-cypress/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"types": ["cypress", "@testing-library/cypress", "cypress-axe", "gatsby-cypress"]
},
"include": ["."]
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
module.exports = {
import type { GatsbyConfig } from "gatsby"

const config: GatsbyConfig = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
},
plugins: [],
}

export default config
3 changes: 0 additions & 3 deletions examples/using-cypress/gatsby-ssr.js

This file was deleted.

30 changes: 14 additions & 16 deletions examples/using-cypress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,27 @@
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test develop http://localhost:8000 cy:open",
"test:e2e:ci": "start-server-and-test develop http://localhost:8000 cy:run"
"cy:open": "cypress open --browser chrome --e2e",
"test:e2e": "CYPRESS_SUPPORT=y start-server-and-test develop http://localhost:8000 cy:open",
"cy:run": "CYPRESS_baseUrl=http://localhost:9000 cypress run --browser chrome",
"test:e2e:ci": "CYPRESS_SUPPORT=y npm run build && start-server-and-test serve http://localhost:9000 cy:run"
},
"dependencies": {
"gatsby": "next",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@testing-library/cypress": "^8.0.3",
"axe-core": "^4.4.3",
"cypress": "^10.3.1",
"cypress-axe": "^1.0.0",
"start-server-and-test": "^1.14.0"
},
"keywords": [
"gatsby"
]
}
"@testing-library/cypress": "^9.0.0",
"axe-core": "^4.7.0",
"cypress": "^12.11.0",
"cypress-axe": "^1.4.0",
"gatsby-cypress": "^3.9.0",
"start-server-and-test": "^2.0.0",
"typescript": "^5.0.4"
}
}
9 changes: 0 additions & 9 deletions examples/using-cypress/src/components/header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"

const Header = ({ siteTitle }) => (
Expand Down Expand Up @@ -31,12 +30,4 @@ const Header = ({ siteTitle }) => (
</header>
)

Header.propTypes = {
siteTitle: PropTypes.string,
}

Header.defaultProps = {
siteTitle: ``,
}

export default Header
5 changes: 0 additions & 5 deletions examples/using-cypress/src/components/layout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"

import Header from "./header"
Expand Down Expand Up @@ -37,8 +36,4 @@ const Layout = ({ children }) => {
)
}

Layout.propTypes = {
children: PropTypes.node.isRequired,
}

export default Layout
7 changes: 6 additions & 1 deletion examples/using-cypress/src/pages/404.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ const NotFoundPage = () => (

export default NotFoundPage

export const Head = () => <title>404: Not found</title>
export const Head = () => (
<>
<html lang="en" />
<title>404: Not found</title>
</>
)
7 changes: 6 additions & 1 deletion examples/using-cypress/src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ const IndexPage = () => (

export default IndexPage

export const Head = () => <title>Home</title>
export const Head = () => (
<>
<html lang="en" />
<title>Home</title>
</>
)
7 changes: 6 additions & 1 deletion examples/using-cypress/src/pages/page-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ const SecondPage = () => (

export default SecondPage

export const Head = () => <title>Page two</title>
export const Head = () => (
<>
<html lang="en" />
<title>Page Two</title>
</>
)
77 changes: 30 additions & 47 deletions packages/gatsby-cypress/README.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,60 @@
# gatsby-cypress

This package provides additional [Cypress](https://cypress.io/) commands for [testing Gatsby websites](/docs/end-to-end-testing/).
This package provides additional [Cypress](https://cypress.io/) commands for [testing Gatsby websites](https://www.gatsbyjs.com/docs/how-to/testing/end-to-end-testing/).

> **NOTE:** This package is _not_ required to use Gatsby and Cypress together. It only exists to add extra commands for convenience.
**Please note:** This package is **not** required to use Gatsby and Cypress together. It only exists to add extra commands for convenience.

If you want to find elements (e.g. by test ID or by text/label/etc.), consider using [`@testing-library/cypress`](https://github.com/testing-library/cypress-testing-library).

## Installation

To use these commands, first install the necessary packages:

```shell
npm install cypress gatsby-cypress start-server-and-test --save-dev
npm install gatsby-cypress --save-dev
```

## Add custom Gatsby testing commands
## Adding custom Gatsby testing commands

Next, add a new file located at `cypress/support/index.js` and add the Gatsby-specific Cypress commands:
Next, add a new file located at `cypress/support/e2e.ts` and add the Gatsby-specific Cypress commands:

```js:title=cypress/support/index.js
```ts:title=cypress/support/e2e.ts
import "gatsby-cypress/commands"
```

If you're using TypeScript, add its types to Cypress' `tsconfig.json` file:

```json:title=cypress/tsconfig.json
{
"compilerOptions": {
// highlight-next-line
"types": ["cypress", "gatsby-cypress"]
},
"include": ["."]
}
```

Once imported, the following additional commands are available:

- `cy.waitForRouteChange()`: Waits for Gatsby to finish the route change, in
order to ensure event handlers are properly setup. Example:

```js
// after navigating to another page via a link
cy.waitForRouteChange().get(`#element-with-event-handler`).click()
cy.visit(`/page-2`).waitForRouteChange()
```

- [**no longer recommended**] `cy.getTestElement(selector)`: Selects elements where the `data-testid`
attribute matches the value of `selector`. Example:
- `cy.waitForAPI('api-name')`: Waits for a specific Gatsby API to finish. Example:

```jsx
<button data-testid="btn-to-test">click me</button>
```js
cy.waitForAPI(`onRouteUpdate`).get(`#element-with-event-handler`).click()
```

- `cy.waitForAPIorTimeout('api-name')`: Waits for a specific Gatsby API to finish. It timeouts if it doesn't finish. Example:

```js
cy.getTestElement("btn-to-test").click()
cy.waitForAPIorTimeout(`onRouteUpdate`)
```

> **NOTE:** It’s recommended not to use test IDs. Instead, consider using [`cypress-testing-library`](https://github.com/testing-library/cypress-testing-library) and relying on `findByText` instead.
## Running Cypress tests in Gatsby

Add a new script in `package.json` to run the Cypress tests. A common name for this is `cy:open`.

You also need to expose a `CYPRESS_SUPPORT` environment variable to enable [Gatsby test utilities](https://github.com/gatsbyjs/gatsby/blob/1fb376f84ee538bac79824cd119bef6a17c19b33/packages/gatsby/cache-dir/api-runner-browser.js#L9-L18). Place it in your test script in `package.json`, for example:

```diff:title=package.json
"scripts": {
... other scripts ...
+ "cy:open": "cypress open",
+ "test:dev": "CYPRESS_SUPPORT=y start-server-and-test develop http://localhost:8000 cy:open",
+ "test": "CYPRESS_SUPPORT=y npm run build && start-server-and-test serve http://localhost:9000 cy:open"
}
```

> **NOTE:** `test:dev` runs the site in development mode, which allows you to quickly fix and retest any issues. `test` is better suited for build systems like Circle CI, Travis CI, etc.
## Writing Cypress tests for Gatsby pages

Add tests by creating a spec file. We recommend starting with a `cypress/integrations/index.spec.js` to test the home page:

```js
context("Homepage", () => {
beforeEach(() => {
cy.visit(`http://localhost:8000`)
cy.waitForRouteChange()
})

it("has focusable buttons", () => {
cy.findByText("click me").focus()
cy.focused().should("have.text", "click me")
})
})
```
Read the [End-to-End Testing documentation](https://www.gatsbyjs.com/docs/how-to/testing/end-to-end-testing/) to learn how to use Cypress and Gatsby together.
30 changes: 30 additions & 0 deletions packages/gatsby-cypress/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
declare global {
namespace Cypress {
interface Chainable<Subject = any> {
/**
* Waits for Gatsby to finish the route change, in order to ensure event handlers are properly setup
* @example
* cy.visit(`/page-2`).waitForRouteChange()
*/
waitForRouteChange: () => Chainable<Subject>
/**
* Waits for a specific Gatsby API to finish
* @example
* cy.waitForAPI(`onRouteUpdate`).get(`#element-with-event-handler`).click()
*/
waitForAPI: (lifeCycleName: string) => Chainable<Subject>
/**
* Waits for a specific Gatsby API to finish. It timeouts if it doesn't finish.
* @example
* cy.waitForAPIorTimeout(`onRouteUpdate`)
*/
waitForAPIorTimeout: (lifeCycleName: string, options?: WaitForApiOrTimeoutOptions) => Chainable<Subject>
}
}
}

export interface WaitForApiOrTimeoutOptions {
timeout?: number
}

export {}
1 change: 1 addition & 0 deletions packages/gatsby-cypress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "3.10.0-next.1",
"description": "Cypress tools for Gatsby projects",
"main": "index.js",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/gatsbyjs/gatsby.git",
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-cypress/src/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import apiHandler, { waitForAPI } from "./api-handler"

// TODO(v6): Remove this command
Cypress.Commands.add(`getTestElement`, (selector, options = {}) =>
cy.get(`[data-testid="${selector}"]`, options)
)
Expand Down

0 comments on commit 3b31e35

Please sign in to comment.