diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json new file mode 100644 index 000000000000..b9667aeef85f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/package.json @@ -0,0 +1,29 @@ +{ + "name": "cloudflare-hono", + "scripts": { + "dev": "wrangler dev", + "build": "wrangler deploy --dry-run --var E2E_TEST_DSN=$E2E_TEST_DSN", + "test": "vitest", + "typecheck": "tsc --noEmit", + "cf-typegen": "wrangler types --env-interface CloudflareBindings", + "test:build": "pnpm install && pnpm build", + "//": "Just checking if it builds correctly and types don't break", + "test:assert": "pnpm typecheck" + }, + "dependencies": { + "@sentry/cloudflare": "latest || *", + "hono": "4.7.10" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.8.31", + "@cloudflare/workers-types": "^4.20250521.0", + "vitest": "3.1.0", + "wrangler": "^4.16.0" + }, + "volta": { + "extends": "../../package.json" + }, + "sentryTest": { + "optional": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/env.d.ts b/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/env.d.ts new file mode 100644 index 000000000000..0c9e04919e42 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/env.d.ts @@ -0,0 +1,6 @@ +// Generated by Wrangler on Mon Jul 29 2024 21:44:31 GMT-0400 (Eastern Daylight Time) +// by running `wrangler types` + +interface Env { + E2E_TEST_DSN: ''; +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/index.ts b/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/index.ts new file mode 100644 index 000000000000..7cd667c72408 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/src/index.ts @@ -0,0 +1,34 @@ +import { Hono } from 'hono'; +import * as Sentry from '@sentry/cloudflare'; + +const app = new Hono(); + +app.get('/', ctx => { + return ctx.json({ message: 'Welcome to Hono API' }); +}); + +app.get('/hello/:name', ctx => { + const name = ctx.req.param('name'); + return ctx.json({ message: `Hello, ${name}!` }); +}); + +app.get('/error', () => { + throw new Error('This is a test error'); +}); + +app.onError((err, ctx) => { + console.error(`Error occured: ${err.message}`); + return ctx.json({ error: err.message }, 500); +}); + +app.notFound(ctx => { + return ctx.json({ message: 'Not Found' }, 404); +}); + +export default Sentry.withSentry( + (env: Env) => ({ + dsn: env?.E2E_TEST_DSN, + tracesSampleRate: 1.0, + }), + app, +); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/env.d.ts b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/env.d.ts new file mode 100644 index 000000000000..3b9f82b9628f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/env.d.ts @@ -0,0 +1,4 @@ +declare module 'cloudflare:test' { + // ProvidedEnv controls the type of `import("cloudflare:test").env` + interface ProvidedEnv extends Env {} +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/index.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/index.test.ts new file mode 100644 index 000000000000..2ae93f9b1fd5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/index.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from 'vitest'; +import app from '../src/index'; +import { SELF, createExecutionContext, env, waitOnExecutionContext } from 'cloudflare:test'; + +describe('Hono app on Cloudflare Workers', () => { + describe('Unit Tests', () => { + it('should return welcome message', async () => { + const res = await app.request('/', {}, env); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data).toEqual({ message: 'Welcome to Hono API' }); + }); + + it('should greet a user with their name', async () => { + const res = await app.request('/hello/tester', {}, env); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data).toEqual({ message: 'Hello, tester!' }); + }); + + it('should handle errors with custom error handler', async () => { + const res = await app.request('/error', {}, env); + expect(res.status).toBe(500); + const data = await res.json(); + expect(data).toHaveProperty('error', 'This is a test error'); + }); + + it('should handle 404 with custom not found handler', async () => { + const res = await app.request('/non-existent-route', {}, env); + expect(res.status).toBe(404); + const data = await res.json(); + expect(data).toEqual({ message: 'Not Found' }); + }); + }); + + // Integration test style with worker.fetch + describe('Integration Tests', () => { + it('should fetch the root endpoint', async () => { + // Create request and context + const request = new Request('http://localhost/'); + const ctx = createExecutionContext(); + + const response = await app.fetch(request, env, ctx); + + await waitOnExecutionContext(ctx); + + expect(response.status).toBe(200); + const data = await response.json(); + expect(data).toEqual({ message: 'Welcome to Hono API' }); + }); + + it('should handle a parameter route', async () => { + // Create request and context + const request = new Request('http://localhost/hello/cloudflare'); + const ctx = createExecutionContext(); + + const response = await app.fetch(request, env, ctx); + + await waitOnExecutionContext(ctx); + + expect(response.status).toBe(200); + const data = await response.json(); + expect(data).toEqual({ message: 'Hello, cloudflare!' }); + }); + + it('should handle errors gracefully', async () => { + const response = await SELF.fetch('http://localhost/error'); + + expect(response.status).toBe(500); + const data = await response.json(); + expect(data).toHaveProperty('error', 'This is a test error'); + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/tsconfig.json b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/tsconfig.json new file mode 100644 index 000000000000..bc019a7e2bfb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers"] + }, + "include": ["./**/*.ts", "../src/env.d.ts"], + "exclude": [] +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/tsconfig.json b/dev-packages/e2e-tests/test-applications/cloudflare-hono/tsconfig.json new file mode 100644 index 000000000000..e2025cec5039 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "lib": [ + "ESNext" + ], + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" + }, + "include": ["src/**/*"], + "exclude": ["test", "node_modules"] +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/vitest.config.ts b/dev-packages/e2e-tests/test-applications/cloudflare-hono/vitest.config.ts new file mode 100644 index 000000000000..4466287fbe5b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineWorkersProject } from '@cloudflare/vitest-pool-workers/config' + +export default defineWorkersProject(() => { + return { + test: { + globals: true, + poolOptions: { + workers: { wrangler: { configPath: './wrangler.toml' } }, + }, + }, + } +}) diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-hono/wrangler.toml b/dev-packages/e2e-tests/test-applications/cloudflare-hono/wrangler.toml new file mode 100644 index 000000000000..9fdfb60c18b9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-hono/wrangler.toml @@ -0,0 +1,7 @@ +name = "cloudflare-hono" +main = "src/index.ts" +compatibility_date = "2023-10-30" +compatibility_flags = ["nodejs_compat"] + +# [vars] +# E2E_TEST_DSN = ""