Skip to content

Commit

Permalink
test(e2e): Add standard frontend test app specification (#5874)
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst committed Oct 4, 2022
1 parent 29f4af1 commit 28c464d
Show file tree
Hide file tree
Showing 12 changed files with 9,375 additions and 0 deletions.
41 changes: 41 additions & 0 deletions packages/e2e-tests/README.md
Expand Up @@ -93,3 +93,44 @@ add Sentry dependencies to your test application, you should set the dependency
```

All that is left for you to do now is to create a test app and run `yarn test:e2e`.

## Standardized Test Apps

For some of our E2E tests we define a standard for test applications as to how they should look and behave. Standardized
test apps enables us to reuse the same test suite over a number of different frameworks/SDKs.

### Standardized Frontend Test Apps

A standardized frontend test application has the following features:

- Just for the sake of consistency we prefix the standardized frontend tests with `standard-frontend-`. For example
`standard-frontend-nextjs`.
- A page at path `/`
- Having a `<input type="button" id="exception-button">` that captures an Exception when clicked. The returned
`eventId` from the `Sentry.captureException()` call must be written to `window.capturedExceptionId`. It doesn not
matter what the captured error looks like.
- Having an link with `id="navigation"` that navigates to `/user/5`. It doesn't have to be an `<a>` tag, for example
if a framework has another way of doing routing, the important part is that the element to click for navigation has
the correct `id`. Text of the link doesn't matter.
- An empty page at `/user/5`
- Apps should write all pageload and navigation transaction IDs into an array at `window.recordedTransactions`. This can
be done with an event processor:

```ts
Sentry.addGlobalEventProcessor(event => {
if (
event.type === 'transaction' &&
(event.contexts?.trace?.op === 'pageload' || event.contexts?.trace?.op === 'navigation')
) {
const eventId = event.event_id;
window.recordedTransactions = window.recordedTransactions || [];
window.recordedTransactions.push(eventId);
}

return event;
});
```

### Standardized Backend Test Apps

TBD
@@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

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

!*.d.ts
@@ -0,0 +1,2 @@
@sentry:registry=http://localhost:4873
@sentry-internal:registry=http://localhost:4873
@@ -0,0 +1,44 @@
{
"name": "standard-frontend-react-test",
"version": "0.1.0",
"private": true,
"dependencies": {
"@sentry/react": "*",
"@sentry/tracing": "*",
"@testing-library/jest-dom": "5.14.1",
"@testing-library/react": "13.0.0",
"@testing-library/user-event": "13.2.1",
"@types/jest": "27.0.1",
"@types/node": "16.7.13",
"@types/react": "18.0.0",
"@types/react-dom": "18.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "^6.4.1",
"react-scripts": "5.0.1",
"typescript": "4.4.2",
"web-vitals": "2.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
@@ -0,0 +1,61 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import {
Routes,
BrowserRouter,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
Route,
} from 'react-router-dom';
import Index from './pages/Index';
import User from './pages/User';

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [
new BrowserTracing({
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
),
}),
],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
release: 'e2e-test',
});

Sentry.addGlobalEventProcessor(event => {
if (
event.type === 'transaction' &&
(event.contexts?.trace?.op === 'pageload' || event.contexts?.trace?.op === 'navigation')
) {
const eventId = event.event_id;
// @ts-ignore
window.recordedTransactions = window.recordedTransactions || [];
// @ts-ignore
window.recordedTransactions.push(eventId);
}

return event;
});

const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<BrowserRouter>
<SentryRoutes>
<Route path="/" element={<Index />} />
<Route path="/user/:id" element={<User />} />
</SentryRoutes>
</BrowserRouter>,
);
@@ -0,0 +1,25 @@
import * as React from 'react';
import * as Sentry from '@sentry/react';
import { Link } from 'react-router-dom';

const Index = () => {
return (
<>
<input
type="button"
value="Capture Exception"
id="exception-button"
onClick={() => {
const eventId = Sentry.captureException(new Error('I am an error!'));
// @ts-ignore
window.capturedExceptionId = eventId;
}}
/>
<Link to="/user/5" id="navigation">
navigate
</Link>
</>
);
};

export default Index;
@@ -0,0 +1,7 @@
import * as React from 'react';

const User = () => {
return <p>I am a blank page :)</p>;
};

export default User;
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
@@ -0,0 +1,6 @@
{
"$schema": "../../test-recipe-schema.json",
"testApplicationName": "standard-frontend-react",
"buildCommand": "yarn install && yarn build",
"tests": []
}
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["src"]
}

0 comments on commit 28c464d

Please sign in to comment.