Reputation: 298
I am using Nuxt.js as frontend framework and Laravel as api server, and writing some e2e testings with Cypress. I'm trying to reduce asyncData api call by using cy.intercept but failed to sucessfully intercept the api call, my test spec looks like below:
const siteUrl = Cypress.env('site_url')
const apiUrl = Cypress.env('api_url')
describe('Post Test', () => {
beforeEach(() => {
cy.intercept('GET', `${apiUrl}/post`, {
fixture: 'post.json',
})
})
it('should render posts same as mock data', () => {
cy.visit(`/post`)
cy.contains('some posts from mock data')
})
})
and my posts/index.vue look like this:
<template>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.description }}</p>
</div>
</template>
<script>
export default {
async asyncData({ params, $http }) {
const post = await $http.$get(`${apiUrl}/post`)
return { post }
}
}
</script>
When I run the test, in asyncData hook Nuxt.js will still send actual request to Laravel asking for post data. I've read this issue but still want to ask is there any other ways to intercept api calls from server-side with Cypress?
Upvotes: 7
Views: 5340
Reputation: 23553
When you run a Nuxt app with server-side rendering, the asyncData()
call is made on the server.
Data received is added to a "hydration" function at the foot of the page then served to the Cypress browser. So the cy.intercept()
never catches the call.
One way to handle it is to mock the server during testing, which can be done in a task
/cypress/plugins/index.js
let server; // static reference to the mock server
// so we can close and re-assign on 2nd call
module.exports = (on, config) => {
on('task', {
mockServer({ interceptUrl, fixture }) {
const fs = require('fs')
const http = require('http')
const { URL } = require('url')
if (server) server.close(); // close any previous instance
const url = new URL(interceptUrl)
server = http.createServer((req, res) => {
if (req.url === url.pathname) {
const data = fs.readFileSync(`./cypress/fixtures/${fixture}`)
res.end(data)
} else {
res.end()
}
})
server.listen(url.port)
console.log(`listening at port ${url.port}`)
return null
},
})
}
Test
const apiUrl = Cypress.env('api_url'); // e.g "http://localhost:9000"
cy.task('mockServer', { interceptUrl: `${apiUrl}/post`, fixture: 'post.json' })
cy.visit('/post')
// a different fixture
cy.task('mockServer', { interceptUrl: `${apiUrl}/post`, fixture: 'post2.json' })
cy.visit('/post')
cypress.json
{
"baseUrl": "http://localhost:3000",
"env": {
"api_url": "http://localhost:9000"
}
}
Note
apiUrl
Another approach
See Control Next.js Server-Side Data During Cypress Tests.
The idea is to intercept the page as it arrives from the server and modify it's hydration function.
You leave the production API server running for the test so that SSR fetches as normal.
function interceptHydration( interceptUrl, fixture, key ) {
cy.fixture(fixture).then(mockData => {
cy.intercept(
interceptUrl,
(req) => {
req.continue(res => {
// look for "key" in page body, replace with fixture
const regex = new RegExp(`${key}:\s*{([^}]*)}`)
const mock = `${key}: ${JSON.stringify(mockData)}`
res.body = res.body.replace(regex, mock)
})
}
)
})
}
it('changes hydration data', () => {
interceptHydration( '/post', 'post', 'post' )
cy.visit('/post')
cy.get('h1').contains('post #2') // value from fixture
})
Upvotes: 8