Forcing Vue Rerender for Faster Tests
07 June, 2021
I'm becoming a big fan of E2E tests with Cypress, but reloading a web application between each test can be fairly slow. We can avoid a lot of overhead by skipping page reload and instead forcing the root Vue instance to rerender fresh.
Disclaimer: This can introduce some issues with stale state (like in Vuex) hanging around between tests. Also this is more of a hack than anything else, but it might be useful in some situations.
The best way to do this within the context of a Vue component would be to use a key on the top-level component, but what about in the browser at runtime? Assuming we have a Vue component with the id app
, we can do the following:
async function rerenderVue() {
const root = document.getElementById('app').__vue__.$root
const vnode = root._vnode
vnode.key = vnode.key ? vnode.key + 1 : 1
root.$forceUpdate()
await root.$nextTick()
}
You might make a Cypress command to call between tests, like this.
/** Reload app without page reload by changing the key on the root Vue instance. */
Cypress.Commands.add('reloadVue', () => {
Cypress.log({
displayName: 'reload',
message: 'forcing Vue rerender',
})
cy.get('#app', { log: false }).then(async ($app) => {
const app = $app[0]
// Changing key forces remount
const vnode = app.__vue__.$root._vnode
vnode.key = vnode.key ? vnode.key + 1 : 1
app.__vue__.$root.$forceUpdate()
await app.__vue__.$root.$nextTick()
})
})