Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ jobs:
'nextjs-13',
'nextjs-14',
'nextjs-15',
'nextjs-t3',
'react-17',
'react-19',
'react-create-hash-router',
Expand Down
45 changes: 45 additions & 0 deletions dev-packages/e2e-tests/test-applications/nextjs-t3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

!*.d.ts

# Sentry
.sentryclirc

.vscode

test-results
2 changes: 2 additions & 0 deletions dev-packages/e2e-tests/test-applications/nextjs-t3/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@sentry:registry=http://127.0.0.1:4873
@sentry-internal:registry=http://127.0.0.1:4873
29 changes: 29 additions & 0 deletions dev-packages/e2e-tests/test-applications/nextjs-t3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Create T3 App

This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.

## What's next? How do I make an app with this?

We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.

If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.

- [Next.js](https://nextjs.org)
- [NextAuth.js](https://next-auth.js.org)
- [Prisma](https://prisma.io)
- [Drizzle](https://orm.drizzle.team)
- [Tailwind CSS](https://tailwindcss.com)
- [tRPC](https://trpc.io)

## Learn More

To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:

- [Documentation](https://create.t3.gg/)
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials

You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!

## How do I deploy this?

Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
11 changes: 11 additions & 0 deletions dev-packages/e2e-tests/test-applications/nextjs-t3/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
await import('./src/env.js');

/** @type {import("next").NextConfig} */
const config = {};

import { withSentryConfig } from '@sentry/nextjs';

export default withSentryConfig(config, {
disableLogger: true,
silent: true,
});
52 changes: 52 additions & 0 deletions dev-packages/e2e-tests/test-applications/nextjs-t3/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "t3",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "next build",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
"test:prod": "TEST_ENV=production playwright test",
"test:dev": "TEST_ENV=development playwright test",
"test:build": "pnpm install && npx playwright install && pnpm build",
"test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build",
"test:build-latest": "pnpm install && pnpm add next@rc && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build",
"test:assert": "pnpm test:prod && pnpm test:dev"
},
"dependencies": {
"@sentry/nextjs": "latest || *",
"@t3-oss/env-nextjs": "^0.10.1",
"@tanstack/react-query": "^5.50.0",
"@trpc/client": "^11.0.0-rc.446",
"@trpc/react-query": "^11.0.0-rc.446",
"@trpc/server": "^11.0.0-rc.446",
"geist": "^1.3.0",
"next": "^14.2.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.23.3"
},
"devDependencies": {
"@types/eslint": "^8.56.10",
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.4",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"tailwindcss": "^3.4.3",
"typescript": "^5.5.3"
},
"ct3aMetadata": {
"initVersion": "7.37.0"
},
"volta": {
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
const testEnv = process.env.TEST_ENV;

if (!testEnv) {
throw new Error('No test env defined');
}

const config = getPlaywrightConfig(
{
startCommand: testEnv === 'development' ? 'pnpm next dev -p 3030' : 'pnpm next start -p 3030',
port: 3030,
},
{
// This comes with the risk of tests leaking into each other but the tests run quite slow so we should parallelize
workers: '100%',
},
);

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const config = {
plugins: {
tailwindcss: {},
},
};

module.exports = config;
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as Sentry from '@sentry/nextjs';

Sentry.init({
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1,
debug: false,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
// The config you add here will be used whenever one of the edge features is loaded.
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/

import * as Sentry from '@sentry/nextjs';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as Sentry from '@sentry/nextjs';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use client';

import { useState } from 'react';

import { api } from '~/trpc/react';

export function LatestPost() {
const [latestPost] = api.post.getLatest.useSuspenseQuery();

const utils = api.useUtils();
const [name, setName] = useState('');
const createPost = api.post.create.useMutation({
onSuccess: async () => {
await utils.post.invalidate();
setName('');
},
});

const throwingMutation = api.post.throwError.useMutation({
onSuccess: async () => {
await utils.post.invalidate();
setName('');
},
});

return (
<div className="w-full max-w-xs">
{latestPost ? (
<p className="truncate">Your most recent post: {latestPost.name}</p>
) : (
<p>You have no posts yet.</p>
)}
<form
onSubmit={e => {
e.preventDefault();
createPost.mutate({ name });
}}
className="flex flex-col gap-2"
>
<input
type="text"
placeholder="Title"
value={name}
onChange={e => setName(e.target.value)}
id="createInput"
className="w-full rounded-full px-4 py-2 text-black"
/>
<button
type="submit"
id="createButton"
className="rounded-full bg-white/10 px-10 py-3 font-semibold transition hover:bg-white/20"
disabled={createPost.isPending}
>
{createPost.isPending ? 'Submitting...' : 'Submit'}
</button>
</form>
<form
onSubmit={e => {
e.preventDefault();
throwingMutation.mutate({ name: 'I love dogs' });
}}
className="flex flex-col gap-2"
>
<button
id="error-button"
type="submit"
className="rounded-full bg-white/10 px-10 py-3 font-semibold transition hover:bg-white/20"
disabled={throwingMutation.isPending}
>
THROW TRPC
</button>
</form>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextRequest } from "next/server";

import { env } from "~/env";
import { appRouter } from "~/server/api/root";
import { createTRPCContext } from "~/server/api/trpc";

/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a HTTP request (e.g. when you make requests from Client Components).
*/
const createContext = async (req: NextRequest) => {
return createTRPCContext({
headers: req.headers,
});
};

const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createContext(req),
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
);
}
: undefined,
});

export { handler as GET, handler as POST };
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import * as Sentry from "@sentry/nextjs";
import NextError from "next/error";
import { useEffect } from "react";

export default function GlobalError({
error,
}: {
error: Error & { digest?: string };
}) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);

return (
<html>
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import "~/styles/globals.css";

import { GeistSans } from "geist/font/sans";
import { type Metadata } from "next";

import { TRPCReactProvider } from "~/trpc/react";

export const metadata: Metadata = {
title: "Create T3 App",
description: "Generated by create-t3-app",
icons: [{ rel: "icon", url: "/favicon.ico" }],
};

export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${GeistSans.variable}`}>
<body>
<TRPCReactProvider>{children}</TRPCReactProvider>
</body>
</html>
);
}
Loading
Loading