TypeScript
What you'll learn​
- How to set up TypeScript in Cypress
- How to configure TypeScript for custom commands, assertions, and plugins
- How to use TypeScript with Cypress component testing
- How to avoid clashing types with Jest
- How to set up your development environment for TypeScript
Cypress ships with official type declarations for TypeScript. This allows you to write your tests in TypeScript.
Get Started​
Install TypeScript​
To use TypeScript with Cypress, you will need TypeScript 4.0+. If you do not already have TypeScript installed as a part of your framework, you will need to install it:
- npm
- yarn
npm install typescript --save-dev
yarn add typescript --dev
Configure tsconfig.json​
We recommend creating a
tsconfig.json
inside your
cypress
folder
with the following configuration:
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts"]
}
The "types"
will tell the TypeScript compiler to only include type definitions
from Cypress. This will address instances where the project also uses
@types/chai
or @types/jquery
. Since
Chai and
jQuery are
namespaces (globals), incompatible versions will cause the package manager
(yarn
or npm
) to nest and include multiple definitions and cause conflicts.
You may have to restart your IDE's TypeScript server if the setup above does not appear to work. For example:
VS Code (within a .ts or .js file):
- Open the command palette (Mac:
cmd+shift+p
, Windows:ctrl+shift+p
) - Type "restart ts" and select the "TypeScript: Restart TS server." option
If that does not work, try restarting the IDE.
Processing your Cypress configuration and plugins​
Cypress needs to be able to transpile your Cypress configuration and plugins written in TypeScript in order to make them executable within Cypress's Node.js runtime. To do this, Cypress will attempt to read the user's TypeScript and project configuration to apply the correct TypeScript loader to Cypress's Node.js runtime.
If your project is an ESM
package (short for ECMAScript Module), Cypress attempts to apply the ts-node/esm Node.js loader to resolve the Cypress configuration and plugins. ESM
is determined by Cypress if you have the type: "module"
key-value pair present inside your project's package.json
.
Otherwise, regular ts-node is required into Cypress's Node.js runtime.
Since Node.js by itself can only interpret CommonJS files, Cypress attempts to make your TypeScript configuration compatible with Cypress' Node.js runtime.
To do this, Cypress overrides the following configuration values found inside your project's tsconfig.json
:
{
"module": "commonjs",
"moduleResolution": "node",
"preserveValueImports": false
}
This does not have an impact on your project or its TypeScript configuration settings. This override only happens within the context of the Cypress runtime.
Extending TypeScript Support​
Types for Custom Commands​
When adding custom commands to the cy
object, you can manually add their types to avoid TypeScript errors.
For example if you add the command cy.dataCy
into your
supportFile like this:
// cypress/support/index.ts
Cypress.Commands.add('dataCy', (value) => {
return cy.get(`[data-cy=${value}]`)
})
Then you can add the dataCy
command to the global Cypress Chainable interface
(so called because commands are chained together).
// cypress/support/index.ts
declare global {
namespace Cypress {
interface Chainable {
/**
* Custom command to select DOM element by data-cy attribute.
* @example cy.dataCy('greeting')
*/
dataCy(value: string): Chainable<JQuery<HTMLElement>>
}
}
}
A nice detailed JSDoc comment above the method type will be really appreciated by any users of your custom command.
Types of all the parameters taken by the implementation callback are inferred
automatically based on the declared interface. Thus, in the example above, the
value
will be of type string
implicitly.
In your specs, you can now use the custom command as expected
- End-to-End Test
- Component Test
it('works', () => {
// from your cypress/e2e/spec.cy.ts
cy.visit('/')
// IntelliSense and TS compiler should
// not complain about unknown method
cy.dataCy('greeting')
})
it('works', () => {
// from your src/components/MyComponent.cy.ts
cy.mount(<MyComponent />)
// IntelliSense and TS compiler should
// not complain about unknown method
cy.dataCy('greeting')
})
Adding child or dual commands​
When you add a custom command with prevSubject
, Cypress will infer the subject
type automatically based on the specified prevSubject
.
// cypress/support/index.ts
declare global {
namespace Cypress {
interface Chainable {
/**
* Custom command to type a few random words into input elements
* @param count=3
* @example cy.get('input').typeRandomWords()
*/
typeRandomWords(
count?: number,
options?: Partial<TypeOptions>
): Chainable<JQuery<HTMLElement>>
}
}
}
// cypress/support/index.ts
Cypress.Commands.add(
'typeRandomWords',
{ prevSubject: 'element' },
(subject /* :JQuery<HTMLElement> */, count = 3, options?) => {
return cy.wrap(subject).type(generateRandomWords(count), options)
}
)
Overwriting child or dual commands​
When overwriting either built-in or custom commands which make use of
prevSubject
, you must specify generic parameters to help the type-checker to
understand the type of the prevSubject
.
interface TypeOptions extends Cypress.TypeOptions {
sensitive: boolean
}
Cypress.Commands.overwrite<'type', 'element'>(
'type',
(originalFn, element, text, options?: Partial<TypeOptions>) => {
if (options && options.sensitive) {
// turn off original log
options.log = false
// create our own log with masked message
Cypress.log({
$el: element,
name: 'type',
message: '*'.repeat(text.length),
})
}
return originalFn(element, text, options)
}
)
As you can see there are generic parameters <'type', 'element'>
are used:
- The first parameter is the command name, equal to first parameter passed to
Cypress.Commands.overwrite
. - The second parameter is the type of the
prevSubject
that is used by the original command. Possible values:- 'element' infers it as
JQuery<HTMLElement>
- 'window' infers it as
Window
- 'document' infers it as
Document
- 'optional' infers it as
unknown
- 'element' infers it as
Examples:​
- See Adding Custom Commands (TS) example recipe.
- Example project cypress-example-todomvc custom commands uses custom commands to avoid boilerplate code.
Types for custom assertions​
If you extend Cypress assertions, you can extend the assertion types to make the TypeScript compiler understand the new methods. See the Recipe: Adding Chai Assertions for instructions.
Types for plugins​
You can utilize Cypress's type declarations in your plugins file by annotating it like the following:
// cypress/plugins/index.ts
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {}
Using an External Typings File​
You might find it easier to organize your types by moving them from the support
file into an external
declaration (*.d.ts) file.
To do so, create a new file, like cypress.d.ts, and cut the types for your
custom commands/assertions from the support file and into the new file. Below
is an example of moving the custom cy.mount
typings that come by default with
a component testing app into a root level cypress.d.ts file.
- cypress.d.ts
import { mount } from 'cypress/react'
// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
You might need to include the *.d.ts in the include options in any tsconfig.json files in your project for TypeScript to pick up the new types:
- tsconfig.json
"include": [
"src",
"./cypress.d.ts"
]
- ./cypress/tsconfig.json
"include": [
"**/*.ts",
"../cypress.d.ts"
]
Set up your dev environment​
Please refer to your code editor in TypeScript's Editor Support doc and follow the instructions for your IDE to get TypeScript support and intelligent code completion configured in your developer environment before continuing. TypeScript support is built in for Visual Studio Code, Visual Studio, and WebStorm - all other editors require extra setup.
Clashing types with Jest​
If you are using both Jest and Cypress in the same project, the TypeScript types
registered globally by the two test runners can clash. For example, both Jest
and Cypress provide the clashing types for the describe
and it
functions.
Both Jest and Expect (bundled inside Cypress) provide the clashing types for the
expect
assertion, etc.
You may want to consider configuring your app with separate tsconfig.json
to solve
clashing types with jest.
You will need to exclude cypress.config.ts
, cypress
, node_modules
in your
root tsconfig.json
file.
{
"exclude": ["cypress.config.ts", "cypress", "node_modules"]
}
History​
Version | Changes |
---|---|
13.0.0 | Raised minimum required TypeScript version from 3.4+ to 4.0+ |
10.0.0 | Update guide to cover TypeScript setup for component testing |
5.0.0 | Raised minimum required TypeScript version from 2.9+ to 3.4+ |
4.4.0 | Added support for TypeScript without needing your own transpilation through preprocessors. |