Skip to content

Commit

Permalink
update and expand the custom command recipe (#540)
Browse files Browse the repository at this point in the history
  • Loading branch information
bahmutov authored Aug 4, 2020
1 parent 07a0d93 commit 1d82032
Show file tree
Hide file tree
Showing 18 changed files with 190 additions and 28 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Recipe | Description
[Environment variables](./examples/server-communication__env-variables) | Passing environment variables to tests
[Dynamic tests](./examples/fundamentals__dynamic-tests) | Create tests dynamically from data
[Fixtures](./examples/fundamentals__fixtures) | Loading single or multiple fixtures
[Adding Custom Commands](./examples/fundamentals__add-custom-command) | Write your own custom commands with correct types
[Adding Custom Commands](./examples/fundamentals__add-custom-command) | Write your own custom commands using JavaScript with correct types for IntelliSense to work
[Adding Custom Commands (TS)](./examples/fundamentals__add-custom-command-ts) | Write your own custom commands using TypeScript
[Adding Chai Assertions](./examples/extending-cypress__chai-assertions) | Add new or custom chai assertions with correct types
[Cypress module API](./examples/fundamentals__module-api) | Run Cypress via its module API
[Wrapping Cypress module API](./examples/fundamentals__module-api-wrap) | Writing a wrapper around "cypress run" command line parsing
Expand Down
6 changes: 6 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ jobs:
<<: *defaults
fundamentals__add-custom-command:
<<: *defaults
fundamentals__add-custom-command-ts:
<<: *defaults
logging-in__csrf-tokens:
<<: *defaults
logging-in__html-web-forms:
Expand Down Expand Up @@ -384,6 +386,9 @@ all_jobs: &all_jobs
- fundamentals__add-custom-command:
requires:
- build
- fundamentals__add-custom-command-ts:
requires:
- build
- logging-in__csrf-tokens:
requires:
- build
Expand Down Expand Up @@ -542,6 +547,7 @@ all_jobs: &all_jobs
- fundamentals__module-api
- fundamentals__module-api-wrap
- fundamentals__add-custom-command
- fundamentals__add-custom-command-ts
- logging-in__csrf-tokens
- logging-in__html-web-forms
- logging-in__single-sign-on
Expand Down
13 changes: 13 additions & 0 deletions examples/fundamentals__add-custom-command-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# add custom command in TypeScript
> Write your own Cypress commands using TypeScript
In this recipe we do not use `tsconfig.json` file, thus we need to specify additional command types using `/// <reference ...>` commands, like this:

```js
// cypress/integration/spec.ts
/// <reference path="../support/index.d.ts" />
```

![IntelliSense for custom command cy.dataCy](images/data-cy.png)

See [cypress/support/index.ts](cypress/support/index.ts) and [cypress/support/index.d.ts](cypress/support/index.d.ts).
6 changes: 6 additions & 0 deletions examples/fundamentals__add-custom-command-ts/cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"viewportHeight": 200,
"viewportWidth": 300,
"pluginsFile": false,
"fixturesFolder": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// by loading custom commands from the support file,
// we also load the global Cypress type definition
/// <reference path="../support/index.d.ts" />

Cypress.Commands.add('asyncAdd', (a, b) => {
cy.log(`${a} + ${b}`)
// our application in "index.html" has placed a promise-returning
// method "asyncAdd" onto the "window" object.
// from the tests's custom command we can invoke that method
// Cypress automatically waits for the promises to resolve
// before yielding their value to the next command in the test
// https://on.cypress.io/invoke
cy.window().invoke('asyncAdd', a, b)
})

describe('example', () => {
it('adds numbers using custom command', () => {
cy.visit('index.html')
// the custom command will yield resolved value
cy.asyncAdd(2, 3).should('equal', 5)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// load the type definition for new command we are adding "cy.dataCy"
// note: this definition also will load regular Cypress definition
// because index.d.ts references global "cypress" types
/// <reference path="../support/index.d.ts" />
// after that the custom commands like "cy.dataCy"
// should be recognized

describe('finds', () => {
it('element using data-cy custom command', () => {
cy.visit('index.html')
// use custom command we have defined above
cy.dataCy('greeting').should('be.visible')
})

it('element using h1', () => {
cy.visit('index.html')
// sanity check that h1 element has "data-cy" attribute
// with expected value
cy.get('h1')
.should('be.visible')
.and('have.attr', 'data-cy', 'greeting')
})

it('dynamically added element', () => {
cy.visit('index.html')
// another custom command, this one comes from external module
// load https://github.com/NoriSte/cypress-wait-until
cy.waitUntil(() => {
return cy
.window()
.then((win) => Boolean(win.document.querySelector('[data-cy=dynamic]')))
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// describe custom Cypress commands in this file

// load the global Cypress types
/// <reference types="cypress" />
// load the 3rd party command definition for cy.waitUntil()
/// <reference types="cypress-wait-until" />

declare namespace Cypress {
interface Chainable {
/**
* Custom command to select DOM element by data-cy attribute.
* @example cy.dataCy('greeting')
*/
dataCy(value: string): Chainable<Element>

/**
* Custom command that adds two given numbers
*/
asyncAdd(a: number, b: number): Chainable<number>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// load the global Cypress types
/// <reference types="cypress" />
// load the 3rd party command definition for cy.waitUntil()
/// <reference types="cypress-wait-until" />

// load https://github.com/NoriSte/cypress-wait-until
// which adds "cy.waitUntil" command
// note that this 3rd party module includes TypeScript "types"
// file that correctly adds "waitUntil" to the Cypress Chainer namespace
require('cypress-wait-until')

/**
* Adds custom command "cy.dataCy" to the global "cy" object
*
* @example cy.dataCy('greeting')
*/
Cypress.Commands.add('dataCy', (value) => cy.get(`[data-cy=${value}]`))

// the types for cy.dataCy will be defined in "index.d.ts" file
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions examples/fundamentals__add-custom-command-ts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<body>
<main>
<h1 data-cy="greeting">Page title</h1>
<p>This is a simple page with a <pre>data-cy="greeting"</pre> element.</p>
</main>
<script>
// adds another data-cy element dynamically
setTimeout(() => {
let el = document.createElement('div')
el.setAttribute('data-cy', 'dynamic')
el.innerText = 'This element was added dynamically'
document.body.appendChild(el)
}, 1000)
</script>

<script>
// method is added to the "window" object dynamically
setTimeout(() => {
window.asyncAdd = (a, b) => {
return new Promise((resolve) => {
setTimeout(() => resolve(a + b), 1000)
})
}
}, 1000)
</script>
</body>
11 changes: 11 additions & 0 deletions examples/fundamentals__add-custom-command-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "add-custom-command-ts",
"version": "1.0.0",
"description": "Write your own Cypress commands in TypeScript",
"scripts": {
"cypress:open": "../../node_modules/.bin/cypress open",
"cypress:run": "../../node_modules/.bin/cypress run",
"test:ci": "../../node_modules/.bin/cypress run",
"test:ci:record": "../../node_modules/.bin/cypress run --record"
}
}
15 changes: 14 additions & 1 deletion examples/fundamentals__add-custom-command/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,25 @@ And the TypeScript and IntelliSense should be happy

## 3rd party modules

As an example this spec includes 3rd party module [cypress-wait-until](https://github.com/NoriSte/cypress-wait-until). This module ships with its own TypeScript definition, which allows `cy.waitUntil` to work.
As an example this spec includes 3rd party module [cypress-wait-until](https://github.com/NoriSte/cypress-wait-until). This module ships with its own TypeScript definition, which allows `cy.waitUntil` to work. Load the types from the `support/index.d.ts` file

```js
// load the global Cypress types
/// <reference types="cypress" />
// load the 3rd party command definition for cy.waitUntil()
/// <reference types="cypress-wait-until" />
```

![IntelliSense for cy.waitUntil command](images/wait-until.png)

**Tip:** use JSDoc comments to document your commands.

## Async commands

A custom command can call an async function from the application, the resolved value will be automatically yielded to the next command or assertion in the test. See [cypress/integration/async-command.js](cypress/integration/async-command.js) file.

![Async add custom command](images/async-add.png)

## More info

- [Cypress custom commands](https://on.cypress.io/custom-commands)
5 changes: 3 additions & 2 deletions examples/fundamentals__add-custom-command/cypress.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"viewportHeight": 200,
"viewportWidth": 300,
"pluginsFile": false
}
"pluginsFile": false,
"fixturesFolder": false
}
22 changes: 0 additions & 22 deletions examples/fundamentals__add-custom-command/cypress/README.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/// <reference types="cypress" />
// by loading custom commands from the support file,
// we also load the global Cypress type definition
/// <reference path="../support/index.d.ts" />

Cypress.Commands.add('asyncAdd', (a, b) => {
cy.log(`${a} + ${b}`)
// our application in "index.html" has placed a promise-returning
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// load the global Cypress types
/// <reference types="cypress" />
// load the 3rd party command definition for cy.waitUntil()
/// <reference types="cypress-wait-until" />

// typically custom commands are added in this support folder
// so it makes sense to put their TypeScript definitions here
Expand All @@ -12,7 +15,12 @@ declare namespace Cypress {
/**
* Custom command to select DOM element by data-cy attribute.
* @example cy.dataCy('greeting')
*/
*/
dataCy(value: string): Chainable<Element>

/**
* Custom command that adds two given numbers
*/
asyncAdd(a: number, b: number): Chainable<number>
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1d82032

Please sign in to comment.