From 6f302e45632d7ce58965f9b6b59ee589b029830c Mon Sep 17 00:00:00 2001 From: chorobin Date: Tue, 2 Apr 2024 20:06:10 +0200 Subject: [PATCH 1/2] tests: fix type tests for Link, make sure type tests fail in ci and add type tests for union `to` and difference in params --- packages/react-router/src/link.tsx | 15 +- packages/react-router/tests/fileRoute.test.ts | 9 +- packages/react-router/tests/link.test-d.tsx | 216 +++++++++++++++++- packages/react-router/tests/route.test.ts | 2 +- packages/react-router/tsconfig.json | 2 +- 5 files changed, 224 insertions(+), 20 deletions(-) diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index a00874fb15d..e24a694d2fc 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -76,11 +76,9 @@ export type Last> = T extends [...infer _, infer L] ? L : never -export type RemoveTrailingSlashes = T extends '/' - ? T - : T extends `${infer R}/` - ? RemoveTrailingSlashes - : T +export type RemoveTrailingSlashes = T extends `${infer R}/` + ? RemoveTrailingSlashes + : T export type RemoveLeadingSlashes = T extends `/${infer R}` ? RemoveLeadingSlashes @@ -217,10 +215,11 @@ export type ResolveRoute< ? TFrom : ResolveRelativePath >, -> = - RouteByPath extends never +> = TPath extends string + ? RouteByPath extends never ? RouteByPath - : RouteByPath + : RouteByPath + : never type PostProcessParams< T, diff --git a/packages/react-router/tests/fileRoute.test.ts b/packages/react-router/tests/fileRoute.test.ts index ab758918486..f00a76836c0 100644 --- a/packages/react-router/tests/fileRoute.test.ts +++ b/packages/react-router/tests/fileRoute.test.ts @@ -5,6 +5,7 @@ import { createFileRoute, createLazyRoute, createLazyFileRoute, + LazyRoute, } from '../src' describe('createFileRoute has the same hooks as getRouteApi', () => { @@ -16,7 +17,7 @@ describe('createFileRoute has the same hooks as getRouteApi', () => { it.each(hookNames.map((name) => [name]))( 'should have the "%s" hook defined', (hookName) => { - expect(route[hookName]).toBeDefined() + expect(hookName as keyof LazyRoute).toBeDefined() }, ) }) @@ -30,20 +31,20 @@ describe('createLazyFileRoute has the same hooks as getRouteApi', () => { it.each(hookNames.map((name) => [name]))( 'should have the "%s" hook defined', (hookName) => { - expect(route[hookName]).toBeDefined() + expect(route[hookName as keyof LazyRoute]).toBeDefined() }, ) }) describe('createLazyRoute has the same hooks as getRouteApi', () => { const routeApi = getRouteApi('foo') - const hookNames = Object.keys(routeApi).filter((key) => key.startsWith('use')) const route = createLazyRoute({})({}) + const hookNames = Object.keys(routeApi).filter((key) => key.startsWith('use')) it.each(hookNames.map((name) => [name]))( 'should have the "%s" hook defined', (hookName) => { - expect(route[hookName]).toBeDefined() + expect(route[hookName as keyof LazyRoute]).toBeDefined() }, ) }) diff --git a/packages/react-router/tests/link.test-d.tsx b/packages/react-router/tests/link.test-d.tsx index 4f00de4b6f4..f4e35f3cb98 100644 --- a/packages/react-router/tests/link.test-d.tsx +++ b/packages/react-router/tests/link.test-d.tsx @@ -1,4 +1,4 @@ -import { test, expectTypeOf } from 'vitest' +import { expectTypeOf, test } from 'vitest' import { Link, createRoute } from '../src' import { createRootRoute } from '../src' @@ -40,9 +40,17 @@ const invoiceRoute = createRoute({ validateSearch: () => ({ page: 0 }), }) +const invoiceEditRoute = createRoute({ + getParentRoute: () => invoiceRoute, + path: 'edit', +}) + const routeTree = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), - invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]), + invoicesRoute.addChildren([ + invoicesIndexRoute, + invoiceRoute.addChildren([invoiceEditRoute]), + ]), indexRoute, ]) @@ -60,6 +68,7 @@ test('when navigating to the root, to autocompletes to all routes, ../ and ./', | '/invoices' | '/invoices/' | '/invoices/$invoiceId' + | '/invoices/$invoiceId/edit' | '../' | './' | undefined @@ -78,6 +87,7 @@ test('when navigating from a static route to the root, to autocompletes to all r | '/invoices' | '/invoices/' | '/invoices/$invoiceId' + | '/invoices/$invoiceId/edit' | '../' | './' | undefined @@ -100,6 +110,7 @@ test('when navigating from a static route to the parent route, to autocompletes | '../posts/' | '../posts/$postId' | '../invoices/$invoiceId' + | '../invoices/$invoiceId/edit' | '../invoices' | '../invoices/' | '../' @@ -121,6 +132,7 @@ test('from autocompletes to all absolute routes', () => { | '/invoices' | '/invoices/' | '/invoices/$invoiceId' + | '/invoices/$invoiceId/edit' | undefined >() }) @@ -176,7 +188,7 @@ test('when navigating to a route with params, params can be a object of required const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') - params.exclude().toMatchTypeOf<{ postId: string }>() + params.exclude().toEqualTypeOf<{ postId: string }>() }) test('when navigating to a route with params, params is a function from all params to next params', () => { @@ -202,7 +214,9 @@ test('when navigating from a route with no params to a route with params, params const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') - params.exclude().toMatchTypeOf<{ invoiceId: string }>() + params + .exclude() + .branded.toEqualTypeOf<{ invoiceId: string }>() }) test('when navigating from a route with no params to a route with params, params is a function from no params to next params', () => { @@ -216,6 +230,116 @@ test('when navigating from a route with no params to a route with params, params params.parameter(0).toEqualTypeOf<{}>() }) +test('when navigating from a route to a route with the same params, the params can be an object with optional properties', () => { + const TestLink = Link + const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') + + params + .exclude() + .branded.toEqualTypeOf<{ invoiceId?: string | undefined } | undefined>() +}) + +test('when navigating from a route to a route with the same params, params is a function from params to optional next params', () => { + const TestLink = Link + const params = expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('params') + .extract() + + params.returns.branded.toEqualTypeOf<{ invoiceId?: string | undefined }>() + params.parameter(0).toEqualTypeOf<{ invoiceId: string }>() +}) + +test('when navigating to a union of routes, params is optional', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() +}) + +test('when navigating to a union of routes, params can be a union of objects', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('params') + .exclude() + .toEqualTypeOf<{ invoiceId: string } | { postId: string } | undefined>() +}) + +test('when navigating to a union of routes, params is a function from all params to a union of params', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + + const params = expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('params') + .extract() + + params.returns.branded.toEqualTypeOf< + { invoiceId: string } | { postId: string } + >() + + params + .parameter(0) + .toEqualTypeOf<{} | { invoiceId: string } | { postId: string }>() +}) + +test('when navigating to a union of routes including the route, params is optional', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() +}) + +test('when navigating to a union of routes including the root, params can be a union of objects', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('params') + .exclude() + .toEqualTypeOf< + { invoiceId: string } | { postId: string } | {} | undefined + >() +}) + +test('when navigating to a union of routes including the root, params is a function from all params to a union of params', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + + const params = expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('params') + .extract() + + params.returns.branded.toEqualTypeOf< + { invoiceId: string } | { postId: string } | {} + >() + + params + .parameter(0) + .toEqualTypeOf<{} | { invoiceId: string } | { postId: string }>() +}) + test('when navigating to the same route search is optional', () => { const TestLink = Link expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() @@ -267,7 +391,7 @@ test('when navigating to a route with search params, search params can be a obje const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') - params.exclude().toMatchTypeOf<{ page: number }>() + params.exclude().toEqualTypeOf<{ page: number }>() }) test('when navigating to a route with search params, search params is a function from all params to current params', () => { @@ -291,7 +415,7 @@ test('when navigating from a route with no search params to a route with search const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') - params.exclude().toMatchTypeOf<{ page: number }>() + params.exclude().toEqualTypeOf<{ page: number }>() }) test('when navigating to a route with search params, search params is a function from all search params to current search params', () => { @@ -304,3 +428,83 @@ test('when navigating to a route with search params, search params is a function params.returns.branded.toEqualTypeOf<{ page: number }>() params.parameter(0).toEqualTypeOf<{}>() }) + +test('when navigating to a union of routes, search params is optional', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() +}) + +test('when navigating to a union of routes, search params can be a union of objects', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ page: number } | {} | undefined>() +}) + +test('when navigating to a union of routes, search params is a function from all params to a union of params', () => { + const TestLink = Link< + RouteTree, + string, + '/invoices/$invoiceId/' | '/posts/$postId/' + > + + const params = expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .extract() + + params.returns.branded.toEqualTypeOf<{ page: number } | {}>() + + params.parameter(0).toEqualTypeOf<{} | { page: number }>() +}) + +test('when navigating to a union of routes including the route, search params is optional', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() +}) + +test('when navigating to a union of routes including the root, search params can be a union of objects', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ page: number } | {} | undefined>() +}) + +test('when navigating to a union of routes including the root, params is a function from all params to a union of params', () => { + const TestLink = Link< + RouteTree, + string, + '/' | '/invoices/$invoiceId/' | '/posts/$postId/' + > + + const params = expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .extract() + + params.returns.toEqualTypeOf<{ page: number } | {}>() + + params.parameter(0).toEqualTypeOf<{} | { page: number }>() +}) diff --git a/packages/react-router/tests/route.test.ts b/packages/react-router/tests/route.test.ts index abc2a0f686a..a001f7bfa7d 100644 --- a/packages/react-router/tests/route.test.ts +++ b/packages/react-router/tests/route.test.ts @@ -47,7 +47,7 @@ describe('createRoute has the same hooks as getRouteApi', () => { it.each(hookNames.map((name) => [name]))( 'should have the "%s" hook defined', (hookName) => { - expect(route[hookName]).toBeDefined() + expect(route[hookName as keyof typeof route]).toBeDefined() }, ) }) diff --git a/packages/react-router/tsconfig.json b/packages/react-router/tsconfig.json index 0fba1a6e6dd..01ad9218979 100644 --- a/packages/react-router/tsconfig.json +++ b/packages/react-router/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "jsx": "react", }, - "include": ["src", "vite.config.ts"], + "include": ["src", "tests", "vite.config.ts"], } From e58250fadc3058c261dda83f250ef9b41d01e398 Mon Sep 17 00:00:00 2001 From: chorobin Date: Thu, 4 Apr 2024 14:20:07 +0200 Subject: [PATCH 2/2] tests: refactor tests to only include scenario and group expects There was a lot of repetition in the tests, I think its simpler to group the tests by use cases and have multiple expects for the use case --- packages/react-router/tests/link.test-d.tsx | 277 +++++--------------- 1 file changed, 61 insertions(+), 216 deletions(-) diff --git a/packages/react-router/tests/link.test-d.tsx b/packages/react-router/tests/link.test-d.tsx index f4e35f3cb98..ce9e99ed835 100644 --- a/packages/react-router/tests/link.test-d.tsx +++ b/packages/react-router/tests/link.test-d.tsx @@ -56,7 +56,7 @@ const routeTree = rootRoute.addChildren([ type RouteTree = typeof routeTree -test('when navigating to the root, to autocompletes to all routes, ../ and ./', () => { +test('when navigating to the root', () => { expectTypeOf(Link) .parameter(0) .toHaveProperty('to') @@ -75,7 +75,7 @@ test('when navigating to the root, to autocompletes to all routes, ../ and ./', >() }) -test('when navigating from a static route to the root, to autocompletes to all routes', () => { +test('when navigating from a route with no params and no search to the root', () => { expectTypeOf(Link) .parameter(0) .toHaveProperty('to') @@ -94,14 +94,14 @@ test('when navigating from a static route to the root, to autocompletes to all r >() }) -test('when navigating from a static route to the current route, to autocompletes to relative routes', () => { +test('when navigating from a route with no params and no search to the current route', () => { expectTypeOf(Link) .parameter(0) .toHaveProperty('to') .toEqualTypeOf<'./$postId' | undefined | './'>() }) -test('when navigating from a static route to the parent route, to autocompletes to relative routes', () => { +test('when navigating from a route with no params and no search to the parent route', () => { expectTypeOf(Link) .parameter(0) .toHaveProperty('to') @@ -137,41 +137,55 @@ test('from autocompletes to all absolute routes', () => { >() }) -test('when navigating to the same route params is optional', () => { +test('when navigating to the same route', () => { const TestLink = Link + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() -}) -test('when naviating to the same route params can be true', () => { - const TestLink = Link expectTypeOf(TestLink) .parameter(0) .toHaveProperty('params') .extract() .toEqualTypeOf() -}) -test('when navigating to the parent route params is optional', () => { - const TestLink = Link - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .extract() + .toEqualTypeOf() + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .extract() + .toEqualTypeOf() }) -test('when navigating to the parent route params can be true', () => { +test('when navigating to the parent route', () => { const TestLink = Link + + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() expectTypeOf(TestLink) .parameter(0) .toHaveProperty('params') .extract() .toEqualTypeOf() -}) -test('when navigating from a route with params to the same route, params is optional', () => { - const TestLink = Link - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() + + expectTypeOf(TestLink) + .parameter(0) + .toHaveProperty('search') + .extract() + .toEqualTypeOf() }) -test('when navigating from a route with params to the same route, params can be true', () => { +test('when navigating from a route with params to the same route', () => { const TestLink = Link + + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() expectTypeOf(TestLink) .parameter(0) .toHaveProperty('params') @@ -179,24 +193,14 @@ test('when navigating from a route with params to the same route, params can be .toEqualTypeOf() }) -test('when navigating to a route with params, params is required', () => { +test('when navigating to a route with params', () => { const TestLink = Link + expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ params: unknown }>() -}) -test('when navigating to a route with params, params can be a object of required params', () => { - const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') params.exclude().toEqualTypeOf<{ postId: string }>() -}) - -test('when navigating to a route with params, params is a function from all params to next params', () => { - const TestLink = Link - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') - .extract() params.returns.toEqualTypeOf<{ postId: string }>() params @@ -204,86 +208,48 @@ test('when navigating to a route with params, params is a function from all para .toEqualTypeOf<{} | { invoiceId: string } | { postId: string }>() }) -test('when navigating from a route with no params to a route with params, params are required', () => { +test('when navigating from a route with no params to a route with params', () => { const TestLink = Link expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ params: unknown }>() -}) -test('when navigating from a route with no params to a route with params, params can be an object of required params', () => { - const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') params .exclude() .branded.toEqualTypeOf<{ invoiceId: string }>() -}) - -test('when navigating from a route with no params to a route with params, params is a function from no params to next params', () => { - const TestLink = Link - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') - .extract() params.returns.branded.toEqualTypeOf<{ invoiceId: string }>() params.parameter(0).toEqualTypeOf<{}>() }) -test('when navigating from a route to a route with the same params, the params can be an object with optional properties', () => { +test('when navigating from a route to a route with the same params', () => { const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() + params .exclude() .branded.toEqualTypeOf<{ invoiceId?: string | undefined } | undefined>() -}) - -test('when navigating from a route to a route with the same params, params is a function from params to optional next params', () => { - const TestLink = Link - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') - .extract() params.returns.branded.toEqualTypeOf<{ invoiceId?: string | undefined }>() params.parameter(0).toEqualTypeOf<{ invoiceId: string }>() }) -test('when navigating to a union of routes, params is optional', () => { +test('when navigating to a union of routes with params', () => { const TestLink = Link< RouteTree, string, '/invoices/$invoiceId/' | '/posts/$postId/' > - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() -}) + const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') -test('when navigating to a union of routes, params can be a union of objects', () => { - const TestLink = Link< - RouteTree, - string, - '/invoices/$invoiceId/' | '/posts/$postId/' - > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') + params .exclude() .toEqualTypeOf<{ invoiceId: string } | { postId: string } | undefined>() -}) - -test('when navigating to a union of routes, params is a function from all params to a union of params', () => { - const TestLink = Link< - RouteTree, - string, - '/invoices/$invoiceId/' | '/posts/$postId/' - > - - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') - .extract() params.returns.branded.toEqualTypeOf< { invoiceId: string } | { postId: string } @@ -294,42 +260,21 @@ test('when navigating to a union of routes, params is a function from all params .toEqualTypeOf<{} | { invoiceId: string } | { postId: string }>() }) -test('when navigating to a union of routes including the route, params is optional', () => { +test('when navigating to a union of routes including the root', () => { const TestLink = Link< RouteTree, string, '/' | '/invoices/$invoiceId/' | '/posts/$postId/' > - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() -}) + const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('params') -test('when navigating to a union of routes including the root, params can be a union of objects', () => { - const TestLink = Link< - RouteTree, - string, - '/' | '/invoices/$invoiceId/' | '/posts/$postId/' - > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ params: unknown }>() - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') + params .exclude() .toEqualTypeOf< { invoiceId: string } | { postId: string } | {} | undefined >() -}) - -test('when navigating to a union of routes including the root, params is a function from all params to a union of params', () => { - const TestLink = Link< - RouteTree, - string, - '/' | '/invoices/$invoiceId/' | '/posts/$postId/' - > - - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('params') - .extract() params.returns.branded.toEqualTypeOf< { invoiceId: string } | { postId: string } | {} @@ -340,41 +285,10 @@ test('when navigating to a union of routes including the root, params is a funct .toEqualTypeOf<{} | { invoiceId: string } | { postId: string }>() }) -test('when navigating to the same route search is optional', () => { - const TestLink = Link - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() -}) - -test('when navigating to the same search params can be true', () => { - const TestLink = Link - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() - .toEqualTypeOf() -}) - -test('when navigating to the parent route search params is optional', () => { - const TestLink = Link - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() -}) - -test('when navigating to the parent route search params can be true', () => { - const TestLink = Link - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() - .toEqualTypeOf() -}) +test('when navigating from a route with search params to the same route', () => { + const TestLink = Link -test('when navigating from a route with search params to the same route, search params is required', () => { - const TestLink = Link expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ search: unknown }>() -}) - -test('when navigating from a route with search params to the same route, search params can be true', () => { - const TestLink = Link expectTypeOf(TestLink) .parameter(0) .toHaveProperty('search') @@ -382,129 +296,60 @@ test('when navigating from a route with search params to the same route, search .toEqualTypeOf() }) -test('when navigating to a route with search params, search params is required', () => { - const TestLink = Link - expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ search: unknown }>() -}) - -test('when navigating to a route with search params, search params can be a object of required params', () => { +test('when navigating to a route with search params', () => { const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') - params.exclude().toEqualTypeOf<{ page: number }>() -}) - -test('when navigating to a route with search params, search params is a function from all params to current params', () => { - const TestLink = Link - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() + expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ search: unknown }>() + params.exclude().toEqualTypeOf<{ page: number }>() params.returns.toEqualTypeOf<{ page: number }>() params.parameter(0).toEqualTypeOf<{} | { page: number }>() }) -test('when navigating from a route with no search params to a route with search params, search params are required', () => { - const TestLink = Link - - expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ search: unknown }>() -}) - -test('when navigating from a route with no search params to a route with search params, search params can be an object of required params', () => { +test('when navigating from a route with no search params to a route with search params', () => { const TestLink = Link const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') + expectTypeOf(TestLink).parameter(0).toMatchTypeOf<{ search: unknown }>() params.exclude().toEqualTypeOf<{ page: number }>() -}) - -test('when navigating to a route with search params, search params is a function from all search params to current search params', () => { - const TestLink = Link - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() - params.returns.branded.toEqualTypeOf<{ page: number }>() params.parameter(0).toEqualTypeOf<{}>() }) -test('when navigating to a union of routes, search params is optional', () => { +test('when navigating to a union of routes with search params', () => { const TestLink = Link< RouteTree, string, '/invoices/$invoiceId/' | '/posts/$postId/' > - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() -}) + const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') -test('when navigating to a union of routes, search params can be a union of objects', () => { - const TestLink = Link< - RouteTree, - string, - '/invoices/$invoiceId/' | '/posts/$postId/' - > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') + params .exclude() .toEqualTypeOf<{ page: number } | {} | undefined>() -}) - -test('when navigating to a union of routes, search params is a function from all params to a union of params', () => { - const TestLink = Link< - RouteTree, - string, - '/invoices/$invoiceId/' | '/posts/$postId/' - > - - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() params.returns.branded.toEqualTypeOf<{ page: number } | {}>() params.parameter(0).toEqualTypeOf<{} | { page: number }>() }) -test('when navigating to a union of routes including the route, search params is optional', () => { +test('when navigating to a union of routes with search params including the root', () => { const TestLink = Link< RouteTree, string, '/' | '/invoices/$invoiceId/' | '/posts/$postId/' > - expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() -}) + const params = expectTypeOf(TestLink).parameter(0).toHaveProperty('search') -test('when navigating to a union of routes including the root, search params can be a union of objects', () => { - const TestLink = Link< - RouteTree, - string, - '/' | '/invoices/$invoiceId/' | '/posts/$postId/' - > + expectTypeOf(TestLink).parameter(0).not.toMatchTypeOf<{ search: unknown }>() - expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') + params .exclude() .toEqualTypeOf<{ page: number } | {} | undefined>() -}) - -test('when navigating to a union of routes including the root, params is a function from all params to a union of params', () => { - const TestLink = Link< - RouteTree, - string, - '/' | '/invoices/$invoiceId/' | '/posts/$postId/' - > - - const params = expectTypeOf(TestLink) - .parameter(0) - .toHaveProperty('search') - .extract() params.returns.toEqualTypeOf<{ page: number } | {}>() - params.parameter(0).toEqualTypeOf<{} | { page: number }>() })