Skip to content

Use Shared Variables with Cypress in Typescript

September 11th, 2019 - SoftwareTutorial(1 min)

This week I started adding Cypress testing to my blog (with some surprises too!). I insisted to use typescript with Cypress and my code looks now like this:

describe('SEO', () => {
  beforeEach(() => {
    cy.visit('/').waitForRouteChange()
  })

  it('Contains title tag', () => {
    cy.get('head title').should('contain', 'Laur IVAN|PRO')
  })
})

This is fine and it works.

Thing is that I have the text ‘Laur IVAN|PRO” defined in another file and the site is getting it from that file.

I tried to import the file via:

import website from '../../config/website'

To my surprise, I got an error:

ParseError: ‘import’ and ‘export’ may appear only with ‘sourceType: module’

Following this link, I found out that Cypress is executed directly in hte browser. Hence the modern import syntax is not supported.

Solution

My solution (following the SO question above) is as follows:

  1. Add the dependent packages:

    yarn add -D ts-loader @cypress/webpack-preprocessor
  2. Update the cypress/tsconfig.json file:

    {
      "compilerOptions": {
        "strict": true,
        "baseUrl": "../node_modules",
        "target": "es6",
        "lib": ["es6", "dom"],
        "types": ["cypress"],
        "allowJs": true
      },
      "include": ["**/*.ts"]
    }
  3. Create a cypress/webpack.config.js file with the following:

    const path = require('path')
    
    module.exports = {
      entry: './src/index.ts',
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/
          }
        ]
      },
      resolve: {
        extensions: ['.tsx', '.ts', '.js']
      },
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      }
    }
  4. Add the following to the /cypress/plugins/index.js file:

    const webpack = require('@cypress/webpack-preprocessor')
    
    module.exports = on => {
      const options = {
        // send in the options from your webpack.config.js, so it works the same
        // as your app's code
        webpackOptions: require('../webpack.config'),
        watchOptions: {}
      }
    
      on('file:preprocessor', webpack(options))
    }

    This will make sure the typescript will be processed before execution :)

Now my typescript file looks like this:

import website from '../../config/website-components'

describe('SEO', () => {
  beforeEach(() => {
    cy.visit('/').waitForRouteChange()
  })

  it('Contains title tag', () => {
    cy.get('head title').should('contain', website.title)
  })
})

Downsides

As with any hack, there are downsides. The ones I found are:

  1. Increased processing time (yes, the files need to be compiled into JS)
  2. If you’re not careful, you may end up with webpack errors which I personally am not fond of.

Nevertheless, the convenience of having variables trumps these issues for the time being (the alternative would be to move to pure javascript).

HTH,

Share on
Reddit
Linked in
Whatsapp

A little experiment: