Skip to content

Commit

Permalink
fix(history): allow hash history with no origin
Browse files Browse the repository at this point in the history
Close #163
  • Loading branch information
posva committed Apr 13, 2020
1 parent d7c71b5 commit 760d216
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 30 deletions.
81 changes: 81 additions & 0 deletions __tests__/history/hash.spec.ts
@@ -0,0 +1,81 @@
import { JSDOM } from 'jsdom'
import createWebHashHistory from '../../src/history/hash'
import createWebHistory from '../../src/history/html5'
import { createDom } from '../utils'

jest.mock('../../src/history/html5')

describe('History Hash', () => {
let dom: JSDOM
beforeAll(() => {
dom = createDom()
})

beforeEach(() => {
;(createWebHistory as jest.Mock).mockClear()
})

afterAll(() => {
dom.window.close()
})

afterEach(() => {
// ensure no base element is left after a test as only the first is
// respected
for (let element of Array.from(document.getElementsByTagName('base')))
element.remove()
})

describe('url', () => {
beforeEach(() => {
dom.reconfigure({ url: 'https://example.com' })
})

it('should use a correct', () => {
createWebHashHistory()
expect(createWebHistory).toHaveBeenCalledWith('/#')
})

it('should be able to provide a base', () => {
createWebHashHistory('/folder/')
expect(createWebHistory).toHaveBeenCalledWith('/folder/#')
})

it('should be able to provide a base with no trailing slash', () => {
createWebHashHistory('/folder')
expect(createWebHistory).toHaveBeenCalledWith('/folder/#')
})

it('should read the base tag', () => {
const baseEl = document.createElement('base')
baseEl.href = '/foo/'
document.head.appendChild(baseEl)
createWebHashHistory()
expect(createWebHistory).toHaveBeenCalledWith('/foo/#')
})

it('should use the base option over the base tag', () => {
const baseEl = document.createElement('base')
baseEl.href = '/foo/'
document.head.appendChild(baseEl)
createWebHashHistory('/bar/')
expect(createWebHistory).toHaveBeenCalledWith('/bar/#')
})
})

describe('file://', () => {
beforeEach(() => {
dom.reconfigure({ url: 'file:///usr/some-file.html' })
})

it('should use a correct base', () => {
createWebHashHistory()
// both, a trailing / and none work
expect(createWebHistory).toHaveBeenCalledWith(
expect.stringMatching(/^#\/?$/)
)
})

it.todo('warns if we provide a base with file://')
})
})
10 changes: 10 additions & 0 deletions __tests__/history/html5.spec.ts
Expand Up @@ -68,4 +68,14 @@ describe('History HTMl5', () => {
expect(createWebHistory('/foo/').base).toBe('/foo')
expect(createWebHistory('/foo').base).toBe('/foo')
})

it('handles a single hash base', () => {
expect(createWebHistory('#').base).toBe('#')
expect(createWebHistory('#/').base).toBe('#')
})

it('handles a non-empty hash base', () => {
expect(createWebHistory('#/bar').base).toBe('#/bar')
expect(createWebHistory('#/bar/').base).toBe('#/bar')
})
})
29 changes: 29 additions & 0 deletions src/history/common.ts
Expand Up @@ -145,3 +145,32 @@ export function normalizeHistoryLocation(
fullPath: (location as HistoryLocation).fullPath || (location as string),
}
}

/**
* Normalizes a base by removing any trailing slash and reading the base tag if
* present.
*
* @param base base to normalize
*/
export function normalizeBase(base?: string): string {
if (!base) {
if (__BROWSER__) {
// respect <base> tag
const baseEl = document.querySelector('base')
base = (baseEl && baseEl.getAttribute('href')) || '/'
// strip full URL origin
base = base.replace(/^\w+:\/\/[^\/]+/, '')
} else {
base = '/'
}
}

// ensure leading slash when it was removed by the regex above avoid leading
// slash with hash because the file could be read from the disk like file://
// and the leading slash would cause problems
if (base.charAt(0) !== '/' && base.charAt(0) !== '#') base = '/' + base

// remove the trailing slash so all other method can just do `base + fullPath`
// to build an href
return base.replace(/\/$/, '')
}
6 changes: 3 additions & 3 deletions src/history/hash.ts
@@ -1,7 +1,7 @@
import { RouterHistory } from './common'
import { RouterHistory, normalizeBase } from './common'
import createWebHistory from './html5'

export default function createWebHashHistory(base: string = ''): RouterHistory {
export default function createWebHashHistory(base?: string): RouterHistory {
// Make sure this implementation is fine in terms of encoding, specially for IE11
return createWebHistory(base + '/#')
return createWebHistory(location.host ? normalizeBase(base) + '/#' : '#')
}
28 changes: 1 addition & 27 deletions src/history/html5.ts
Expand Up @@ -8,6 +8,7 @@ import {
HistoryState,
RawHistoryLocation,
ValueContainer,
normalizeBase,
} from './common'
import { computeScrollPosition, ScrollToPosition } from '../utils/scroll'
import { warn } from 'vue'
Expand Down Expand Up @@ -262,33 +263,6 @@ function useHistoryStateNavigation(base: string) {
}
}

/**
* Normalizes a base by removing any trailing slash and reading the base tag if
* present.
*
* @param base base to normalize
*/
function normalizeBase(base?: string): string {
if (!base) {
if (__BROWSER__) {
// respect <base> tag
const baseEl = document.querySelector('base')
base = (baseEl && baseEl.getAttribute('href')) || '/'
// strip full URL origin
base = base.replace(/^\w+:\/\/[^\/]+/, '')
} else {
base = '/'
}
}

// ensure leading slash when it was removed by the regex above
if (base.charAt(0) !== '/') base = '/' + base

// remove the trailing slash so all other method can just do `base + fullPath`
// to build an href
return base.replace(/\/$/, '')
}

export default function createWebHistory(base?: string): RouterHistory {
base = normalizeBase(base)

Expand Down

0 comments on commit 760d216

Please sign in to comment.