In this post I will show how you can write end-to-end tests in TypeScript and how to import from test code your application source files using path aliases like this:
1 | import {greeting} from '@app/greeting' |
instead of brittle relative paths like this
1 | import {greeting} from '../../app/src/greeting' |
Note: the source code for this blog post is at bahmutov/using-ts-aliases-in-cypress-tests
Application
For this demo I will use a minimal example: just an HTML page index.html with some TypeScript code
1 | <html> |
The code src/app.ts places the greeting imported from src/utils.ts into the DOM
1 | export const greeting = 'Hello World' |
1 | import { greeting } from "./utils" |
To serve the app I will use Parce bundler
1 | npm i -D parcel-bundler |
1 | { |
When I run npm start
the page is working as expected at localhost:1234
Cypress Tests in TypeScript
We can add Cypress end-to-end tests to this project with
1 | npm i -D cypress |
To quickly scaffold everything, I prefer to use my little utility @bahmutov/cly which stands for "quickly". Or maybe it stands for "Cypress CLI"? Who knows.
1 | $ npx @bahmutov/cly init |
We have cypress.json
and cypress
folder, let's change the contents of cypress/integration/spec.js
to test our page.
1 | /// <reference types="Cypress" /> |
Start the app in one terminal with npm start
and open Cypress from another terminal with npx cypress open
- the test should be green.
But if we write our application in TypeScript, let's also write our tests in TypeScript. The simplest way to configure test bundling is by installing @bahmutov/add-typescript-to-cypress package. We also need to install TypeScript module itself, and we need Webpack
1 | npm install --save-dev @bahmutov/add-typescript-to-cypress typescript webpack |
Super, it even has created a default tsconfig.json
file for us
1 | { |
We can rename our test file from spec.js
to spec.ts
- and it should run the same. Since the tsconfig.json
file is only necessary for our Cypress tests I will move it into the cypress
folder. Do not forget to update the paths in tsconfig.json
after moving.
Our application shows the greeting text - and I don't want to hardcode the string to find in my test code. Instead I think it is ok to load the greeting from the application code. It is simple to do using a relative path.
1 | import {greeting} from '../../src/utils' |
Nice, but I really dislike the long relative paths that use ../..
to get out of the Cypress integration folder. Luckily TypeScript and Webpack both have ways to define aliases to use shortcuts. We need TypeScript path aliases to make sure our TypeScript tooling (like VSCode IntelliSense) understands the spec files, while Webpack aliases are needed to find the code during bundling.
Our goal is to refer to all source files by @src/...
from our spec files rather than ../../src/...
.
In the cypress/tsconfig.json add baseUrl
and paths
properties.
1 | { |
Nice, now we can import greeting
from the test file like this
1 | import {greeting} from '@src/utils' |
VSCode can resolve the alias correctly, as shown by this popup
But if we try to run Cypress test right now, we will get a nasty error
1 | ./cypress/integration/spec.ts |
This is due to the fact that Webpack bundler does not know about the path aliases in tsconfig.json
. The simplest way is to tell Webpack how to alias modules by prefix. In file cypress/plugins/cy-ts-preprocessor.js add the following alias
object to the existing resolve
block:
1 | const webpackOptions = { |
That is it, our tests can share code with application without fragile folder hops.