diff --git a/packages/next-server/lib/router/router.ts b/packages/next-server/lib/router/router.ts index 3d8595bc1c886..f6bb0b11620c1 100644 --- a/packages/next-server/lib/router/router.ts +++ b/packages/next-server/lib/router/router.ts @@ -587,7 +587,9 @@ export default class Router implements BaseRouter { abortComponentLoad(as: string): void { if (this.clc) { - Router.events.emit('routeChangeError', new Error('Route Cancelled'), as) + const e = new Error('Route Cancelled') + ;(e as any).cancelled = true + Router.events.emit('routeChangeError', e, as) this.clc() this.clc = null } diff --git a/test/integration/route-load-cancel/pages/index.js b/test/integration/route-load-cancel/pages/index.js new file mode 100644 index 0000000000000..89a653639162d --- /dev/null +++ b/test/integration/route-load-cancel/pages/index.js @@ -0,0 +1,26 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import React, { useEffect } from 'react' + +export default () => { + const router = useRouter() + useEffect(() => { + router.events.on('routeChangeError', err => { + if (err.cancelled) { + window.routeCancelled = 'yes' + } + }) + + // Intentionally is not cleaned up + }, []) + return ( + <> + + Page 1 + {' '} + + Page 2 + + > + ) +} diff --git a/test/integration/route-load-cancel/pages/page1.js b/test/integration/route-load-cancel/pages/page1.js new file mode 100644 index 0000000000000..f471ffa2d06b7 --- /dev/null +++ b/test/integration/route-load-cancel/pages/page1.js @@ -0,0 +1,10 @@ +function Page1 () { + return
1
+} + +Page1.getInitialProps = async function getInitialProps () { + await new Promise(resolve => setTimeout(resolve, 5000)) + return {} +} + +export default Page1 diff --git a/test/integration/route-load-cancel/pages/page2.js b/test/integration/route-load-cancel/pages/page2.js new file mode 100644 index 0000000000000..c33db7319bdf0 --- /dev/null +++ b/test/integration/route-load-cancel/pages/page2.js @@ -0,0 +1,5 @@ +function Page2 () { + return2
+} + +export default Page2 diff --git a/test/integration/route-load-cancel/test/index.test.js b/test/integration/route-load-cancel/test/index.test.js new file mode 100644 index 0000000000000..6e2449920f673 --- /dev/null +++ b/test/integration/route-load-cancel/test/index.test.js @@ -0,0 +1,67 @@ +/* eslint-env jest */ +/* global jasmine */ +import webdriver from 'next-webdriver' +import { join } from 'path' +import { + findPort, + launchApp, + killApp, + waitFor, + runNextCommand, + nextServer, + startApp, + stopApp +} from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5 + +let app +let appPort +let server +const appDir = join(__dirname, '../') + +function runTests () { + it('should cancel slow page loads on re-navigation', async () => { + const browser = await webdriver(appPort, '/') + await waitFor(5000) + + await browser.elementByCss('#link-1').click() + await waitFor(1000) + await browser.elementByCss('#link-2').click() + await waitFor(1000) + + const text = await browser.elementByCss('#page-text').text() + expect(text).toMatch(/2/) + expect(await browser.eval('window.routeCancelled')).toBe('yes') + }) +} + +describe('next/dynamic', () => { + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests(true) + }) + + describe('production mode', () => { + beforeAll(async () => { + await runNextCommand(['build', appDir]) + + app = nextServer({ + dir: appDir, + dev: false, + quiet: true + }) + + server = await startApp(app) + appPort = server.address().port + }) + afterAll(() => stopApp(server)) + + runTests() + }) +})