Skip to content

Commit

Permalink
upgrade to v2 and add another test while we're at it
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Sep 15, 2023
1 parent de3e427 commit 474fd2f
Show file tree
Hide file tree
Showing 24 changed files with 1,492 additions and 3,073 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</div>

```sh
npx create-remix@latest --typescript --install --template epicweb-dev/epic-stack
npx create-remix@latest --install --template epicweb-dev/epic-stack
```

[![The Epic Stack](https://github-production-user-asset-6210df.s3.amazonaws.com/1500684/246885449-1b00286c-aa3d-44b2-9ef2-04f694eb3592.png)](https://www.epicweb.dev/epic-stack)
Expand Down
1 change: 0 additions & 1 deletion app/components/error-boundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export function GeneralErrorBoundary({
<div className="container flex items-center justify-center p-20 text-h2">
{isRouteErrorResponse(error)
? (statusHandlers?.[error.status] ?? defaultStatusHandler)({
// @ts-expect-error, pretty sure this is a bug in Remix
error,
params,
})
Expand Down
7 changes: 5 additions & 2 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { PassThrough } from 'stream'
import { Response, type HandleDocumentRequestFunction } from '@remix-run/node'
import {
createReadableStreamFromReadable,
type HandleDocumentRequestFunction,
} from '@remix-run/node'
import { RemixServer } from '@remix-run/react'
import isbot from 'isbot'
import { getInstanceInfo } from 'litefs-js'
Expand Down Expand Up @@ -54,7 +57,7 @@ export default async function handleRequest(...args: DocRequestArgs) {
responseHeaders.set('Content-Type', 'text/html')
responseHeaders.append('Server-Timing', timings.toString())
resolve(
new Response(body, {
new Response(createReadableStreamFromReadable(body), {
headers: responseHeaders,
status: didError ? 500 : responseStatusCode,
}),
Expand Down
4 changes: 2 additions & 2 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
type DataFunctionArgs,
type HeadersFunction,
type LinksFunction,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import {
Form,
Expand Down Expand Up @@ -88,7 +88,7 @@ export const links: LinksFunction = () => {
].filter(Boolean)
}

export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
{ title: data ? 'Epic Notes' : 'Error | Epic Notes' },
{ name: 'description', content: `Your own captain's log` },
Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/forgot-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import { Link, useFetcher } from '@remix-run/react'
import { z } from 'zod'
Expand Down Expand Up @@ -108,7 +108,7 @@ function ForgotPasswordEmail({
)
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Password Recovery for Epic Notes' }]
}

Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import { Form, Link, useActionData, useSearchParams } from '@remix-run/react'
import { safeRedirect } from 'remix-utils'
Expand Down Expand Up @@ -350,7 +350,7 @@ export default function LoginPage() {
)
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Login to Epic Notes' }]
}

Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import {
Form,
Expand Down Expand Up @@ -135,7 +135,7 @@ export async function handleVerification({ submission }: VerifyFunctionArgs) {
})
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Setup Epic Notes Account' }]
}

Expand Down
7 changes: 3 additions & 4 deletions app/routes/_auth+/onboarding_.$provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import {
Form,
Expand Down Expand Up @@ -90,11 +90,10 @@ export async function loader({ request, params }: DataFunctionArgs) {

return json({
email,
formError: typeof formError === 'string' ? formError : null,
status: 'idle',
submission: {
intent: '',
payload: (prefilledProfile ?? {}) as {},
payload: (prefilledProfile ?? {}) as Record<string, unknown>,
error: {
'': typeof formError === 'string' ? [formError] : [],
},
Expand Down Expand Up @@ -177,7 +176,7 @@ export async function handleVerification({ submission }: VerifyFunctionArgs) {
})
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Setup Epic Notes Account' }]
}

Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/reset-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import { Form, useActionData, useLoaderData } from '@remix-run/react'
import { z } from 'zod'
Expand Down Expand Up @@ -98,7 +98,7 @@ export async function action({ request }: DataFunctionArgs) {
})
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Reset Password | Epic Notes' }]
}

Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
json,
redirect,
type DataFunctionArgs,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/node'
import { Form, useActionData, useSearchParams } from '@remix-run/react'
import { z } from 'zod'
Expand Down Expand Up @@ -100,7 +100,7 @@ export function SignupEmail({
)
}

export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [{ title: 'Sign Up | Epic Notes' }]
}

Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth+/verify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export async function loader({ request }: DataFunctionArgs) {
status: 'idle',
submission: {
intent: '',
payload: Object.fromEntries(params),
error: {},
payload: Object.fromEntries(params) as Record<string, unknown>,
error: {} as Record<string, Array<string>>,
},
} as const)
}
Expand Down
4 changes: 2 additions & 2 deletions app/routes/_marketing+/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type V2_MetaFunction } from '@remix-run/node'
import { type MetaFunction } from '@remix-run/node'
import {
Tooltip,
TooltipContent,
Expand All @@ -7,7 +7,7 @@ import {
} from '#app/components/ui/tooltip.tsx'
import { logos, stars } from './logos/logos.ts'

export const meta: V2_MetaFunction = () => [{ title: 'Epic Notes' }]
export const meta: MetaFunction = () => [{ title: 'Epic Notes' }]

export default function Index() {
return (
Expand Down
17 changes: 12 additions & 5 deletions app/routes/settings+/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { requireUserId } from '#app/utils/auth.server.ts'
import { prisma } from '#app/utils/db.server.ts'
import { cn, invariantResponse } from '#app/utils/misc.tsx'
import { useUser } from '#app/utils/user.ts'
import { z } from 'zod'

export const handle = {
breadcrumb: <Icon name="file-text">Edit Profile</Icon>,
Expand All @@ -21,17 +22,23 @@ export async function loader({ request }: DataFunctionArgs) {
return json({})
}

const BreadcrumbHandleMatch = z.object({
handle: z.object({ breadcrumb: z.any() }),
})

export default function EditUserProfile() {
const user = useUser()
const matches = useMatches()
const breadcrumbs = matches
.map(m =>
m.handle?.breadcrumb ? (
.map(m => {
const result = BreadcrumbHandleMatch.safeParse(m)
if (!result.success) return null
return (
<Link key={m.id} to={m.pathname} className="flex items-center">
{m.handle.breadcrumb}
{result.data.handle.breadcrumb}
</Link>
) : null,
)
)
})
.filter(Boolean)

return (
Expand Down
95 changes: 95 additions & 0 deletions app/routes/users+/$username.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @vitest-environment jsdom
*/
import { faker } from '@faker-js/faker'
import { unstable_createRemixStub as createRemixStub } from '@remix-run/testing'
import { render, screen } from '@testing-library/react'
import setCookieParser from 'set-cookie-parser'
import { test } from 'vitest'
import { loader as rootLoader } from '#app/root.tsx'
import { getSessionExpirationDate, sessionKey } from '#app/utils/auth.server.ts'
import { prisma } from '#app/utils/db.server.ts'
import { sessionStorage } from '#app/utils/session.server.ts'
import { createUser, getUserImages } from '#tests/db-utils.ts'
import { default as UsernameRoute, loader } from './$username.tsx'

test('The user profile when not logged in as self', async () => {
const userImages = await getUserImages()
const userImage =
userImages[faker.number.int({ min: 0, max: userImages.length - 1 })]
const user = await prisma.user.create({
select: { id: true, username: true, name: true },
data: { ...createUser(), image: { create: userImage } },
})
const App = createRemixStub([
{
path: '/users/:username',
Component: UsernameRoute,
loader,
},
])

const routeUrl = `/users/${user.username}`
render(<App initialEntries={[routeUrl]} />)

await screen.findByRole('heading', { level: 1, name: user.name! })
await screen.findByRole('img', { name: user.name! })
await screen.findByRole('link', { name: `${user.name}'s notes` })
})

test('The user profile when logged in as self', async () => {
const userImages = await getUserImages()
const userImage =
userImages[faker.number.int({ min: 0, max: userImages.length - 1 })]
const user = await prisma.user.create({
select: { id: true, username: true, name: true },
data: { ...createUser(), image: { create: userImage } },
})
const session = await prisma.session.create({
select: { id: true },
data: {
expirationDate: getSessionExpirationDate(),
userId: user.id,
},
})

const cookieSession = await sessionStorage.getSession()
cookieSession.set(sessionKey, session.id)
const setCookieHeader = await sessionStorage.commitSession(cookieSession)
const parsedCookie = setCookieParser.parseString(setCookieHeader)
const cookieHeader = new URLSearchParams({
[parsedCookie.name]: parsedCookie.value,
}).toString()

const App = createRemixStub([
{
id: 'root',
path: '/',
loader: async args => {
// add the cookie header to the request
args.request.headers.set('cookie', cookieHeader)
return rootLoader(args)
},
children: [
{
path: 'users/:username',
Component: UsernameRoute,
loader: async args => {
// add the cookie header to the request
args.request.headers.set('cookie', cookieHeader)
return loader(args)
},
},
],
},
])

const routeUrl = `/users/${user.username}`
await render(<App initialEntries={[routeUrl]} />)

await screen.findByRole('heading', { level: 1, name: user.name! })
await screen.findByRole('img', { name: user.name! })
await screen.findByRole('button', { name: /logout/i })
await screen.findByRole('link', { name: /my notes/i })
await screen.findByRole('link', { name: /edit profile/i })
})
9 changes: 2 additions & 7 deletions app/routes/users+/$username.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { json, type DataFunctionArgs } from '@remix-run/node'
import {
Form,
Link,
useLoaderData,
type V2_MetaFunction,
} from '@remix-run/react'
import { Form, Link, useLoaderData, type MetaFunction } from '@remix-run/react'
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
import { Spacer } from '#app/components/spacer.tsx'
import { Button } from '#app/components/ui/button.tsx'
Expand Down Expand Up @@ -102,7 +97,7 @@ export default function ProfileRoute() {
)
}

export const meta: V2_MetaFunction<typeof loader> = ({ data, params }) => {
export const meta: MetaFunction<typeof loader> = ({ data, params }) => {
const displayName = data?.user.name ?? params.username
return [
{ title: `${displayName} | Epic Notes` },
Expand Down
4 changes: 2 additions & 2 deletions app/routes/users+/$username_+/notes.$noteId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Link,
useActionData,
useLoaderData,
type V2_MetaFunction,
type MetaFunction,
} from '@remix-run/react'
import { formatDistanceToNow } from 'date-fns'
import { z } from 'zod'
Expand Down Expand Up @@ -191,7 +191,7 @@ export function DeleteNote({ id }: { id: string }) {
)
}

export const meta: V2_MetaFunction<
export const meta: MetaFunction<
typeof loader,
{ 'routes/users+/$username_+/notes': typeof notesLoader }
> = ({ data, params, matches }) => {
Expand Down
4 changes: 2 additions & 2 deletions app/routes/users+/$username_+/notes.index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type V2_MetaFunction } from '@remix-run/react'
import { type MetaFunction } from '@remix-run/react'
import { type loader as notesLoader } from './notes.tsx'

export default function NotesIndexRoute() {
Expand All @@ -9,7 +9,7 @@ export default function NotesIndexRoute() {
)
}

export const meta: V2_MetaFunction<
export const meta: MetaFunction<
null,
{ 'routes/users+/$username_+/notes': typeof notesLoader }
> = ({ params, matches }) => {
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dotenv/config'
import 'source-map-support/register.js'
import { installGlobals } from '@remix-run/node'
import chalk from 'chalk'
import closeWithGrace from 'close-with-grace'
Expand Down
Loading

0 comments on commit 474fd2f

Please sign in to comment.