Skip to content

Commit e4e6c01

Browse files
authored
test(hono): Add E2E test for Hono on Cloudflare, Node and Bun (#20406)
Adds a minimal E2E test setup. More tests are added later on - to keep reviewing this easier. Adds a testing matrix where each test uses a different entry point. This way, only one E2E application needs to be added and we have an easier time keeping the tests and functionality in sync across runtimes. Closes #20403
1 parent 2fd74e9 commit e4e6c01

File tree

14 files changed

+327
-1
lines changed

14 files changed

+327
-1
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,9 @@ jobs:
930930
with:
931931
node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json'
932932
- name: Set up Bun
933-
if: contains(fromJSON('["node-exports-test-app","nextjs-16-bun", "elysia-bun"]'), matrix.test-application)
933+
if:
934+
contains(fromJSON('["node-exports-test-app","nextjs-16-bun", "elysia-bun", "hono-4"]'),
935+
matrix.test-application)
934936
uses: oven-sh/setup-bun@v2
935937
- name: Set up AWS SAM
936938
if: matrix.test-application == 'aws-serverless'
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# prod
2+
dist/
3+
4+
# dev
5+
.yarn/
6+
!.yarn/releases
7+
.vscode/*
8+
!.vscode/launch.json
9+
!.vscode/*.code-snippets
10+
.idea/workspace.xml
11+
.idea/usage.statistics.xml
12+
.idea/shelf
13+
14+
# deps
15+
node_modules/
16+
.wrangler
17+
18+
# env
19+
.env
20+
.env.production
21+
.dev.vars
22+
23+
# logs
24+
logs/
25+
*.log
26+
npm-debug.log*
27+
yarn-debug.log*
28+
yarn-error.log*
29+
pnpm-debug.log*
30+
lerna-debug.log*
31+
32+
# test
33+
test-results
34+
35+
# misc
36+
.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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "hono-4",
3+
"type": "module",
4+
"version": "0.0.0",
5+
"private": true,
6+
"scripts": {
7+
"dev:cf": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')",
8+
"dev:node": "node --import tsx/esm --import @sentry/node/preload src/entry.node.ts",
9+
"dev:bun": "bun src/entry.bun.ts",
10+
"build": "wrangler deploy --dry-run",
11+
"test:build": "pnpm install && pnpm build",
12+
"test:assert": "TEST_ENV=production playwright test"
13+
},
14+
"dependencies": {
15+
"@sentry/hono": "latest || *",
16+
"@sentry/node": "latest || *",
17+
"@hono/node-server": "^1.19.10",
18+
"hono": "^4.12.14"
19+
},
20+
"devDependencies": {
21+
"@playwright/test": "~1.56.0",
22+
"@cloudflare/workers-types": "^4.20240725.0",
23+
"@sentry-internal/test-utils": "link:../../../test-utils",
24+
"tsx": "^4.20.3",
25+
"typescript": "^5.5.2",
26+
"wrangler": "^4.61.0"
27+
},
28+
"volta": {
29+
"extends": "../../package.json"
30+
},
31+
"sentryTest": {
32+
"variants": [
33+
{
34+
"assert-command": "RUNTIME=node pnpm test:assert",
35+
"label": "hono-4 (node)"
36+
},
37+
{
38+
"assert-command": "RUNTIME=bun pnpm test:assert",
39+
"label": "hono-4 (bun)"
40+
}
41+
]
42+
}
43+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
type Runtime = 'cloudflare' | 'node' | 'bun';
4+
5+
const RUNTIME = (process.env.RUNTIME || 'cloudflare') as Runtime;
6+
7+
const testEnv = process.env.TEST_ENV;
8+
9+
if (!testEnv) {
10+
throw new Error('No test env defined');
11+
}
12+
13+
const APP_PORT = 38787;
14+
15+
const startCommands: Record<Runtime, string> = {
16+
cloudflare: `pnpm dev:cf --port ${APP_PORT}`,
17+
node: `pnpm dev:node`,
18+
bun: `pnpm dev:bun`,
19+
};
20+
21+
const config = getPlaywrightConfig(
22+
{
23+
startCommand: startCommands[RUNTIME],
24+
port: APP_PORT,
25+
},
26+
{
27+
workers: '100%',
28+
retries: 0,
29+
},
30+
);
31+
32+
export default config;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Hono } from 'hono';
2+
import { sentry } from '@sentry/hono/bun';
3+
import { addRoutes } from './routes';
4+
5+
const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>();
6+
7+
app.use(
8+
// @ts-expect-error - Env is not yet in type
9+
sentry(app, {
10+
dsn: process.env.E2E_TEST_DSN,
11+
environment: 'qa',
12+
tracesSampleRate: 1.0,
13+
tunnel: 'http://localhost:3031/',
14+
}),
15+
);
16+
17+
addRoutes(app);
18+
19+
const port = Number(process.env.PORT || 38787);
20+
21+
export default {
22+
port,
23+
fetch: app.fetch,
24+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Hono } from 'hono';
2+
import { sentry } from '@sentry/hono/cloudflare';
3+
import { addRoutes } from './routes';
4+
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/',
13+
})),
14+
);
15+
16+
addRoutes(app);
17+
18+
export default app;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Hono } from 'hono';
2+
import { sentry } from '@sentry/hono/node';
3+
import { serve } from '@hono/node-server';
4+
import { addRoutes } from './routes';
5+
6+
const app = new Hono<{ Bindings: { E2E_TEST_DSN: string } }>();
7+
8+
app.use(
9+
// @ts-expect-error - Env is not yet in type
10+
sentry(app, {
11+
dsn: process.env.E2E_TEST_DSN,
12+
environment: 'qa',
13+
tracesSampleRate: 1.0,
14+
tunnel: 'http://localhost:3031/',
15+
}),
16+
);
17+
18+
addRoutes(app);
19+
20+
const port = Number(process.env.PORT || 38787);
21+
22+
serve({ fetch: app.fetch, port }, () => {
23+
console.log(`Hono (Node) listening on port ${port}`);
24+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Hono } from 'hono';
2+
import { HTTPException } from 'hono/http-exception';
3+
4+
export function addRoutes(app: Hono<{ Bindings: { E2E_TEST_DSN: string } }>): void {
5+
app.get('/', c => {
6+
return c.text('Hello Hono!');
7+
});
8+
9+
app.get('/test-param/:paramId', c => {
10+
return c.json({ paramId: c.req.param('paramId') });
11+
});
12+
13+
app.get('/error/:cause', c => {
14+
throw new Error('This is a test error for Sentry!', {
15+
cause: c.req.param('cause'),
16+
});
17+
});
18+
19+
app.get('/http-exception/:code', c => {
20+
// oxlint-disable-next-line typescript/no-explicit-any
21+
const code = Number(c.req.param('code')) as any;
22+
throw new HTTPException(code, { message: `HTTPException ${code}` });
23+
});
24+
}
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',
6+
});

0 commit comments

Comments
 (0)