How To Type Function Mocha Context With Cypress Aliases

Specify custom types for extra Mocha context properties created by Cypress aliases.

In Cypress you can save values under aliases which is pretty handy. You can get the aliased value using the cy.get, or by using a cy.then(function () { ... }) callback, or (my favorite solution) using the alias in the next hook or test callback via this.[alias] property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
beforeEach(() => {
cy.wrap(42).as('answer')
// later in the same code
cy.get('@answer')
.should('equal', 42)

// alternative 1: cy.then(callback)
cy.get(...)
.then(function () {
// we can access the aliased value
expect(this.answer, 'answer').to.equal(42)
})
})

// alternative 2
// by the time the test runs, the alias property is set
it('works', function () {
expect(this.answer, 'test answer').to.equal(42)
})

Using the function () { ... } rather than () => { ... } is very important; we need the this reference to point at the Mocha context object where Cypress aliases are stored.

All is good, except types; our linter / code editor does not know that this test context is expected to have a new custom property answer, what do we do?

Unknown property "answer" on the test context object

If we look at the type definition for the it(title, ...) function, we can find the Mocha types bundled with Cypress

The type of the "it" test function

Open the type definition for the test functions

The type for the callback function Func is simply

1
2
3
4
/**
* Callback function used for tests and hooks.
*/
type Func = (this: Context, done: Done) => void;

The first argument is this: Context and that is what we need to extend with our custom property answer. Unfortunately, it is not exposed, so we cannot simply merge interfaces (like we do with custom Cypress commands), so we need to overwrite the type Func signature.

cypress/e2e/misc/context-types.cy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
declare namespace Mocha {
interface MyContext {
/**
* The answer to life, the universe, and everything.
* @example console.log(this.answer) // 42
*/
answer: number
}

/**
* Callback function used for tests and hooks.
*/
type MyFunc = (this: MyContext) => void

interface TestFunction {
(title: string, fn?: MyFunc): Mocha.Test
}
}

describe('Mocha context types', () => {
beforeEach(() => {
cy.wrap(42).as('answer')
})

it('accesses aliased object via this', function () {
// access the aliased object via "this"
// and assert its property "answer" is 42
expect(this.answer, 'answer').to.equal(42)
})
})

By putting the Mocha.MyContext interface in the spec file we restrict its effect on the current spec. Now it should work

Cypress test callback with typed custom alias property

Strong typing for the win.