lukaszwozniak.dev
Published on

How to prepare a GatsbyJS blog for testing with Cypress

How to prepare a GatsbyJS blog for testing with Cypress
Authors

It's not a good idea to use your original posts in end-to-end testing. Tests can become sensitive to change (when you decide to edit your post, e.g. change some tag or fix description typo).

Especially during working on a new layout - it is worth testing blog post details (like title, tags, or date) on both - list (/blog) and post (/blog/[slug]) pages, navigation between posts, tags logic and more!

Here I will describe how to mock blog posts to provide post-edit insensitive tests.

At the end of this post, I'll link a repository with an example based on Gatsby's blog starter.

Setup Cypress

Add Cypress as a devDependency to your GatsbyJS project:

npm i cypress --save-dev

Create cypress.json file in the project directory (at the same level as package.json) with the following content (we will use port 3030):

{
  "baseUrl": "http://localhost:3030/"
}

Create a cypress directory containing the following structure (note: blog dir should have the same structure as your original content/blog directory):

.
├── cypress
│   ├── fixtures
│   │   ├── post.json           # file with assertion of post data
│   │   └── blog                # mock of posts
│   │   │   └── ...             # clone of content/blog structure
│   └── integration             # dir with tests to run by cypress
│       ├── [your-test].spec.js
│       └── ...

Prepare post fixtures:

Install cross-env:

npm i cross-env --save-dev

Then add the GATSBY_TEST=cypress flag (you can name it whatever you want) and command to open cypress in package.json scripts:

  "scripts": {
    "develop": "gatsby develop",
+   "develop:test": "cross-env GATSBY_TEST=cypress gatsby develop --port=3030",
+   "cypress:open": "cypress open"

In the gatsby-config.js file find code responsible for the copying of blog posts from content/blog and use the GATSBY_TEST flag:

depending on npm run develop command different .md post files will be copied.

{
  resolve: "gatsby-source-filesystem",
  options: {
+    path: process.env.GATSBY_TEST === "cypress"
+         ? `${__dirname}/cypress/fixtures/blog`
+         : `${__dirname}/content/blog`,
-    path: "${__dirname}/content/blog",
    name: "blog"
  },
},

Write test

This is an example of a test that checks if a post is displayed in the post list:

describe('Blog post display every detail on list', async () => {
  let postFixture

  before(() => {
    cy.fixture('blog/post').then((post) => {
      postFixture = post
    })
  })

  beforeEach(() => {
    cy.visit('/')
  })

  it('Title is displayed', () => {
    const { title } = postFixture
    cy.get(`*[data-cy="${title}-title"]`).should('contain.text', title)
  })

  it('Date is displayed', () => {
    const { title, date } = postFixture
    cy.get(`*[data-cy="${title}-date"]`).should('contain.text', date)
  })
})

According to the Cypress documentation, the test uses data-cy attribute to locate the element in DOM.

The test uses the postFixture variable that is loaded from the /cypress/fixtures/blog/post.json file. The file contains expected values of tests for easier editing and reusing these values (there is a few test files that are using the same expected values):

{
  "title": "Test post 2",
  "date": "February 25, 2021"
}

Automate your tests

Below there is an example of a configuration file to run e2e tests on every push or pull request to master and develop branches:

name: End-to-end tests
on:
  push:
    branches: [master, develop]
  pull_request:
    branches: [master, develop]
jobs:
  cypress-run:
    runs-on: ubuntu-18.04
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Cypress run
        uses: cypress-io/github-action@v2
        with:
          browser: chrome
          start: npm run develop:test
          wait-on: http://localhost:3030

Repository with working example.

Passed Cypress end-to-end tests