Skip to content

Commit f9a3e32

Browse files
committed
Add Puppeteer example
1 parent e5037b9 commit f9a3e32

File tree

13 files changed

+697
-3
lines changed

13 files changed

+697
-3
lines changed

fixtures/vitest-pool-workers-examples/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
"scripts": {
66
"check:type": "node tsc-all.mjs",
77
"list": "vitest --config vitest.workers.config.ts list",
8+
"puppeteer:initialize": "pnpx puppeteer browsers install chrome",
9+
"pretest": "npm run puppeteer:initialize",
810
"test": "vitest --config vitest.workers.config.ts --reporter basic",
11+
"pretest:ci": "npm run puppeteer:initialize",
912
"test:ci": "run-script-os",
1013
"test:ci:default": "vitest run --config vitest.workers.config.ts --reporter basic",
1114
"test:ci:win32": "vitest run --config vitest.workers.config.ts --reporter basic --exclude test/sqlite-in-do.test.ts"
1215
},
1316
"devDependencies": {
17+
"@cloudflare/puppeteer": "^0.0.14",
1418
"@cloudflare/vitest-pool-workers": "workspace:*",
1519
"@cloudflare/workers-types": "^4.20241004.0",
1620
"@types/node": "20.8.3",
1721
"ext-dep": "file:./internal-module-resolution/vendor/ext-dep",
1822
"jose": "^5.2.2",
1923
"miniflare": "workspace:*",
24+
"puppeteer": "^23.5.3",
2025
"run-script-os": "^1.1.6",
2126
"toucan-js": "^3.3.1",
2227
"typescript": "^5.5.2",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# ✅ Puppeteer
2+
3+
This Worker contains tests which are run with [Puppeteer](https://pptr.dev/). This is a useful tool for testing applications which use [static assets](https://developers.cloudflare.com/workers/static-assets/), (in particular, [full-stack frameworks](https://developers.cloudflare.com/workers/frameworks/)).
4+
5+
| Test | Overview |
6+
| --------------------------------------------- | ----------------------------------------------------------------- |
7+
| [puppeteer.test.ts](./test/globalSetup.ts) | A setup and teardown file for initializing the Puppeteer browser. |
8+
| [puppeteer.test.ts](./test/puppeteer.test.ts) | A test using Puppeteer and `SELF` dispatches. |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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>404 Not Found</title>
7+
</head>
8+
<body>
9+
<h1>404 Not Found</h1>
10+
</body>
11+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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>Blog | Hello World</title>
7+
</head>
8+
<body>
9+
<h1>Blog | Hello World</h1>
10+
<p>This is a blog post.</p>
11+
</body>
12+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export function greet(request: Request): string {
2+
return `👋 ${request.url}`;
3+
}
4+
5+
export default {
6+
async fetch(request, env, ctx) {
7+
const url = new URL(request.url);
8+
9+
if (url.pathname === "/api/date") {
10+
return new Response(new Date().toISOString());
11+
}
12+
13+
if (url.pathname === "/binding") {
14+
const response = await env.ASSETS.fetch(request);
15+
return new HTMLRewriter()
16+
.on("h1", {
17+
element(element) {
18+
element.setInnerContent("Intercept!");
19+
},
20+
})
21+
.transform(response);
22+
}
23+
24+
return env.ASSETS.fetch(request);
25+
},
26+
} satisfies ExportedHandler<{ ASSETS: Fetcher }>;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.workerd.json",
3+
"include": ["./**/*.ts"]
4+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import puppeteer, { Browser } from "puppeteer";
2+
import type { GlobalSetupContext } from "vitest/node";
3+
4+
let browser: Browser;
5+
6+
export default async function setup({ provide }: GlobalSetupContext) {
7+
browser = await puppeteer.launch({
8+
// args: [`--no-sandbox`, `--disable-setuid-sandbox`], // DISABLING THESE SANDBOXES IS PROBABLY NOT REQUIRED IN YOUR PROJECT
9+
});
10+
provide("browserWSEndpoint", browser.wsEndpoint());
11+
}
12+
13+
export async function teardown() {
14+
await browser.close();
15+
}
16+
17+
declare module "vitest" {
18+
export interface ProvidedContext {
19+
browserWSEndpoint: string;
20+
}
21+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import puppeteer from "@cloudflare/puppeteer";
2+
import { SELF } from "cloudflare:test";
3+
import { beforeAll, describe, expect, inject, it } from "vitest";
4+
import type { Browser, HTTPRequest } from "@cloudflare/puppeteer";
5+
6+
const interceptRequest = async (request: HTTPRequest) => {
7+
const miniflareRequest = new Request(request.url(), {
8+
method: request.method(),
9+
body: request.postData(),
10+
});
11+
const response = await SELF.fetch(miniflareRequest);
12+
const arrayBuffer = await response.arrayBuffer();
13+
14+
await request.respond({
15+
body: Buffer.from(arrayBuffer),
16+
headers: Object.fromEntries(response.headers.entries()),
17+
status: response.status,
18+
});
19+
};
20+
21+
describe("Puppeteer", () => {
22+
let browser: Browser;
23+
24+
beforeAll(async () => {
25+
browser = await puppeteer.connect({
26+
browserWSEndpoint: inject("browserWSEndpoint"),
27+
});
28+
});
29+
30+
it("can fetch static assets", async () => {
31+
const page = await browser.newPage();
32+
33+
page.setRequestInterception(true);
34+
page.on("request", interceptRequest);
35+
36+
await page.goto("http://fakehost/blog/hello-world");
37+
38+
const contentSelector = await page.locator("text/blog post").waitHandle();
39+
const content = await contentSelector?.evaluate((el) => el.textContent);
40+
expect(content).toMatchInlineSnapshot(`"This is a blog post."`);
41+
});
42+
43+
it("can fetch a Worker", async () => {
44+
const page = await browser.newPage();
45+
46+
page.setRequestInterception(true);
47+
page.on("request", interceptRequest);
48+
49+
await page.goto("http://fakehost/api/date");
50+
51+
expect(await page.content()).toMatch(
52+
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/
53+
);
54+
55+
// Alternatively...
56+
57+
const response = await SELF.fetch("http://fakehost/api/date");
58+
59+
expect(await response.text()).toMatch(
60+
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/
61+
);
62+
});
63+
64+
it("can fetch a Worker which binds to assets", async () => {
65+
const page = await browser.newPage();
66+
67+
page.setRequestInterception(true);
68+
page.on("request", interceptRequest);
69+
70+
await page.goto("http://fakehost/intercept");
71+
72+
const contentSelector = await page.locator("text/blog post").waitHandle();
73+
const content = await contentSelector?.evaluate((el) => el.textContent);
74+
expect(content).toMatchInlineSnapshot(`"This is a blog post."`);
75+
76+
const titleSelector = await page.locator("text/Intercept").waitHandle();
77+
const title = await titleSelector?.evaluate((el) => el.textContent);
78+
expect(title).toMatchInlineSnapshot(`"Intercept!"`);
79+
});
80+
81+
it("can fetch and 404 correctly", async () => {
82+
const page = await browser.newPage();
83+
84+
page.setRequestInterception(true);
85+
page.on("request", interceptRequest);
86+
87+
await page.goto("http://fakehost/non-existent");
88+
89+
const titleSelector = await page.locator("text/404").waitHandle();
90+
const title = await titleSelector?.evaluate((el) => el.textContent);
91+
expect(title).toMatchInlineSnapshot(`"404 Not Found"`);
92+
});
93+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.workerd-test.json",
3+
"include": ["./**/*.ts"]
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../tsconfig.node.json",
3+
"include": ["./*.ts"]
4+
}

0 commit comments

Comments
 (0)