-
Notifications
You must be signed in to change notification settings - Fork 793
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add support for testing Pages Functions (#5900)
Co-authored-by: bcoll <bcoll@cloudflare.com>
- Loading branch information
1 parent
53f22a0
commit 5bf0a6b
Showing
31 changed files
with
682 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@cloudflare/vitest-pool-workers": minor | ||
--- | ||
|
||
feature: add support for testing Pages Functions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
declare module "cloudflare:test" { | ||
interface ProvidedEnv { | ||
ASSETS?: Fetcher; | ||
KV_NAMESPACE: KVNamespace; | ||
OTHER_OBJECT: DurableObjectNamespace; | ||
} | ||
} |
184 changes: 184 additions & 0 deletions
184
fixtures/vitest-pool-workers-examples/misc/test/pages-functions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import { | ||
createPagesEventContext, | ||
env, | ||
ProvidedEnv, | ||
waitOnExecutionContext, | ||
} from "cloudflare:test"; | ||
import { expect, it, onTestFinished } from "vitest"; | ||
|
||
// This will improve in the next major version of `@cloudflare/workers-types`, | ||
// but for now you'll need to do something like this to get a correctly-typed | ||
// `Request` to pass to `createPagesEventContext()`. | ||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>; | ||
|
||
type BareFunction = PagesFunction<ProvidedEnv, never, Record<string, never>>; | ||
|
||
it("can consume body in middleware and in next request", async () => { | ||
const fn: BareFunction = async (ctx) => { | ||
const requestText = await ctx.request.text(); | ||
const nextResponse = await ctx.next(); | ||
const nextResponseText = await nextResponse.text(); | ||
return Response.json({ requestText, nextResponseText }); | ||
}; | ||
const request = new IncomingRequest("https://example.com", { | ||
method: "POST", | ||
body: "body", | ||
}); | ||
const ctx = createPagesEventContext<typeof fn>({ | ||
request, | ||
async next(nextRequest) { | ||
const nextRequestText = await nextRequest.text(); | ||
return new Response(nextRequestText); | ||
}, | ||
}); | ||
const response = await fn(ctx); | ||
await waitOnExecutionContext(ctx); | ||
expect(await response.json()).toStrictEqual({ | ||
requestText: "body", | ||
nextResponseText: "body", | ||
}); | ||
}); | ||
|
||
it("can rewrite to absolute and relative urls in next", async () => { | ||
const fn: BareFunction = async (ctx) => { | ||
const { pathname } = new URL(ctx.request.url); | ||
if (pathname === "/absolute") { | ||
return ctx.next("https://example.com/new-absolute", { method: "PUT" }); | ||
} else if (pathname === "/relative/") { | ||
return ctx.next("./new", { method: "PATCH" }); | ||
} else { | ||
return new Response(null, { status: 404 }); | ||
} | ||
}; | ||
|
||
// Check with absolute rewrite | ||
let request = new IncomingRequest("https://example.com/absolute"); | ||
let ctx = createPagesEventContext<typeof fn>({ | ||
request, | ||
async next(nextRequest) { | ||
return new Response(`next:${nextRequest.method} ${nextRequest.url}`); | ||
}, | ||
}); | ||
let response = await fn(ctx); | ||
await waitOnExecutionContext(ctx); | ||
expect(await response.text()).toBe( | ||
"next:PUT https://example.com/new-absolute" | ||
); | ||
|
||
// Check with relative rewrite | ||
request = new IncomingRequest("https://example.com/relative/"); | ||
ctx = createPagesEventContext<typeof fn>({ | ||
request, | ||
async next(nextRequest) { | ||
return new Response(`next:${nextRequest.method} ${nextRequest.url}`); | ||
}, | ||
}); | ||
response = await fn(ctx); | ||
await waitOnExecutionContext(ctx); | ||
expect(await response.text()).toBe( | ||
"next:PATCH https://example.com/relative/new" | ||
); | ||
}); | ||
|
||
it("requires next property to call next()", async () => { | ||
const fn: BareFunction = (ctx) => ctx.next(); | ||
const request = new IncomingRequest("https://example.com"); | ||
const ctx = createPagesEventContext<typeof fn>({ request }); | ||
expect(fn(ctx)).rejects.toThrowErrorMatchingInlineSnapshot( | ||
`[TypeError: Cannot call \`EventContext#next()\` without including \`next\` property in 2nd argument to \`createPagesEventContext()\`]` | ||
); | ||
}); | ||
|
||
it("requires ASSETS service binding", async () => { | ||
let originalASSETS = env.ASSETS; | ||
onTestFinished(() => { | ||
env.ASSETS = originalASSETS; | ||
}); | ||
delete env.ASSETS; | ||
|
||
const request = new IncomingRequest("https://example.com", { | ||
method: "POST", | ||
body: "body", | ||
}); | ||
expect(() => | ||
createPagesEventContext<BareFunction>({ request }) | ||
).toThrowErrorMatchingInlineSnapshot( | ||
`[TypeError: Cannot call \`createPagesEventContext()\` without defining \`ASSETS\` service binding]` | ||
); | ||
}); | ||
|
||
it("waits for waitUntil()ed promises", async () => { | ||
const fn: BareFunction = (ctx) => { | ||
ctx.waitUntil(ctx.env.KV_NAMESPACE.put("key", "value")); | ||
return new Response(); | ||
}; | ||
const request = new IncomingRequest("https://example.com"); | ||
const ctx = createPagesEventContext<typeof fn>({ request }); | ||
await fn(ctx); | ||
await waitOnExecutionContext(ctx); | ||
expect(await env.KV_NAMESPACE.get("key")).toBe("value"); | ||
}); | ||
|
||
it("correctly types parameters", async () => { | ||
const request = new IncomingRequest("https://example.com"); | ||
|
||
// Check no params and no data required | ||
{ | ||
type Fn = PagesFunction<ProvidedEnv, never, Record<string, never>>; | ||
createPagesEventContext<Fn>({ request }); | ||
createPagesEventContext<Fn>({ request, params: {} }); | ||
// @ts-expect-error no params required | ||
createPagesEventContext<Fn>({ request, params: { a: "1" } }); | ||
createPagesEventContext<Fn>({ request, data: {} }); | ||
// @ts-expect-error no data required | ||
createPagesEventContext<Fn>({ request, data: { b: "1" } }); | ||
} | ||
|
||
// Check no params but data required | ||
{ | ||
type Fn = PagesFunction<ProvidedEnv, never, { b: string }>; | ||
// @ts-expect-error data required | ||
createPagesEventContext<Fn>({ request }); | ||
// @ts-expect-error data required | ||
createPagesEventContext<Fn>({ request, params: {} }); | ||
// @ts-expect-error no params but data required | ||
createPagesEventContext<Fn>({ request, params: { a: "1" } }); | ||
// @ts-expect-error data required | ||
createPagesEventContext<Fn>({ request, data: {} }); | ||
createPagesEventContext<Fn>({ request, data: { b: "1" } }); | ||
} | ||
|
||
// Check no data but params required | ||
{ | ||
type Fn = PagesFunction<ProvidedEnv, "a", Record<string, never>>; | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request }); | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request, params: {} }); | ||
createPagesEventContext<Fn>({ request, params: { a: ["1"] } }); | ||
// @ts-expect-error no data but params required | ||
createPagesEventContext<Fn>({ request, data: {} }); | ||
// @ts-expect-error no data but params required | ||
createPagesEventContext<Fn>({ request, data: { b: "1" } }); | ||
} | ||
|
||
// Check params and data required | ||
{ | ||
type Fn = PagesFunction<ProvidedEnv, "a", { b: string }>; | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request }); | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request, params: {} }); | ||
// @ts-expect-error data required | ||
createPagesEventContext<Fn>({ request, params: { a: "1" } }); | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request, data: {} }); | ||
// @ts-expect-error params required | ||
createPagesEventContext<Fn>({ request, data: { b: "1" } }); | ||
createPagesEventContext<Fn>({ | ||
request, | ||
params: { a: "1" }, | ||
data: { b: "1" }, | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
dist-functions |
8 changes: 8 additions & 0 deletions
8
...es/vitest-pool-workers-examples/pages-functions-unit-integration-self/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# ⚡️ pages-functions-unit-integration-self | ||
|
||
This project uses Pages Functions. Integration tests dispatch events using the `SELF` helper from the `cloudflare:test` module. Unit tests call handler functions directly. [`global-setup.ts`](global-setup.ts) builds Pages Functions into a Worker for integration testing, watching for changes. | ||
|
||
| Test | Overview | | ||
| --------------------------------------------------------- | ------------------------------------------------------------- | | ||
| [integration-self.test.ts](test/integration-self.test.ts) | Basic `fetch` integration test using `SELF` **(recommended)** | | ||
| [unit.test.ts](test/unit.test.ts) | Basic unit test calling `worker.fetch()` directly | |
11 changes: 11 additions & 0 deletions
11
...-pool-workers-examples/pages-functions-unit-integration-self/functions/api/_middleware.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Add data to the request and make all bodies uppercase | ||
export const onRequest: PagesFunction< | ||
Env, | ||
never, | ||
Data | Record<string, never> | ||
> = async (ctx) => { | ||
ctx.data = { user: "ada" }; | ||
const response = await ctx.next(); | ||
const text = await response.text(); | ||
return new Response(text.toUpperCase(), response); | ||
}; |
11 changes: 11 additions & 0 deletions
11
...est-pool-workers-examples/pages-functions-unit-integration-self/functions/api/kv/[key].ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export const onRequestGet: PagesFunction<Env, "key", Data> = async (ctx) => { | ||
const key = `${ctx.data.user}:${ctx.params.key}`; | ||
const value = await ctx.env.KV_NAMESPACE.get(key, "stream"); | ||
return new Response(value, { status: value === null ? 204 : 200 }); | ||
}; | ||
|
||
export const onRequestPut: PagesFunction<Env, "key", Data> = async (ctx) => { | ||
const key = `${ctx.data.user}:${ctx.params.key}`; | ||
await ctx.env.KV_NAMESPACE.put(key, ctx.request.body ?? ""); | ||
return new Response(null, { status: 204 }); | ||
}; |
3 changes: 3 additions & 0 deletions
3
.../vitest-pool-workers-examples/pages-functions-unit-integration-self/functions/api/ping.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const onRequest: PagesFunction<Env, never, Data> = ({ request }) => { | ||
return new Response(`${request.method} pong`); | ||
}; |
6 changes: 6 additions & 0 deletions
6
...res/vitest-pool-workers-examples/pages-functions-unit-integration-self/functions/env.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
interface Env { | ||
KV_NAMESPACE: KVNamespace; | ||
ASSETS: Fetcher; | ||
} | ||
|
||
type Data = { user: string }; |
4 changes: 4 additions & 0 deletions
4
...itest-pool-workers-examples/pages-functions-unit-integration-self/functions/tsconfig.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "../../tsconfig.workerd.json", | ||
"include": ["./**/*.ts"] | ||
} |
25 changes: 25 additions & 0 deletions
25
fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self/global-setup.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import childProcess from "node:child_process"; | ||
import events from "node:events"; | ||
|
||
// Global setup runs inside Node.js, not `workerd` | ||
export default async function () { | ||
console.log( | ||
"Building pages-functions-unit-integration-self and watching for changes..." | ||
); | ||
|
||
// Not building to `dist` here as Vitest ignores changes in `dist` by default | ||
const buildProcess = childProcess.spawn( | ||
"wrangler pages functions build --outdir dist-functions --watch", | ||
{ cwd: __dirname, shell: true } | ||
); | ||
buildProcess.stdout.pipe(process.stdout); | ||
buildProcess.stderr.pipe(process.stderr); | ||
|
||
// Wait for first build | ||
await events.once(buildProcess.stdout, "data"); | ||
|
||
// Stop watching for changes on teardown | ||
return () => { | ||
buildProcess.kill(); | ||
}; | ||
} |
1 change: 1 addition & 0 deletions
1
fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self/public/404.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<p>Not found 😭</p> |
4 changes: 4 additions & 0 deletions
4
fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self/public/_headers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/secure | ||
X-Frame-Options: DENY | ||
X-Content-Type-Options: nosniff | ||
Referrer-Policy: no-referrer |
1 change: 1 addition & 0 deletions
1
...ures/vitest-pool-workers-examples/pages-functions-unit-integration-self/public/_redirects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/take-me-home / 302 |
1 change: 1 addition & 0 deletions
1
...ures/vitest-pool-workers-examples/pages-functions-unit-integration-self/public/index.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<p>Homepage 🏡</p> |
1 change: 1 addition & 0 deletions
1
...res/vitest-pool-workers-examples/pages-functions-unit-integration-self/public/secure.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<p>Secure page 🔐</p> |
4 changes: 4 additions & 0 deletions
4
fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self/test/env.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
declare module "cloudflare:test" { | ||
// Controls the type of `import("cloudflare:test").env` | ||
interface ProvidedEnv extends Env {} | ||
} |
Oops, something went wrong.