name: undefined,
},
},
+ repeatedParams2: {
+ string: '/p/1/2',
+ normalized: {
+ fullPath: '/p/1/2',
+ path: '/p/1/2',
+ params: { p: ['1', '2'] },
+ meta: {},
+ query: {},
+ hash: '',
+ matched: [records.home],
+ redirectedFrom: undefined,
+ name: undefined,
+ },
+ },
+ repeatedParams3: {
+ string: '/p/1/2/3',
+ normalized: {
+ fullPath: '/p/1/2/3',
+ path: '/p/1/2/3',
+ params: { p: ['1', '2', '3'] },
+ meta: {},
+ query: {},
+ hash: '',
+ matched: [records.home],
+ redirectedFrom: undefined,
+ name: undefined,
+ },
+ },
}
describe('RouterLink', () => {
expect(el.querySelector('a')!.className).toContain('router-link-active')
})
+ it('is not active with more repeated params', () => {
+ const { el } = factory(
+ locations.repeatedParams2.normalized,
+ { to: locations.repeatedParams3.string },
+ locations.repeatedParams3.normalized
+ )
+ expect(el.querySelector('a')!.className).toBe('')
+ })
+
+ it('is not active with partial repeated params', () => {
+ const { el } = factory(
+ locations.repeatedParams3.normalized,
+ { to: locations.repeatedParams2.string },
+ locations.repeatedParams2.normalized
+ )
+ expect(el.querySelector('a')!.className).toBe('')
+ })
+
it.todo('can be active as an alias')
it.todo('can be exact-active as an alias')
it.todo('is active when a child is active')
})
})
+ it('can reroute to a replaced route with the same component', async () => {
+ const { router } = await newRouter()
+ router.addRoute({
+ path: '/new/foo',
+ component: components.Foo,
+ name: 'new',
+ })
+ // navigate to the route we just added
+ await router.replace({ name: 'new' })
+ // replace it
+ router.addRoute({
+ path: '/new/bar',
+ component: components.Foo,
+ name: 'new',
+ })
+ // navigate again
+ await router.replace({ name: 'new' })
+ expect(router.currentRoute.value).toMatchObject({
+ path: '/new/bar',
+ name: 'new',
+ })
+ })
+
+ it('can reroute to child', async () => {
+ const { router } = await newRouter()
+ router.addRoute({
+ path: '/new',
+ component: components.Foo,
+ children: [],
+ name: 'new',
+ })
+ // navigate to the route we just added
+ await router.replace('/new/child')
+ // replace it
+ router.addRoute('new', {
+ path: 'child',
+ component: components.Bar,
+ name: 'new-child',
+ })
+ // navigate again
+ await router.replace('/new/child')
+ expect(router.currentRoute.value).toMatchObject({
+ name: 'new-child',
+ })
+ })
+
it('can reroute when adding a new route', async () => {
const { router } = await newRouter()
await router.push('/p/p')
>/docs/é</router-link
>
</li>
+ <li>
+ <router-link to="/rep">/rep</router-link>
+ </li>
+ <li>
+ <router-link to="/rep/a">/rep/a</router-link>
+ </li>
+ <li>
+ <router-link to="/rep/a/b">/rep/a/b</router-link>
+ </li>
</ul>
<!-- <transition
name="fade"
unref,
} from 'vue'
import { RouteLocation, RouteLocationNormalized, Immutable } from '../types'
-import { isSameLocationObject } from '../utils'
+import { isSameLocationObject, isSameRouteRecord } from '../utils'
import { routerKey } from '../injectKeys'
-import { RouteRecordNormalized } from '../matcher/types'
type VueUseOptions<T> = {
[k in keyof T]: Ref<T[k]> | T[k]
type UseLinkOptions = VueUseOptions<LinkProps>
-function isSameRouteRecord(
- a: Immutable<RouteRecordNormalized>,
- b: Immutable<RouteRecordNormalized>
-): boolean {
- // TODO: handle aliases
- return a === b
-}
-
-function includesParams(
- outter: Immutable<RouteLocationNormalized['params']>,
- inner: Immutable<RouteLocationNormalized['params']>
-): boolean {
- for (let key in inner) {
- let innerValue = inner[key]
- let outterValue = outter[key]
- if (typeof innerValue === 'string') {
- if (innerValue !== outterValue) return false
- } else {
- if (
- !Array.isArray(outterValue) ||
- innerValue.some((value, i) => value !== outterValue[i])
- )
- return false
- }
- }
-
- return true
-}
-
export function useLink(props: UseLinkOptions) {
const router = inject(routerKey)!
return true
}
+
+function includesParams(
+ outter: Immutable<RouteLocationNormalized['params']>,
+ inner: Immutable<RouteLocationNormalized['params']>
+): boolean {
+ for (let key in inner) {
+ let innerValue = inner[key]
+ let outterValue = outter[key]
+ if (typeof innerValue === 'string') {
+ if (innerValue !== outterValue) return false
+ } else {
+ if (
+ !Array.isArray(outterValue) ||
+ outterValue.length !== innerValue.length ||
+ innerValue.some((value, i) => value !== outterValue[i])
+ )
+ return false
+ }
+ }
+
+ return true
+}
guardToPromiseFn,
isSameLocationObject,
applyToParams,
+ isSameRouteRecord,
} from './utils'
import { useCallbacks } from './utils/callbacks'
import { encodeParam, decode } from './utils/encoding'
path: matchedRoute.path,
}),
hash: location.hash || '',
- query: normalizeQuery(location.query || {}),
+ query: normalizeQuery(location.query),
...matchedRoute,
redirectedFrom: undefined,
}
const force: boolean | undefined = to.force
// TODO: should we throw an error as the navigation was aborted
- // TODO: needs a proper check because order in query could be different
if (!force && isSameLocation(from, toLocation)) return from
toLocation.redirectedFrom = redirectedFrom
a.name === b.name &&
a.path === b.path &&
a.hash === b.hash &&
- isSameLocationObject(a.query, b.query)
+ isSameLocationObject(a.query, b.query) &&
+ a.matched.length === b.matched.length &&
+ a.matched.every((record, i) => isSameRouteRecord(record, b.matched[i]))
)
}
return newParams
}
+export function isSameRouteRecord(
+ a: Immutable<RouteRecordNormalized>,
+ b: Immutable<RouteRecordNormalized>
+): boolean {
+ // TODO: handle aliases
+ return a === b
+}
+
export function isSameLocationObject(
a: Immutable<RouteLocationNormalized['query']>,
b: Immutable<RouteLocationNormalized['query']>
if (typeof a !== typeof b) return false
// both a and b are arrays
if (Array.isArray(a))
- return a.every((value, i) => value === (b as LocationQueryValue[])[i])
+ return (
+ a.length === (b as any[]).length &&
+ a.every((value, i) => value === (b as LocationQueryValue[])[i])
+ )
return a === b
}
* null in arrays
* @param query
*/
-export function normalizeQuery(query: LocationQueryRaw): LocationQuery {
+export function normalizeQuery(
+ query: LocationQueryRaw | undefined
+): LocationQuery {
const normalizedQuery: LocationQuery = {}
for (let key in query) {