DEV Community

Aditya
Aditya

Posted on • Originally published at adityanaik.dev on

How to access Redux state in Cypress

Set up our application

I borrowed the code from react-redux.js.org which showcases react + redux in action using a todo list application.

Set up and scaffold Cypress

Cypress is a fantastic testing framework. It is easy to set up and can be picked up pretty quickly.

Setting up Cypress is pretty straight forward - just run:

$ npm install cypress
or
$ yarn add cypress
Enter fullscreen mode Exit fullscreen mode

We will also install the recommeded dependency

$ npm install -D start-server-and-test
Enter fullscreen mode Exit fullscreen mode

start-server-and-test is a cool tool that basically

Starts server, waits for URL, then runs test command; when the tests end, shuts down server

as explained on their github repo.

How to access the store in Cypress

We don't have access to store() object ordinarily, but cypress can access window() object.
We use this to attach the store to window in our index.js.

import React from 'react'
import ReactDOM from 'react-dom'

import { Provider } from 'react-redux'
import store from './redux/store'

import TodoApp from './TodoApp'

import * as serviceWorker from './serviceWorker'

ReactDOM.render(
  <Provider store={store}>
    <TodoApp />
  </Provider>,
  document.getElementById('root')
)

if (window.Cypress) {
  window.store = store
}
Enter fullscreen mode Exit fullscreen mode

Now we have access to store() and more importantly the state inside it.

Writing the test

Now that we have the store available, we can access redux state at

cy.window().its('store').invoke('getState')
Enter fullscreen mode Exit fullscreen mode

The final version of the test will look like this

/// <reference types="Cypress" />

describe('Tests functionality and redux state', () => {
  it('Successfully uses todo application', () => {
    cy.visit('/')

      // assertions on view, tabs and redux
      .get('[data-cy=Header]')
      .should('have.text', 'Todo List')
      .get('.add-todo')
      .should('have.text', 'Add Todo')
      .get('.todo-list')
      .should('have.text', 'No todos, yay!')
      .get('.visibility-filters')
      .should('include.text', 'all')
      .get('.visibility-filters')
      .should('include.text', 'completed')
      .get('.visibility-filters')
      .should('include.text', 'incomplete')

      .window()
      .its('store')
      .invoke('getState')
      .then((state) => {
        expect(state.visibilityFilter).to.be.a('string').and.equal('all')
        expect(state.todos.allIds).to.be.a('array').and.to.be.empty
        expect(state.todos.byIds).to.be.a('object').and.to.be.empty
      })

      // add a todo, add another todo , mark the first one as complete
      .get('input')
      .type('My First Todo')
      .get('.add-todo')
      .click()
      .get('input')
      .type('My Second Todo')
      .get('.add-todo')
      .click()
      .get('.todo-list')
      .eq(0)
      .click()

      // assertions on view, tabs and redux
      .get('.filter')
      .eq(0)
      .should('include.text', 'all')
      .click()
      .get('.todo-item')
      .should('include.text', '👋 My First Todo')

      .get('.filter')
      .eq(2)
      .should('include.text', 'incomplete')
      .click()
      .get('.todo-item')
      .should('include.text', '👋 My First Todo')

      .get('.filter')
      .eq(1)
      .should('include.text', 'completed')
      .click()
      .get('.todo-item')
      .should('have.text', '👌 My Second Todo')
      .eq(0)
      .children()
      .should('have.class', 'todo-item__text--completed')
      .get('.filter')
      .eq(0)
      .should('include.text', 'all')
      .click()

      .window()
      .its('store')
      .invoke('getState')
      .then((state) => {
        expect(state.visibilityFilter).to.be.a('string').and.equal('all')
        expect(state.todos.allIds).to.be.a('array').and.to.have.lengthOf(2)
        expect(state.todos.byIds)
          .to.be.a('object')
          .and.to.deep.equal({
            1: { content: 'My First Todo', completed: false },
            2: { content: 'My Second Todo', completed: true },
          })
      })
  })
})
Enter fullscreen mode Exit fullscreen mode

Want more ?

We can also dispatch redux actions like this.

cy.window()
  .its('store')
  .invoke('dispatch', {
    type: 'ADD_TODO',
    payload: { content: 'Dispatched TODO', completed: true },
  })
Enter fullscreen mode Exit fullscreen mode

Top comments (0)