]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(hash): allow characters after `#` in base e.g. `#!` (#925)
authorggwujun <dashixiong@tanzhou.cn>
Thu, 13 May 2021 08:04:36 +0000 (03:04 -0500)
committerGitHub <noreply@github.com>
Thu, 13 May 2021 08:04:36 +0000 (10:04 +0200)
Co-authored-by: dashixiong <876337269@qq.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com>
__tests__/history/html5.spec.ts
src/history/html5.ts

index 7df3b381966410c4f8a95a49f6f506af9be1e903..4eb446a3677c6ee7fe27c40822eab0d8aa2b8d17 100644 (file)
@@ -16,6 +16,11 @@ describe('History HTMl5', () => {
     dom = createDom()
   })
 
+  beforeEach(() => {
+    // empty the state to simulate an initial navigation by default
+    window.history.replaceState(null, '', '')
+  })
+
   afterAll(() => {
     dom.window.close()
   })
@@ -30,6 +35,9 @@ describe('History HTMl5', () => {
   it('handles a basic base', () => {
     expect(createWebHistory().base).toBe('')
     expect(createWebHistory('/').base).toBe('')
+    expect(createWebHistory('/#').base).toBe('/#')
+    expect(createWebHistory('#!').base).toBe('#!')
+    expect(createWebHistory('#other').base).toBe('#other')
   })
 
   it('handles a base tag', () => {
@@ -68,11 +76,15 @@ describe('History HTMl5', () => {
   it('handles a single hash base', () => {
     expect(createWebHistory('#').base).toBe('#')
     expect(createWebHistory('#/').base).toBe('#')
+    expect(createWebHistory('#!/').base).toBe('#!')
+    expect(createWebHistory('#other/').base).toBe('#other')
   })
 
   it('handles a non-empty hash base', () => {
     expect(createWebHistory('#/bar').base).toBe('#/bar')
     expect(createWebHistory('#/bar/').base).toBe('#/bar')
+    expect(createWebHistory('#!/bar/').base).toBe('#!/bar')
+    expect(createWebHistory('#other/bar/').base).toBe('#other/bar')
   })
 
   it('prepends the host to support // urls', () => {
@@ -93,29 +105,95 @@ describe('History HTMl5', () => {
     spy.mockRestore()
   })
 
-  it('calls push with hash part of the url with a base', () => {
-    dom.reconfigure({ url: 'file:///usr/etc/index.html' })
-    let history = createWebHistory('#')
-    let spy = jest.spyOn(window.history, 'pushState')
-    history.push('/foo')
-    expect(spy).toHaveBeenCalledWith(
-      expect.anything(),
-      expect.any(String),
-      '#/foo'
-    )
-    spy.mockRestore()
-  })
+  describe('specific to base containing a hash', () => {
+    it('calls push with hash part of the url with a base', () => {
+      dom.reconfigure({ url: 'file:///usr/etc/index.html' })
+      let initialSpy = jest.spyOn(window.history, 'replaceState')
+      let history = createWebHistory('#')
+      // initial navigation
+      expect(initialSpy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#/'
+      )
+      let spy = jest.spyOn(window.history, 'pushState')
+      history.push('/foo')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#/foo'
+      )
+      spy.mockRestore()
+      initialSpy.mockRestore()
+    })
 
-  it('works with something after the hash in the base', () => {
-    dom.reconfigure({ url: 'file:///usr/etc/index.html' })
-    let history = createWebHistory('#something')
-    let spy = jest.spyOn(window.history, 'pushState')
-    history.push('/foo')
-    expect(spy).toHaveBeenCalledWith(
-      expect.anything(),
-      expect.any(String),
-      '#something/foo'
-    )
-    spy.mockRestore()
+    it('works with something after the hash in the base', () => {
+      dom.reconfigure({ url: 'file:///usr/etc/index.html' })
+      let initialSpy = jest.spyOn(window.history, 'replaceState')
+      let history = createWebHistory('#something')
+      // initial navigation
+      expect(initialSpy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#something/'
+      )
+      let spy = jest.spyOn(window.history, 'pushState')
+      history.push('/foo')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#something/foo'
+      )
+      spy.mockRestore()
+      initialSpy.mockRestore()
+    })
+
+    it('works with #! and on a file with initial location', () => {
+      dom.reconfigure({ url: 'file:///usr/etc/index.html#!/foo' })
+      let spy = jest.spyOn(window.history, 'replaceState')
+      createWebHistory('#!')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#!/foo'
+      )
+      spy.mockRestore()
+    })
+
+    it('works with #other', () => {
+      dom.reconfigure({ url: 'file:///usr/etc/index.html' })
+      let spy = jest.spyOn(window.history, 'replaceState')
+      createWebHistory('#other')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#other/'
+      )
+      spy.mockRestore()
+    })
+
+    it('works with custom#other in domain', () => {
+      dom.reconfigure({ url: 'https://esm.dev/custom' })
+      let spy = jest.spyOn(window.history, 'replaceState')
+      createWebHistory('custom#other')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#other/'
+      )
+      spy.mockRestore()
+    })
+
+    it('works with #! and a host with initial location', () => {
+      dom.reconfigure({ url: 'https://esm.dev/#!/foo' })
+      let spy = jest.spyOn(window.history, 'replaceState')
+      createWebHistory('/#!')
+      expect(spy).toHaveBeenCalledWith(
+        expect.anything(),
+        expect.any(String),
+        '#!/foo'
+      )
+      spy.mockRestore()
+    })
   })
 })
index 3b4c8be38585a085499b95e62cdd2d53e337de2c..6dc3daa7ca19b644c666410a365aacdf5a8a0d7e 100644 (file)
@@ -39,11 +39,14 @@ function createCurrentLocation(
   location: Location
 ): HistoryLocation {
   const { pathname, search, hash } = location
-  // allows hash based url
+  // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
   const hashPos = base.indexOf('#')
   if (hashPos > -1) {
+    let slicePos = hash.includes(base.slice(hashPos))
+      ? base.slice(hashPos).length
+      : 1
+    let pathFromHash = hash.slice(slicePos)
     // prepend the starting slash to hash so the url starts with /#
-    let pathFromHash = hash.slice(1)
     if (pathFromHash[0] !== '/') pathFromHash = '/' + pathFromHash
     return stripBase(pathFromHash, '')
   }