Skip to content

Commit

Permalink
feat: add option to fail on 5xx errors (#76)
Browse files Browse the repository at this point in the history
### fail on 5xx

By default, the network calls might fail and the test happily continues. You can make the idle spy fail if any of the matching network calls return 4xx or 5xx errors. These classes of error status code have their own flag to enable.

```js
// fail the test if any of the matching calls
// returns a 5xx status code
cy.waitForNetworkIdlePrepare({
  method: '*',
  alias: 'all',
  pattern: '**',
  failOn5xx: true,
})
```
  • Loading branch information
bahmutov committed Feb 1, 2023
1 parent c051a80 commit 9b639a9
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 2 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ cy.waitForNetworkIdle('@graphql', 1000)
// the page has finished additional processing
```

### fail on 5xx

By default, the network calls might fail and the test happily continues. You can make the idle spy fail if any of the matching network calls return 4xx or 5xx errors. These classes of error status code have their own flag to enable.

```js
// fail the test if any of the matching calls
// returns a 5xx status code
cy.waitForNetworkIdlePrepare({
method: '*',
alias: 'all',
pattern: '**',
failOn5xx: true,
})
```

![The test fails when one of the calls receives 500 from the server](./images/5xx.png)

## Multiple registrations

If you try to register the same intercept method, pattern, and alias multiple times, only a single first registration will be made.
Expand Down
32 changes: 32 additions & 0 deletions cypress/e2e/fail-on-500.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// <reference path="../../src/index.d.ts" />
// @ts-check

import '../..'

// fails the test on 500 response code, but not on 401
it('fails the test when a request receives 500 status code', () => {
cy.on('fail', (e) => {
if (e.message.includes('failed with 500')) {
console.log('expected error')
return false
} else {
throw e
}
})

// fail the test if any of the matching calls
// returns a 5xx status code
cy.waitForNetworkIdlePrepare({
method: '*',
alias: 'all',
pattern: '**',
failOn5xx: true,
})

cy.visit('/fail-500')
.wait(3000)
.then(() => {
// the network should have caught an 5xx error
throw new Error('Should never get here')
})
})
Binary file added images/5xx.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions server/fail-500.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<body>
<p>
This page makes a request that receives 500. It also makes another request
that receives 401.
</p>
<script>
setTimeout(() => {
fetch('/status-401')
}, 300)

setTimeout(() => {
fetch('/status-500')
}, 800)
</script>
</body>
9 changes: 9 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ const afterMs = (req, res, { params }) => {
}, params.ms)
}

const send = (statusCode) => (req, res) => {
setTimeout(() => {
micro.send(res, statusCode, `Server says ${statusCode}`)
}, 500)
}

const html = sendHtml('index.html')
module.exports = dispatch()
.dispatch('/', 'GET', html)
Expand All @@ -53,5 +59,8 @@ module.exports = dispatch()
.dispatch('/four-seconds', 'GET', sendHtml('four-seconds.html'))
.dispatch('/busy-page', 'GET', sendHtml('busy-page.html'))
.dispatch('/get-vs-post', 'GET', sendHtml('get-vs-post.html'))
.dispatch('/status-401', 'GET', send(401))
.dispatch('/status-500', 'GET', send(500))
.dispatch('/fail-500', 'GET', sendHtml('fail-500.html'))
.dispatch('/add-user', 'POST', addUser)
.otherwise(html)
9 changes: 9 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ declare namespace Cypress {
options?: Partial<WaitForNetworkIdleOptions>,
): Chainable<WaitForNetworkIdleResult>

/**
* Starts spying on the matching network calls
* @see https://github.com/bahmutov/cypress-network-idle#readme
*/
waitForNetworkIdlePrepare(
options: WaitForNetworkIdlePrepareOptions,
): Chainable<WaitForNetworkIdleResult>
Expand All @@ -62,5 +66,10 @@ declare namespace Cypress {
pattern: string
alias: string
log?: boolean
/**
* Fail the test if any of the matching network calls
* returns 5xx status code
*/
failOn5xx?: boolean
}
}
19 changes: 17 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,13 @@ function waitForNetworkIdle(...args) {
})
}

function waitForNetworkIdlePrepare({ method, pattern, alias, log } = {}) {
function waitForNetworkIdlePrepare({
method,
pattern,
alias,
log,
failOn5xx,
} = {}) {
if (!alias) {
throw new Error('cypress-network-idle: alias is required')
}
Expand All @@ -189,7 +195,11 @@ function waitForNetworkIdlePrepare({ method, pattern, alias, log } = {}) {
log = true
}

Cypress.log({ name: 'network-idle', message: `prepared for **@${alias}**` })
let message = `prepared for **@${alias}**`
if (failOn5xx) {
message += ' (will fail on **5xx**)'
}
Cypress.log({ name: 'network-idle', message })

const key = `networkIdleCounters_${alias}`
// check if the intercept has already been set
Expand Down Expand Up @@ -227,6 +237,11 @@ function waitForNetworkIdlePrepare({ method, pattern, alias, log } = {}) {
counters.lastNetworkAt = +new Date()
// console.log('res %s %s', req.method, req.url, counters.lastNetworkAt)
// console.log(res.body)
if (failOn5xx && res.statusCode >= 500) {
throw new Error(
`Network call ${req.method} ${req.url} failed with ${res.statusCode}`,
)
}
})
}).as(alias)
}
Expand Down

0 comments on commit 9b639a9

Please sign in to comment.