From 6b50fc24863ea7f27a49cf0274857876e57a8809 Mon Sep 17 00:00:00 2001 From: criitch Date: Thu, 28 Nov 2019 13:12:41 +0300 Subject: [PATCH 1/5] fix(vue-app): sanitize path with trailing slash in `getLocation` (#6744) --- packages/vue-app/template/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index 82ede520a672..9ce130535dc4 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -260,7 +260,7 @@ export function promisify (fn, context) { // Imported from vue-router export function getLocation (base, mode) { - let path = decodeURI(window.location.pathname) + let path = decodeURI(window.location.pathname + '/').replace('//', '/') // to get matched with sanitized router.base if (mode === 'hash') { return window.location.hash.replace(/^#\//, '') } From ae1dc37e7d29a5b439a1b3260cb10c0f4f03d5c1 Mon Sep 17 00:00:00 2001 From: criitch Date: Thu, 28 Nov 2019 15:41:45 +0300 Subject: [PATCH 2/5] fix(vue-app): fix sanitizing path (because double slash is valid for pathname) in `getLocation` (#6744) --- packages/vue-app/template/utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index 9ce130535dc4..10026869f1b8 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -260,7 +260,11 @@ export function promisify (fn, context) { // Imported from vue-router export function getLocation (base, mode) { - let path = decodeURI(window.location.pathname + '/').replace('//', '/') // to get matched with sanitized router.base + let path = decodeURI(window.location.pathname) + // To get matched with sanitized router.base + if (!/\/$/.test(path)) { + path += '/' + } if (mode === 'hash') { return window.location.hash.replace(/^#\//, '') } From 0ee2b656d653585440277d94c7fc6a25af904a8c Mon Sep 17 00:00:00 2001 From: criitch Date: Fri, 29 Nov 2019 14:05:15 +0300 Subject: [PATCH 3/5] fix(vue-app): endsWith method instead of regexp test for sanitizing path in `getLocation` (#6744) --- packages/vue-app/template/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index 10026869f1b8..0aee36f54d01 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -262,7 +262,7 @@ export function promisify (fn, context) { export function getLocation (base, mode) { let path = decodeURI(window.location.pathname) // To get matched with sanitized router.base - if (!/\/$/.test(path)) { + if (!path.endsWith('/')) { path += '/' } if (mode === 'hash') { From 91b7a9913ceb244b9359cf1cff60deeb1bcbfaf2 Mon Sep 17 00:00:00 2001 From: criitch Date: Sat, 30 Nov 2019 17:50:00 +0300 Subject: [PATCH 4/5] fix(vue-app): don't mutate path when sanitizing in `getLocation` (#6744) --- packages/vue-app/template/utils.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index 0aee36f54d01..2ed9112f7ef7 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -261,14 +261,11 @@ export function promisify (fn, context) { // Imported from vue-router export function getLocation (base, mode) { let path = decodeURI(window.location.pathname) - // To get matched with sanitized router.base - if (!path.endsWith('/')) { - path += '/' - } if (mode === 'hash') { return window.location.hash.replace(/^#\//, '') } - if (base && path.indexOf(base) === 0) { + // To get matched with sanitized router.base add trailing slash + if (base && (path.endsWith('/') ? path : path + '/').startsWith(base)) { path = path.slice(base.length) } return (path || '/') + window.location.search + window.location.hash From 7922557e444cf5e7632c8c41bb7d3f10c5454c56 Mon Sep 17 00:00:00 2001 From: Konstantin Barabanov Date: Tue, 30 Jun 2020 19:19:51 +0300 Subject: [PATCH 5/5] fix(vue-app): e2e tests for router.base in spa mode (#6744) --- test/e2e/spa-base.browser.test.js | 71 +++++++++++++++++++++++++++ test/fixtures/spa-base/nuxt.config.js | 13 +++++ test/fixtures/spa/spa.test.js | 1 + 3 files changed, 85 insertions(+) create mode 100644 test/e2e/spa-base.browser.test.js create mode 100644 test/fixtures/spa-base/nuxt.config.js diff --git a/test/e2e/spa-base.browser.test.js b/test/e2e/spa-base.browser.test.js new file mode 100644 index 000000000000..8eac938d7460 --- /dev/null +++ b/test/e2e/spa-base.browser.test.js @@ -0,0 +1,71 @@ +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('spa router base browser', () => { + beforeAll(async () => { + const config = await loadFixture('spa-base') + 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 /app (router base)', async () => { + page = await browser.page(url('/app')) + + expect(await page.evaluate(() => location.href)).toBe(url('/app')) + + expect(await page.html()).not.toContain('This page could not be found') + + expect(await page.evaluate(() => { + const headings = document.evaluate("//div[text()='Hello SPA!']", document, null, XPathResult.ANY_TYPE, null) + return headings.iterateNext() + })).not.toBe(null) + }) + + test('Open /app/ (router base with trailing slash)', async () => { + page = await browser.page(url('/app/')) + + expect(await page.evaluate(() => location.href)).toBe(url('/app/')) + + expect(await page.html()).not.toContain('This page could not be found') + }) + + test('Open /app/mounted', async () => { + page = await browser.page(url('/app/mounted')) + + expect(await page.$text('h1')).toMatch('Test: updated') + }) + + test('/app/unknown', async () => { + page = await browser.page(url('/app/unknown')) + + expect(await page.evaluate(() => location.href)).toBe(url('/app/unknown')) + + expect(await page.html()).toContain('This page could not be found') + }) + + // 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() + }) +}) diff --git a/test/fixtures/spa-base/nuxt.config.js b/test/fixtures/spa-base/nuxt.config.js new file mode 100644 index 000000000000..e8b342045fb6 --- /dev/null +++ b/test/fixtures/spa-base/nuxt.config.js @@ -0,0 +1,13 @@ +import { resolve } from 'path' +import defaultsDeep from 'lodash/defaultsDeep' +import baseNuxtConfig from '../spa/nuxt.config' + +const config = { + buildDir: resolve(__dirname, '.nuxt'), + srcDir: resolve(__dirname, '..', 'spa'), + router: { + base: '/app' + } +} + +export default defaultsDeep(config, baseNuxtConfig) diff --git a/test/fixtures/spa/spa.test.js b/test/fixtures/spa/spa.test.js index 199675fcbca1..f1a696f9f5ab 100644 --- a/test/fixtures/spa/spa.test.js +++ b/test/fixtures/spa/spa.test.js @@ -4,3 +4,4 @@ import { buildFixture } from '../../utils/build' // That's why building both from same test file. buildFixture('spa') buildFixture('spa-hash') +buildFixture('spa-base')