Skip to content

Commit ef4ba1a

Browse files
committed
wip e2e test
1 parent 72bf84d commit ef4ba1a

File tree

13 files changed

+214
-8
lines changed

13 files changed

+214
-8
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vite + TS</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/main.ts"></script>
11+
<button id="trigger-error" type="button" style="background-color: #dc3545; color: white">
12+
Trigger Worker Error
13+
</button>
14+
</body>
15+
</html>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "gh-sentry-javascript-bundler-plugins-755-vite-worker-bundles",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "rm -rf dist &&tsc && vite build",
9+
"preview": "vite preview --port 3030",
10+
"test:build": "pnpm install && pnpm build",
11+
"test:assert": "pnpm test"
12+
},
13+
"devDependencies": {
14+
"@playwright/test": "~1.53.2",
15+
"@sentry-internal/test-utils": "link:../../../test-utils",
16+
"typescript": "~5.8.3",
17+
"vite": "^7.0.4"
18+
},
19+
"dependencies": {
20+
"@sentry/browser": "^9.38.0",
21+
"@sentry/vite-plugin": "^3.5.0"
22+
}
23+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const config = getPlaywrightConfig({
4+
startCommand: `pnpm preview`,
5+
});
6+
7+
export default config;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import './style.css';
2+
import MyWorker from './worker.ts?worker';
3+
import * as Sentry from '@sentry/browser';
4+
5+
Sentry.init({
6+
dsn: import.meta.env.E2E_TEST_DSN,
7+
environment: import.meta.env.MODE || 'development',
8+
tracesSampleRate: 1.0,
9+
debug: true,
10+
integrations: [Sentry.browserTracingIntegration()],
11+
});
12+
13+
const worker = new MyWorker();
14+
15+
Sentry.addIntegration(Sentry.webWorkerIntegration({ worker }));
16+
17+
worker.addEventListener('message', event => {
18+
// this is part of the test, do not delete
19+
console.log('xx received message from worker', event);
20+
});
21+
22+
document.querySelector<HTMLButtonElement>('#trigger-error')!.addEventListener('click', () => {
23+
worker.postMessage({
24+
type: 'TRIGGER_ERROR',
25+
data: 'This message triggers an uncaught error!',
26+
});
27+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
// type cast necessary because TS thinks this file is part of the main
4+
// thread where self is of type `Window` instead of `Worker`
5+
Sentry.registerWebWorker({ self: self as unknown as Worker });
6+
7+
// Let the main thread know the worker is ready
8+
self.postMessage({
9+
msg: 'WORKER_READY',
10+
});
11+
12+
self.addEventListener('message', event => {
13+
if (event.data.msg === 'TRIGGER_ERROR') {
14+
// This will throw an uncaught error in the worker
15+
throw new Error(`Uncaught error in worker`);
16+
}
17+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { startEventProxyServer } from '@sentry-internal/test-utils';
2+
3+
startEventProxyServer({
4+
port: 3031,
5+
proxyServerName: 'browser-webworker-vite',
6+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
const E2E_TEST_APP_NAME = 'browser-webworker-vite';
5+
6+
test('captures an error with debug ids and pageload trace context', async ({ page }) => {
7+
const errorEventPromise = waitForError(E2E_TEST_APP_NAME, event => {
8+
return !event.type && event.exception?.values?.[0]?.value === 'Uncaught error in worker';
9+
});
10+
11+
const transactionPromise = waitForTransaction(E2E_TEST_APP_NAME, transactionEvent => {
12+
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
13+
});
14+
15+
await page.goto('/');
16+
17+
await page.locator('id=trigger-error').click();
18+
19+
const errorEvent = await errorEventPromise;
20+
const transactionEvent = await transactionPromise;
21+
22+
const pageloadTraceId = transactionEvent.contexts?.trace?.trace_id;
23+
const pageloadSpanId = transactionEvent.contexts?.trace?.span_id;
24+
25+
expect(errorEvent.exception?.values).toHaveLength(1);
26+
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught error in worker');
27+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1);
28+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toContain('worker.js');
29+
30+
expect(errorEvent.transaction).toBe('/');
31+
expect(transactionEvent.transaction).toBe('/');
32+
33+
expect(errorEvent.request).toEqual({
34+
url: 'http://localhost:4173/',
35+
headers: expect.any(Object),
36+
});
37+
38+
expect(errorEvent.contexts?.trace).toEqual({
39+
trace_id: pageloadTraceId,
40+
span_id: pageloadSpanId,
41+
});
42+
43+
expect(errorEvent.debug_meta).toEqual({
44+
sourcemaps: {
45+
'worker.js': {
46+
version: expect.any(String),
47+
url: expect.any(String),
48+
},
49+
},
50+
});
51+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"useDefineForClassFields": true,
5+
"module": "ESNext",
6+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"verbatimModuleSyntax": true,
13+
"moduleDetection": "force",
14+
"noEmit": true,
15+
16+
/* Linting */
17+
"strict": true,
18+
"noUnusedLocals": true,
19+
"noUnusedParameters": true,
20+
"erasableSyntaxOnly": true,
21+
"noFallthroughCasesInSwitch": true,
22+
"noUncheckedSideEffectImports": true
23+
},
24+
"include": ["src"]
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { sentryVitePlugin } from '@sentry/vite-plugin';
2+
import { defineConfig } from 'vite';
3+
4+
export default defineConfig({
5+
build: {
6+
sourcemap: 'hidden',
7+
},
8+
9+
plugins: [
10+
sentryVitePlugin({
11+
org: process.env.E2E_TEST_SENTRY_ORG_SLUG,
12+
project: process.env.E2E_TEST_SENTRY_PROJECT,
13+
authToken: process.env.E2E_TEST_AUTH_TOKEN,
14+
}),
15+
],
16+
17+
worker: {
18+
plugins: () => [
19+
...sentryVitePlugin({
20+
org: process.env.E2E_TEST_SENTRY_ORG_SLUG,
21+
project: process.env.E2E_TEST_SENTRY_PROJECT,
22+
authToken: process.env.E2E_TEST_AUTH_TOKEN,
23+
}),
24+
],
25+
},
26+
});

0 commit comments

Comments
 (0)