Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next.js integration tests for Server and Browser #3632

Merged
merged 17 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/nextjs/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
ecmaVersion: 2018,
},
extends: ['@sentry-internal/sdk'],
ignorePatterns: ['build/**', 'dist/**', 'esm/**', 'examples/**', 'scripts/**'],
ignorePatterns: ['build/**', 'dist/**', 'esm/**', 'examples/**', 'scripts/**', 'test/integration/**'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.d.ts'],
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test/integration
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 6 additions & 1 deletion packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@
"fix": "run-s fix:eslint fix:prettier",
"fix:eslint": "eslint . --format stylish --fix",
"fix:prettier": "prettier --write \"{src,test}/**/*.ts\"",
"test": "jest",
"test": "run-s test:unit test:integration",
"test:watch": "jest --watch",
"test:unit": "jest",
"test:integration": "run-s test:integration:build test:integration:server test:integration:browser",
"test:integration:build": "cd test/integration && yarn && yarn build && cd ../..",
"test:integration:server": "node test/integration/test/server.js",
"test:integration:browser": "node test/integration/test/browser.js",
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved
iker-barriocanal marked this conversation as resolved.
Show resolved Hide resolved
"pack": "npm pack",
"vercel:branch": "source vercel/set-up-branch-for-test-app-use.sh",
"vercel:project": "source vercel/make-project-use-current-branch.sh"
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/src/performance/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { Primitive, Transaction, TransactionContext } from '@sentry/types';
import { fill, getGlobalObject, stripUrlQueryAndFragment } from '@sentry/utils';
import { default as Router } from 'next/router';
Expand Down
34 changes: 34 additions & 0 deletions packages/nextjs/test/integration/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel
47 changes: 47 additions & 0 deletions packages/nextjs/test/integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# TypeScript Next.js example
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved

This is a really simple project that shows the usage of Next.js with TypeScript.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-typescript&project-name=with-typescript&repository-name=with-typescript)

## How to use it?

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with
[npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:

```bash
npx create-next-app --example with-typescript with-typescript-app
# or
yarn create next-app --example with-typescript with-typescript-app
```

Deploy it to the cloud with
[Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example)
([Documentation](https://nextjs.org/docs/deployment)).

## Notes

This example shows how to integrate the TypeScript type system into Next.js. Since TypeScript is supported out of the
box with Next.js, all we have to do is to install TypeScript.

```
npm install --save-dev typescript
```

To enable TypeScript's features, we install the type declarations for React and Node.

```
npm install --save-dev @types/react @types/react-dom @types/node
```

When we run `next dev` the next time, Next.js will start looking for any `.ts` or `.tsx` files in our project and builds
it. It even automatically creates a `tsconfig.json` file for our project with the recommended settings.

Next.js has built-in TypeScript declarations, so we'll get autocompletion for Next.js' modules straight away.

A `type-check` script is also added to `package.json`, which runs TypeScript's `tsc` CLI in `noEmit` mode to run
type-checking separately. You can then include this, for example, in your `test` scripts.
41 changes: 41 additions & 0 deletions packages/nextjs/test/integration/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { ReactNode } from 'react';
import Link from 'next/link';
import Head from 'next/head';

type Props = {
children?: ReactNode;
title?: string;
};

const Layout = ({ children, title = 'This is the default title' }: Props) => (
<div>
<Head>
<title>{title}</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<header>
<nav>
<Link href="/">
<a>Home</a>
</Link>{' '}
|{' '}
<Link href="/about">
<a>About</a>
</Link>{' '}
|{' '}
<Link href="/users">
<a>Users List</a>
</Link>{' '}
| <a href="/api/users">Users API</a>
</nav>
</header>
{children}
<footer>
<hr />
<span>I'm here to stay (Footer)</span>
</footer>
</div>
);

export default Layout;
19 changes: 19 additions & 0 deletions packages/nextjs/test/integration/components/List.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';
import ListItem from './ListItem';
import { User } from '../interfaces';

type Props = {
items: User[];
};

const List = ({ items }: Props) => (
<ul>
{items.map(item => (
<li key={item.id}>
<ListItem data={item} />
</li>
))}
</ul>
);

export default List;
16 changes: 16 additions & 0 deletions packages/nextjs/test/integration/components/ListDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from 'react';

import { User } from '../interfaces';

type ListDetailProps = {
item: User;
};

const ListDetail = ({ item: user }: ListDetailProps) => (
<div>
<h1>Detail for {user.name}</h1>
<p>ID: {user.id}</p>
</div>
);

export default ListDetail;
18 changes: 18 additions & 0 deletions packages/nextjs/test/integration/components/ListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import Link from 'next/link';

import { User } from '../interfaces';

type Props = {
data: User;
};

const ListItem = ({ data }: Props) => (
<Link href="/users/[id]" as={`/users/${data.id}`}>
<a>
{data.id}: {data.name}
</a>
</Link>
);

export default ListItem;
10 changes: 10 additions & 0 deletions packages/nextjs/test/integration/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// You can include shared interfaces/types in a separate file
// and then use them in any component by importing them. For
// example, to import the interface below do:
//
// import { User } from 'path/to/interfaces';

export type User = {
id: number;
name: string;
};
2 changes: 2 additions & 0 deletions packages/nextjs/test/integration/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
9 changes: 9 additions & 0 deletions packages/nextjs/test/integration/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { withSentryConfig } = require('@sentry/nextjs');

const moduleExports = {};
const SentryWebpackPluginOptions = {
dryRun: true,
silent: true,
};

module.exports = withSentryConfig(moduleExports, SentryWebpackPluginOptions);
26 changes: 26 additions & 0 deletions packages/nextjs/test/integration/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "with-typescript",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"type-check": "tsc"
},
"dependencies": {
"@sentry/nextjs": "file:../../",
"next": "latest",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"devDependencies": {
"@types/node": "^15.3.1",
"@types/puppeteer": "^5.4.3",
"@types/react": "^17.0.6",
"@types/react-dom": "^17.0.5",
"nock": "^13.1.0",
"puppeteer": "^9.1.1",
"typescript": "^4.2.4"
},
"license": "MIT"
}
3 changes: 3 additions & 0 deletions packages/nextjs/test/integration/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const AboutPage = () => <h1>About</h1>;

export default AboutPage;
9 changes: 9 additions & 0 deletions packages/nextjs/test/integration/pages/alsoHealthy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Link from 'next/link';

const HealthyPage = (): JSX.Element => (
<Link href="/healthy">
<a id="healthy">Healthy</a>
</Link>
);

export default HealthyPage;
8 changes: 8 additions & 0 deletions packages/nextjs/test/integration/pages/api/broken/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { withSentry } from '@sentry/nextjs';
import { NextApiRequest, NextApiResponse } from 'next';

const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
res.status(500).json({ statusCode: 500, message: 'Something went wrong' });
};

export default withSentry(handler);
8 changes: 8 additions & 0 deletions packages/nextjs/test/integration/pages/api/error/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { withSentry } from '@sentry/nextjs';
import { NextApiRequest, NextApiResponse } from 'next';

const handler = async (_req: NextApiRequest, _res: NextApiResponse): Promise<void> => {
throw new Error('API Error');
};

export default withSentry(handler);
10 changes: 10 additions & 0 deletions packages/nextjs/test/integration/pages/api/http/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { withSentry } from '@sentry/nextjs';
import { get } from 'http';
import { NextApiRequest, NextApiResponse } from 'next';

const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
await new Promise(resolve => get('http://example.com', resolve));
res.status(200).json({});
};

export default withSentry(handler);
18 changes: 18 additions & 0 deletions packages/nextjs/test/integration/pages/api/users/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { withSentry } from '@sentry/nextjs';
import { NextApiRequest, NextApiResponse } from 'next';

import { sampleUserData } from '../../../utils/sample-data';

const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
try {
if (!Array.isArray(sampleUserData)) {
throw new Error('Cannot find user data');
}

res.status(200).json(sampleUserData);
} catch (err) {
res.status(500).json({ statusCode: 500, message: (err as Error).message });
}
};

export default withSentry(handler);
16 changes: 16 additions & 0 deletions packages/nextjs/test/integration/pages/crashed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const CrashedPage = (): JSX.Element => {
// Magic to naively trigger onerror to make session crashed and allow for SSR
try {
// @ts-ignore
if (typeof window !== 'undefine' && typeof window.onerror === 'function') {
kamilogorek marked this conversation as resolved.
Show resolved Hide resolved
// Lovely oldschool browsers syntax with 5 arguments <3
// @ts-ignore
window.onerror(null, null, null, null, new Error('Crashed'));
}
} catch (_e) {
// no-empty
}
return <h1>Crashed</h1>;
};

export default CrashedPage;
11 changes: 11 additions & 0 deletions packages/nextjs/test/integration/pages/errorClick.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const ButtonPage = (): JSX.Element => (
<button
onClick={() => {
throw new Error('Sentry Frontend Error');
}}
>
Throw Error
</button>
);

export default ButtonPage;
13 changes: 13 additions & 0 deletions packages/nextjs/test/integration/pages/fetch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const ButtonPage = (): JSX.Element => (
<button
onClick={() => {
fetch('http://example.com').catch(() => {
// no-empty
});
}}
>
Send Request
</button>
);

export default ButtonPage;
9 changes: 9 additions & 0 deletions packages/nextjs/test/integration/pages/healthy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Link from 'next/link';

const HealthyPage = (): JSX.Element => (
<Link href="/alsoHealthy">
<a id="alsoHealthy">AlsoHealthy</a>
</Link>
);

export default HealthyPage;
3 changes: 3 additions & 0 deletions packages/nextjs/test/integration/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const IndexPage = (): JSX.Element => <h1>Hello Next.js</h1>;

export default IndexPage;