Skip to content

Commit 4450bb6

Browse files
committed
test(hono): Add E2E test for Hono on Cloudflare
1 parent 485877e commit 4450bb6

File tree

10 files changed

+170
-37
lines changed

10 files changed

+170
-37
lines changed

dev-packages/e2e-tests/test-applications/hono-4-cf/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@ yarn-error.log*
2929
pnpm-debug.log*
3030
lerna-debug.log*
3131

32+
# test
33+
test-results
34+
3235
# misc
3336
.DS_Store
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
{
22
"name": "hono-4-cf",
33
"type": "module",
4+
"version": "0.0.0",
5+
"private": true,
46
"scripts": {
5-
"dev": "wrangler dev",
7+
"dev": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')",
8+
"build": "wrangler deploy --dry-run",
69
"deploy": "wrangler deploy --minify",
7-
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
10+
"cf-typegen": "wrangler types --env-interface CloudflareBindings",
11+
"test:build": "pnpm install && pnpm build",
12+
"test:assert": "TEST_ENV=production playwright test"
813
},
914
"dependencies": {
15+
"@sentry/hono": "latest || *",
1016
"hono": "^4.12.14"
1117
},
1218
"devDependencies": {
13-
"wrangler": "^4.4.0"
19+
"@playwright/test": "~1.56.0",
20+
"@cloudflare/workers-types": "^4.20240725.0",
21+
"@sentry-internal/test-utils": "link:../../../test-utils",
22+
"typescript": "^5.5.2",
23+
"wrangler": "^4.61.0"
24+
},
25+
"volta": {
26+
"extends": "../../package.json"
1427
}
1528
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const testEnv = process.env.TEST_ENV;
4+
5+
if (!testEnv) {
6+
throw new Error('No test env defined');
7+
}
8+
9+
const APP_PORT = 38787;
10+
11+
const config = getPlaywrightConfig(
12+
{
13+
startCommand: `pnpm dev --port ${APP_PORT}`,
14+
port: APP_PORT,
15+
},
16+
{
17+
workers: '100%',
18+
retries: 0,
19+
},
20+
);
21+
22+
export default config;
Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
import { Hono } from 'hono';
2+
import { HTTPException } from 'hono/http-exception';
3+
import { sentry } from '@sentry/hono/cloudflare';
24

3-
const app = new Hono();
5+
const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>();
6+
7+
app.use(
8+
sentry(app, env => ({
9+
dsn: env.E2E_TEST_DSN,
10+
environment: 'qa',
11+
tracesSampleRate: 1.0,
12+
tunnel: 'http://localhost:3031/', // proxy server
13+
})),
14+
);
415

516
app.get('/', c => {
617
return c.text('Hello Hono!');
718
});
819

20+
app.get('/test-param/:paramId', c => {
21+
return c.json({ paramId: c.req.param('paramId') });
22+
});
23+
24+
app.get('/error/:cause', c => {
25+
throw new Error('This is a test error for Sentry!', {
26+
cause: c.req.param('cause'),
27+
});
28+
});
29+
30+
app.get('/http-exception/:code', c => {
31+
const code = Number(c.req.param('code')) as Parameters<typeof HTTPException>[0];
32+
throw new HTTPException(code, { message: `HTTPException ${code}` });
33+
});
34+
935
export default app;
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: 'hono-4-cf',
6+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError } from '@sentry-internal/test-utils';
3+
4+
test('captures error thrown in route handler', async ({ baseURL }) => {
5+
const errorWaiter = waitForError('hono-4-cf', event => {
6+
return event.exception?.values?.[0]?.value === 'This is a test error for Sentry!';
7+
});
8+
9+
const response = await fetch(`${baseURL}/error/test-cause`);
10+
expect(response.status).toBe(500);
11+
12+
const event = await errorWaiter;
13+
expect(event.exception?.values?.[0]?.value).toBe('This is a test error for Sentry!');
14+
});
15+
16+
test('captures HTTPException with 502 status', async ({ baseURL }) => {
17+
const errorWaiter = waitForError('hono-4-cf', event => {
18+
return event.exception?.values?.[0]?.value === 'HTTPException 502';
19+
});
20+
21+
const response = await fetch(`${baseURL}/http-exception/502`);
22+
expect(response.status).toBe(502);
23+
24+
const event = await errorWaiter;
25+
expect(event.exception?.values?.[0]?.value).toBe('HTTPException 502');
26+
});
27+
28+
// TODO: 401 and 404 HTTPExceptions should not be captured by Sentry by default,
29+
// but currently they are. Fix the filtering and update these tests accordingly.
30+
test('captures HTTPException with 401 status', async ({ baseURL }) => {
31+
const errorWaiter = waitForError('hono-4-cf', event => {
32+
return event.exception?.values?.[0]?.value === 'HTTPException 401';
33+
});
34+
35+
const response = await fetch(`${baseURL}/http-exception/401`);
36+
expect(response.status).toBe(401);
37+
38+
const event = await errorWaiter;
39+
expect(event.exception?.values?.[0]?.value).toBe('HTTPException 401');
40+
});
41+
42+
test('captures HTTPException with 404 status', async ({ baseURL }) => {
43+
const errorWaiter = waitForError('hono-4-cf', event => {
44+
return event.exception?.values?.[0]?.value === 'HTTPException 404';
45+
});
46+
47+
const response = await fetch(`${baseURL}/http-exception/404`);
48+
expect(response.status).toBe(404);
49+
50+
const event = await errorWaiter;
51+
expect(event.exception?.values?.[0]?.value).toBe('HTTPException 404');
52+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
test('sends a transaction for the index route', async ({ baseURL }) => {
5+
const transactionWaiter = waitForTransaction('hono-4-cf', event => {
6+
return event.transaction === 'GET /';
7+
});
8+
9+
const response = await fetch(`${baseURL}/`);
10+
expect(response.status).toBe(200);
11+
12+
const transaction = await transactionWaiter;
13+
expect(transaction.contexts?.trace?.op).toBe('http.server');
14+
});
15+
16+
test('sends a transaction for a parameterized route', async ({ baseURL }) => {
17+
const transactionWaiter = waitForTransaction('hono-4-cf', event => {
18+
return event.transaction === 'GET /test-param/:paramId';
19+
});
20+
21+
const response = await fetch(`${baseURL}/test-param/123`);
22+
expect(response.status).toBe(200);
23+
24+
const transaction = await transactionWaiter;
25+
expect(transaction.contexts?.trace?.op).toBe('http.server');
26+
expect(transaction.transaction).toBe('GET /test-param/:paramId');
27+
});
28+
29+
test('sends a transaction for a route that throws', async ({ baseURL }) => {
30+
const transactionWaiter = waitForTransaction('hono-4-cf', event => {
31+
return event.transaction === 'GET /error/:cause';
32+
});
33+
34+
await fetch(`${baseURL}/error/test-cause`);
35+
36+
const transaction = await transactionWaiter;
37+
expect(transaction.contexts?.trace?.op).toBe('http.server');
38+
expect(transaction.contexts?.trace?.status).toBe('internal_error');
39+
});

dev-packages/e2e-tests/test-applications/hono-4-cf/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"skipLibCheck": true,
88
"lib": ["ESNext"],
99
"jsx": "react-jsx",
10-
"jsxImportSource": "hono/jsx"
10+
"jsxImportSource": "hono/jsx",
11+
"types": ["@cloudflare/workers-types"]
1112
}
1213
}

dev-packages/e2e-tests/test-applications/hono-4-cf/wrangler.jsonc

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,5 @@
33
"name": "hono-4-cf",
44
"main": "src/index.ts",
55
"compatibility_date": "2026-04-20",
6-
// "compatibility_flags": [
7-
// "nodejs_compat"
8-
// ],
9-
// "vars": {
10-
// "MY_VAR": "my-variable"
11-
// },
12-
// "kv_namespaces": [
13-
// {
14-
// "binding": "MY_KV_NAMESPACE",
15-
// "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
16-
// }
17-
// ],
18-
// "r2_buckets": [
19-
// {
20-
// "binding": "MY_BUCKET",
21-
// "bucket_name": "my-bucket"
22-
// }
23-
// ],
24-
// "d1_databases": [
25-
// {
26-
// "binding": "MY_DB",
27-
// "database_name": "my-database",
28-
// "database_id": ""
29-
// }
30-
// ],
31-
// "ai": {
32-
// "binding": "AI"
33-
// },
34-
// "observability": {
35-
// "enabled": true,
36-
// "head_sampling_rate": 1
37-
// }
6+
"compatibility_flags": ["nodejs_compat"],
387
}

0 commit comments

Comments
 (0)