I have a website that sends queries to the server using ‘Apollo graphql client’.
I have set up an interface that will be displayed when this website cannot connect to the server, i.e. when it gets an error ‘Error: connect ECONNREFUSED’.
I can test this manually by shutting down the server.
I want to test with Cypress if this is working correctly.
I want to use cy.intercept() to make it look like the server is not online.
How can I do this?
I read https://docs.cypress.io/api/commands/intercept, I thought I would find a concrete example but I couldn’t.
1 Answer
You could take a look at this thread Network automation to emulate offline mode
The gist is using two RDP calls to simulate the network going offline. As a user you would do this via the devtools. RDP gives you a programatic equivalent.
Cypress.automation('remote:debugger:protocol', {
command: 'Network.enable',
})
Cypress.automation('remote:debugger:protocol', {
command: 'Network.emulateNetworkConditions',
params: {
'offline': true,
'latency': 0,
'downloadThroughput': 0,
'uploadThroughput': 0,
'connectionType': 'none',
},
})
The updated example spec for Cypress 12 is here
/// <reference types="cypress" />
/* global window */
// use window.navigator.onLine property to determine
// if the browser is offline or online
// https://caniuse.com/online-status
const assertOnline = () => {
return cy.wrap(window).its('navigator.onLine').should('be.true')
}
const assertOffline = () => {
return cy.wrap(window).its('navigator.onLine').should('be.false')
}
const goOffline = () => {
cy.log('**go offline**')
.then(() => {
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.enable',
})
})
.then(() => {
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.emulateNetworkConditions',
params: {
offline: true,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
})
})
}
const goOnline = () => {
// disable offline mode, otherwise we will break our tests :)
cy.log('**go online**')
.then(() => {
// https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-emulateNetworkConditions
return Cypress.automation('remote:debugger:protocol',
{
command: 'Network.emulateNetworkConditions',
params: {
offline: false,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
})
})
}
// since we are using Chrome debugger protocol API
// we should only run these tests when NOT in Firefox browser
// see https://on.cypress.io/configuration#Test-Configuration
describe('offline mode', { browser: '!firefox' }, () => {
// the application is making request to this url
const url = 'https://jsonplaceholder.cypress.io/users'
// make sure we get back online, even if a test fails
// otherwise the Cypress can lose the browser connection
beforeEach(goOnline)
afterEach(goOnline)
it('shows network status', () => {
cy.visit('/')
cy.contains('#network-status', 'online')
.wait(1000) // for demo purpose
goOffline()
cy.contains('#network-status', 'offline')
.wait(1000) // for demo purpose
})
it('shows error if we stub the network call', () => {
assertOnline()
cy.visit('/')
cy.intercept(`${url}*`, { forceNetworkError: true }).as('users')
cy.get('#load-users').click()
cy.contains('#users', 'Problem fetching users Failed to fetch')
// cannot wait for the intercept that forces network error
// https://github.com/cypress-io/cypress/issues/9062
// cy.wait('@users', { timeout: 1000 }) // the network call happens
})
it('shows error trying to fetch users in offline mode', () => {
cy.visit('/')
assertOnline()
// since this call returns a promise, must tell Cypress to wait
// for it to be resolved
goOffline()
assertOffline()
cy.get('#load-users').click()
cy.contains('#users', 'Problem fetching users Failed to fetch')
// now let's go back online and fetch the users
goOnline()
assertOnline()
cy.get('#load-users').click()
cy.get('.user').should('have.length', 3)
})
it('makes fetch request when offline', () => {
cy.visit('/')
goOffline()
assertOffline()
// let's spy on the "fetch" method the app calls
cy.window().then((w) => cy.spy(w, 'fetch').withArgs(`${url}?_limit=3`).as('fetchUsers'))
cy.get('#load-users').click()
cy.get('@fetchUsers').should('have.been.calledOnce')
// now let's go back online and fetch the users
goOnline()
assertOnline()
cy.get('#load-users').click()
cy.get('.user').should('have.length', 3)
cy.get('@fetchUsers').should('have.been.calledTwice')
})
it('does not reach the outside network when offline', () => {
cy.visit('/')
// before we go offline we have to set up network intercepts
// since they need to be communicated outside the browser
// and lets keep track the number of network calls made
let callCount = 0
cy.intercept(`${url}*`, () => {
callCount += 1
}).as('users')
goOffline()
assertOffline()
cy.get('#load-users').click()
cy.contains('#users', 'Problem fetching users Failed to fetch')
// the cy.intercept network call does NOT happen
// because the browser does not fire it
// and thus our network proxy does not see it
cy.then(() => {
expect(callCount, 'no network calls made').to.equal(0)
})
// now let's go back online and fetch the users
goOnline()
assertOnline()
cy.get('#load-users').click()
// we can retry the assertion to know when the network call has happened
// using .should callback function with an assertion inside
.should(() => {
expect(callCount, 'single network call').to.equal(1)
})
cy.wait('@users')
.its('response.body')
.should('have.length', 3)
cy.get('.user').should('have.length', 3)
})
})
2
-
NOTE: This test has known issues related to bug in Chrome where "Offline Mode" does not impact websockets. This execution is not recommended and will be refactored to use cy.intercept() at a later date.
– user1839898111 mins ago
-
Lucky you are not using websockets!
– Debella6 mins ago