mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 14:15:13 +00:00
fix(vue-app): prevent looping on error during render of error page (#6217)
This commit is contained in:
parent
7c90310166
commit
93a0924754
@ -34,6 +34,15 @@ export default {
|
||||
default: 'default'
|
||||
}
|
||||
},
|
||||
errorCaptured (error) {
|
||||
// if we receive and error while showing the NuxtError component
|
||||
// capture the error and force an immediate update so we re-render
|
||||
// without the NuxtError component
|
||||
if (this.displayingNuxtError) {
|
||||
this.errorFromNuxtError = error
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
routerViewKey() {
|
||||
// If nuxtChildKey prop is given or current route has children
|
||||
@ -65,18 +74,36 @@ export default {
|
||||
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
|
||||
},
|
||||
render(h) {
|
||||
// If there is some error
|
||||
if (this.nuxt.err) {
|
||||
return h('NuxtError', {
|
||||
props: {
|
||||
error: this.nuxt.err
|
||||
}
|
||||
// if there is no error
|
||||
if (!this.nuxt.err) {
|
||||
// Directly return nuxt child
|
||||
return h('NuxtChild', {
|
||||
key: this.routerViewKey,
|
||||
props: this.$props
|
||||
})
|
||||
}
|
||||
// Directly return nuxt child
|
||||
return h('NuxtChild', {
|
||||
key: this.routerViewKey,
|
||||
props: this.$props
|
||||
|
||||
// if an error occured within NuxtError show a simple
|
||||
// error message instead to prevent looping
|
||||
if (this.errorFromNuxtError) {
|
||||
this.$nextTick(() => (this.errorFromNuxtError = false))
|
||||
|
||||
return h('div', {}, [
|
||||
h('h2', 'An error occured while showing the error page'),
|
||||
h('p', 'Unfortunately an error occured and while showing the error page another error occured'),
|
||||
h('p', `Error details: ${this.errorFromNuxtError.toString()}`),
|
||||
h('nuxt-link', { props: { to: '/' } }, 'Go back to home')
|
||||
])
|
||||
}
|
||||
|
||||
// track if we are showing the NuxtError component
|
||||
this.displayingNuxtError = true
|
||||
this.$nextTick(() => (this.displayingNuxtError = false))
|
||||
|
||||
return h(NuxtError, {
|
||||
props: {
|
||||
error: this.nuxt.err
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
60
test/e2e/error.test.js
Normal file
60
test/e2e/error.test.js
Normal file
@ -0,0 +1,60 @@
|
||||
import Browser from '../utils/browser'
|
||||
import { loadFixture, getPort, Nuxt } from '../utils'
|
||||
|
||||
let port
|
||||
const browser = new Browser()
|
||||
const url = route => 'http://localhost:' + port + route
|
||||
|
||||
let nuxt = null
|
||||
let page = null
|
||||
|
||||
describe('basic browser', () => {
|
||||
beforeAll(async () => {
|
||||
const config = await loadFixture('error')
|
||||
nuxt = new Nuxt(config)
|
||||
await nuxt.ready()
|
||||
|
||||
port = await getPort()
|
||||
await nuxt.server.listen(port, 'localhost')
|
||||
|
||||
await browser.start({
|
||||
// slowMo: 50,
|
||||
// headless: false
|
||||
})
|
||||
})
|
||||
|
||||
test('Open /', async () => {
|
||||
page = await browser.page(url('/'))
|
||||
|
||||
expect(await page.$text('h1')).toBe('Error Loop incoming page')
|
||||
})
|
||||
|
||||
test('/squared doesnt loop due to error on error page', async () => {
|
||||
await page.nuxt.navigate('/squared')
|
||||
|
||||
expect(await page.$text('h2')).toBe('An error occured while showing the error page')
|
||||
})
|
||||
|
||||
test('/about loads normally', async () => {
|
||||
await page.nuxt.navigate('/about')
|
||||
|
||||
expect(await page.$text('h1')).toBe('About')
|
||||
})
|
||||
|
||||
test('/info prints empty page', async () => {
|
||||
await page.nuxt.navigate('/info')
|
||||
|
||||
expect(await page.$text('#__layout')).toBe('')
|
||||
})
|
||||
|
||||
// Close server and ask nuxt to stop listening to file changes
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
})
|
||||
|
||||
// Stop browser
|
||||
afterAll(async () => {
|
||||
await page.close()
|
||||
await browser.close()
|
||||
})
|
||||
})
|
17
test/fixtures/error/layouts/error.vue
vendored
Normal file
17
test/fixtures/error/layouts/error.vue
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Error</h1>
|
||||
<p>Details: {{ this.$route.path === '/squared' ? error.response.data : error.message }}</p>
|
||||
|
||||
<nuxt-link to="/">
|
||||
back
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['error']
|
||||
}
|
||||
</script>
|
2
test/fixtures/error/nuxt.config.js
vendored
2
test/fixtures/error/nuxt.config.js
vendored
@ -1,2 +0,0 @@
|
||||
export default {
|
||||
}
|
8
test/fixtures/error/pages/about.vue
vendored
Normal file
8
test/fixtures/error/pages/about.vue
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>About</h1>
|
||||
<nuxt-link to="/">
|
||||
back
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
10
test/fixtures/error/pages/error.vue
vendored
Normal file
10
test/fixtures/error/pages/error.vue
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<h1>Error page</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-undef: 0 */
|
||||
export default {
|
||||
not_defined
|
||||
}
|
||||
</script>
|
20
test/fixtures/error/pages/index.vue
vendored
20
test/fixtures/error/pages/index.vue
vendored
@ -1,10 +1,14 @@
|
||||
<template>
|
||||
<h1>Error page</h1>
|
||||
<div>
|
||||
<h1>Error Loop incoming page</h1>
|
||||
<nuxt-link id="squared" to="/squared">
|
||||
Error during error
|
||||
</nuxt-link>
|
||||
<nuxt-link id="about" to="/about">
|
||||
About
|
||||
</nuxt-link>
|
||||
<nuxt-link id="info" to="/info">
|
||||
Info (with error)
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-undef: 0 */
|
||||
export default {
|
||||
not_defined
|
||||
}
|
||||
</script>
|
||||
|
19
test/fixtures/error/pages/info.vue
vendored
Normal file
19
test/fixtures/error/pages/info.vue
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Info</h1>
|
||||
<p>Details: {{ info.message.title }}</p>
|
||||
<nuxt-link to="/">
|
||||
back
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
info: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -19,7 +19,7 @@ describe('error', () => {
|
||||
})
|
||||
|
||||
test('/ should display an error', async () => {
|
||||
await expect(nuxt.server.renderRoute('/')).rejects.toMatchObject({
|
||||
await expect(nuxt.server.renderRoute('/error')).rejects.toMatchObject({
|
||||
message: expect.stringContaining('not_defined is not defined')
|
||||
})
|
||||
})
|
||||
@ -30,14 +30,14 @@ describe('error', () => {
|
||||
})
|
||||
|
||||
test('/ with renderAndGetWindow()', async () => {
|
||||
await expect(nuxt.server.renderAndGetWindow(url('/'))).rejects.toMatchObject({
|
||||
await expect(nuxt.server.renderAndGetWindow(url('/error'))).rejects.toMatchObject({
|
||||
statusCode: 500
|
||||
})
|
||||
})
|
||||
|
||||
test('Error: resolvePath()', () => {
|
||||
expect(() => nuxt.resolver.resolvePath()).toThrowError()
|
||||
expect(() => nuxt.resolver.resolvePath('@/pages/about.vue')).toThrowError('Cannot resolve "@/pages/about.vue"')
|
||||
expect(() => nuxt.resolver.resolvePath('@/pages/not-found.vue')).toThrowError('Cannot resolve "@/pages/not-found.vue"')
|
||||
})
|
||||
|
||||
test('Error: callHook()', async () => {
|
||||
@ -56,6 +56,24 @@ describe('error', () => {
|
||||
expect(consola.fatal).toHaveBeenCalledWith(error)
|
||||
})
|
||||
|
||||
test('/info should display an error', async () => {
|
||||
await expect(nuxt.server.renderRoute('/info')).rejects.toMatchObject({
|
||||
message: expect.stringContaining(`Cannot read property 'title' of undefined`)
|
||||
})
|
||||
})
|
||||
|
||||
test('/about should work', async () => {
|
||||
await expect(nuxt.server.renderRoute('/about')).resolves.toMatchObject({
|
||||
html: expect.stringContaining('About')
|
||||
})
|
||||
})
|
||||
|
||||
test('/error-square should display an error', async () => {
|
||||
await expect(nuxt.server.renderRoute('/squared')).rejects.toMatchObject({
|
||||
message: expect.stringContaining(`Cannot read property 'data' of undefined`)
|
||||
})
|
||||
})
|
||||
|
||||
// Close server and ask nuxt to stop listening to file changes
|
||||
afterAll(async () => {
|
||||
await nuxt.close()
|
||||
|
Loading…
Reference in New Issue
Block a user