Skip to content

Commit

Permalink
Custom Chai assertion example (#235)
Browse files Browse the repository at this point in the history
* update chai plugin recipe

* add custom assertion example

* add IntelliSense for custom assertions
  • Loading branch information
bahmutov authored Mar 18, 2019
1 parent 89c05c7 commit da0daa0
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 67 deletions.
58 changes: 56 additions & 2 deletions examples/extending-cypress__chai-assertions/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
# Adding Chai Assertions

- Extend [`chai`](http://chaijs.com/) with the [`chai-date-string`](http://chaijs.com/plugins/chai-date-string/) assertion plugin, see [cypress/support/assertions.js](cypress/support/assertions.js)
- Extend [`chai`](http://chaijs.com/) with the [`chai-colors`](http://chaijs.com/plugins/chai-colors/) assertion plugin, see [spec file](cypress/integration/extending-chai-assertion-plugins-spec.js)
- Extend [`chai`](http://chaijs.com/) with the [`chai-date-string`](http://chaijs.com/plugins/chai-date-string/) assertion plugin from [cypress/support/index.js](cypress/support/index.js) so it is available in every spec file.
- Extend [`chai`](http://chaijs.com/) with the [`chai-colors`](http://chaijs.com/plugins/chai-colors/) assertion plugin in a single [spec file](cypress/integration/extending-chai-assertion-plugins-spec.js)
- Globally extend [`chai`](http://chaijs.com/) for all specs.
- Write and use own Chai assertion, see [cypress/support/index.js](cypress/support/index.js)

### Code completion

Even if you write your specs in JavaScript, you can have Intelligent Code Completion for custom assertions.

1. Add a `.d.ts` file with type definition and good JSDoc comment, for example here is [cypress/support/index.d.ts](cypress/support/index.d.ts).

```ts
// cypress/support/index.d.ts file
// extends Cypress assertion Chainer interface with
// the new assertion methods

/// <reference types="cypress" />

declare namespace Cypress {
interface Chainer<Subject> {
/**
* Custom Chai assertion that checks if given subject is string "foo"
*
* @example
```
expect('foo').to.be.foo()
cy.wrap('foo').should('be.foo')
```
* */
(chainer: 'be.foo'): Chainable<Subject>

/**
* Custom Chai assertion that checks if given subject is NOT string "foo"
*
* @example
```
expect('bar').to.not.be.foo()
cy.wrap('bar').should('not.be.foo')
```
* */
(chainer: 'not.be.foo'): Chainable<Subject>
}
}
```

2. In the spec file add special comment `reference path` pointing at the `.d.ts` file to load it.

```js
/// <reference types="Cypress" />
/// <reference path="../support/index.d.ts" />
```

In a modern code editor like VSCode you should see IntelliSense pop up when hovering over the new assertion method.

![Custom assertion code completion](images/custom-assertion-intellisense.png)

**Related:** [IntelliSense for custom Cypress commands](https://github.com/cypress-io/cypress-example-todomvc#custom-commands)
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/// <reference types="Cypress" />
/// <reference path="../support/index.d.ts" />

// we installed this node_module in package.json
// https://github.com/hurrymaplelad/chai-colors
import chaiColors from 'chai-colors'
Expand All @@ -10,8 +13,8 @@ import chaiColors from 'chai-colors'
// it would not have access to this plugin
chai.use(chaiColors)

describe('Chai Assertion Plugins', function(){
context('chai-date-string', function(){
describe('Chai Assertion Plugins', function () {
context('chai-date-string', function () {
// we installed this node_module in package.json
// https://github.com/googoid/chai-date-string
//
Expand All @@ -23,38 +26,48 @@ describe('Chai Assertion Plugins', function(){
// you'll see that we import cypress/support/assertions.js
// and because the supportFile is bundled before our spec file,
// it means we already have access to it
it('can be used in any spec file', function(){
it('can be used in any spec file', function () {
expect('2015-11-12').to.be.a.dateString()
})

it('can be wrapped by Cypress as well', function(){
it('can be wrapped by Cypress as well', function () {
cy.wrap('2016-03-14').should('be.a.dateString')
})

it('can be negated', function(){
it('can be negated', function () {
expect('2015-14-41').not.to.be.a.dateString()
})

it('can be negated using Cypress', function(){
it('can be negated using Cypress', function () {
cy.wrap('2015-14-41').should('not.be.a.dateString')
})
})

context('chai-colors', function(){
it('can convert rgba to hex', function(){
context('chai-colors', function () {
it('can convert rgba to hex', function () {
expect('rgba(0, 0, 0, 1)').to.be.colored('#000000')
})

it('can be wrapped by Cypress as well', function(){
it('can be wrapped by Cypress as well', function () {
cy.wrap('rgba(0, 0, 0, 1)').should('be.colored', '#000000')
})

it('can be negated', function(){
it('can be negated', function () {
expect('#650042').not.to.be.colored('rgba(1, 2, 3, 4)')
})

it('can be negated using Cypress', function(){
it('can be negated using Cypress', function () {
cy.wrap('#ff0000').should('not.be.colored', 'green')
})
})

context('custom assertion', () => {
it('checks if a given string is "foo" or not', () => {
expect('foo').to.be.foo()
expect('bar').to.not.be.foo()

cy.wrap('foo').should('be.foo')
cy.wrap('bar').should('not.be.foo')
})
})
})

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference types="cypress" />

declare namespace Cypress {
interface Chainer<Subject> {
/**
* Custom Chai assertion that checks if given subject is string "foo"
*
* @example
```
expect('foo').to.be.foo()
cy.wrap('foo').should('be.foo')
```
* */
(chainer: 'be.foo'): Chainable<Subject>

/**
* Custom Chai assertion that checks if given subject is NOT string "foo"
*
* @example
```
expect('bar').to.not.be.foo()
cy.wrap('bar').should('not.be.foo')
```
* */
(chainer: 'not.be.foo'): Chainable<Subject>
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// because this file is imported from cypress/support/index.js
// that means all other spec files will have this assertion plugin
// available to them because the supportFile is bundled and served
// prior to any spec files loading
import chaiDateString from 'chai-date-string'

// Import commands.js using ES2015 syntax:
import './commands'
// chai is a global exposed by Cypress which means
// we can just simply extend it
chai.use(chaiDateString)

// also import 3rd party assertions
// which will globally modify chai
// and make these accessible to all specs
import "./assertions"
/**
* Example that shows how to write a custom Chai assertion.
*
* @see https://www.chaijs.com/guide/helpers/
* @example
```
expect('foo').to.be.foo()
expect('bar').to.not.be.foo()
cy.wrap('foo').should('be.foo')
cy.wrap('bar').should('not.be.foo')
```
* */
const isFoo = (_chai, utils) => {
function assertIsFoo (options) {
this.assert(
this._obj === 'foo',
'expected #{this} to be string "foo"',
'expected #{this} to not be string "foo"',
this._obj
)
}

_chai.Assertion.addMethod('foo', assertIsFoo)
}
// registers our assertion function "isFoo" with Chai
chai.use(isFoo)
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 da0daa0

Please sign in to comment.