Use Shared Variables with Cypress in Typescript
2 min read

Use Shared Variables with Cypress in Typescript

Use Shared Variables with Cypress in Typescript

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,