70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
904 lines
27 KiB
TypeScript
904 lines
27 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { getTodaysCompatDate } from "@cloudflare/workers-utils";
|
|
import getPort from "get-port";
|
|
import dedent from "ts-dedent";
|
|
import { fetch } from "undici";
|
|
import { describe, it } from "vitest";
|
|
import { WranglerE2ETestHelper } from "./helpers/e2e-wrangler-test";
|
|
import { fetchText } from "./helpers/fetch-text";
|
|
import { normalizeOutput } from "./helpers/normalize";
|
|
import { waitFor } from "./helpers/wait-for";
|
|
|
|
const port = await getPort();
|
|
const inspectorPort = await getPort();
|
|
const cmd = "wrangler pages dev";
|
|
describe.sequential("wrangler pages dev", () => {
|
|
it("should warn if no [--compatibility_date] command line arg was specified", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Testing [--compatibility_date]")
|
|
}
|
|
}`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const currentDate = getTodaysCompatDate();
|
|
const output = worker.currentOutput.replaceAll(
|
|
currentDate,
|
|
"<current-date>"
|
|
);
|
|
expect(output).toContain(
|
|
`No compatibility_date was specified. Using today's date: <current-date>.`
|
|
);
|
|
expect(output).toContain(
|
|
`❯❯ Add one to your Wrangler configuration file: compatibility_date = "<current-date>", or`
|
|
);
|
|
expect(output).toContain(
|
|
`❯❯ Pass it in your terminal: wrangler pages dev [<DIRECTORY>] --compatibility-date=<current-date>`
|
|
);
|
|
|
|
const text = await fetchText(url);
|
|
expect(text).toBe("Testing [--compatibility_date]");
|
|
});
|
|
|
|
it("should warn that [--experimental-local] is no longer required, if specified", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} . --experimental-local`
|
|
);
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Testing [--experimental-local]")
|
|
}
|
|
}`,
|
|
});
|
|
const { url } = await worker.waitForReady();
|
|
const text = await fetchText(url);
|
|
expect(text).toBe("Testing [--experimental-local]");
|
|
await waitFor(() =>
|
|
expect(worker.currentOutput).toContain(
|
|
`--experimental-local is no longer required and will be removed in a future version`
|
|
)
|
|
);
|
|
});
|
|
|
|
it("should show [--service] related warnings if specified as arg in the command line", async () => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} . --service STAGING_SERVICE=test-worker@staging`
|
|
);
|
|
|
|
await worker.readUntil(
|
|
/Support for service binding environments is experimental/
|
|
);
|
|
});
|
|
|
|
it("should warn if bindings specified as args in the command line are invalid", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --inspector-port ${inspectorPort} . --port ${port} --service test --kv = --do test --d1 = --r2 =`
|
|
);
|
|
await worker.waitForReady();
|
|
await waitFor(() => {
|
|
expect(worker.currentOutput).toContain(
|
|
`Could not parse Service binding: test`
|
|
);
|
|
expect(worker.currentOutput).toContain(`Could not parse KV binding: =`);
|
|
expect(worker.currentOutput).toContain(
|
|
`Could not parse Durable Object binding: test`
|
|
);
|
|
expect(worker.currentOutput).toContain(`Could not parse R2 binding: =`);
|
|
expect(worker.currentOutput).toContain(`Could not parse D1 binding: =`);
|
|
});
|
|
});
|
|
|
|
it("should use bindings specified as args in the command line", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Hello world")
|
|
}
|
|
}`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --inspector-port ${inspectorPort} . --port ${port} --service TEST_SERVICE=test-worker --kv TEST_KV --do TEST_DO=TestDurableObject@a --d1 TEST_D1 --r2 TEST_R2 --compatibility-date=2025-05-21`
|
|
);
|
|
await worker.waitForReady();
|
|
|
|
await waitFor(() =>
|
|
expect(worker.currentOutput).toContain(
|
|
"Your Worker has access to the following bindings:"
|
|
)
|
|
);
|
|
|
|
const bindingMessages = worker.currentOutput.split(
|
|
"Your Worker has access to the following bindings:"
|
|
);
|
|
const bindings = Array.from(
|
|
(bindingMessages[1] ?? "").matchAll(/env\.[^\n]+/g)
|
|
)
|
|
.flat()
|
|
.map((line) =>
|
|
// Normalize the CF_PAGES_URL which contains a generated project name
|
|
line
|
|
.replace(
|
|
/https:\/\/[a-f0-9]+\.wrangler-smoke-[^.]+/,
|
|
"https://<HASH>.<PROJECT_NAME>"
|
|
)
|
|
.replaceAll(/\s+/g, " ")
|
|
);
|
|
expect(bindings).toMatchInlineSnapshot(`
|
|
[
|
|
"env.TEST_DO (TestDurableObject, defined in a) Durable Object local [not connected]",
|
|
"env.TEST_KV (TEST_KV) KV Namespace local",
|
|
"env.TEST_D1 (local-TEST_D1) D1 Database local",
|
|
"env.TEST_R2 (TEST_R2) R2 Bucket local",
|
|
"env.TEST_SERVICE (test-worker) Worker local [not connected]",
|
|
"env.CF_PAGES ("1") Environment Variable local",
|
|
"env.CF_PAGES_BRANCH ("local") Environment Variable local",
|
|
"env.CF_PAGES_COMMIT_SHA ("0000000000000000000000000000000000000...") Environment Variable local",
|
|
"env.CF_PAGES_URL ("https://<HASH>.<PROJECT_NAME>...") Environment Variable local",
|
|
]
|
|
`);
|
|
});
|
|
|
|
it("should support wrangler.toml", async ({ expect }) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"public/_worker.js": dedent`
|
|
export default {
|
|
async fetch(request, env) {
|
|
return new Response("Pages supports wrangler.toml ⚡️⚡️")
|
|
}
|
|
}`,
|
|
"wrangler.toml": dedent`
|
|
name = "pages-project"
|
|
pages_build_output_dir = "public"
|
|
compatibility_date = "2023-01-01"
|
|
`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort}`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const text = await fetchText(url);
|
|
expect(text).toBe("Pages supports wrangler.toml ⚡️⚡️");
|
|
});
|
|
|
|
it("should recover from syntax error during dev session (_worker)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(`${cmd} .`);
|
|
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Hello World!")
|
|
}
|
|
}`,
|
|
});
|
|
|
|
const { url } = await worker.waitForReady();
|
|
|
|
await expect(fetch(url).then((r) => r.text())).resolves.toBe(
|
|
"Hello World!"
|
|
);
|
|
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Updated Worker!")
|
|
} // Syntax Error
|
|
}
|
|
}`,
|
|
});
|
|
|
|
await worker.readUntil(/Failed to build/, 5_000);
|
|
|
|
// And then make sure Wrangler hasn't crashed
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Updated Worker!")
|
|
}
|
|
}`,
|
|
});
|
|
await worker.waitForReload();
|
|
|
|
await expect(fetch(url).then((r) => r.text())).resolves.toBe(
|
|
"Updated Worker!"
|
|
);
|
|
});
|
|
|
|
it("should recover from syntax error during dev session (Functions)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
|
|
await helper.seed({
|
|
"functions/_middleware.js": dedent`
|
|
export async function onRequest() {
|
|
return new Response("Hello World!")
|
|
}`,
|
|
});
|
|
|
|
const { url } = await worker.waitForReady();
|
|
|
|
await expect(fetch(url).then((r) => r.text())).resolves.toBe(
|
|
"Hello World!"
|
|
);
|
|
|
|
await helper.seed({
|
|
"functions/_middleware.js": dedent`
|
|
export async function onRequest() {
|
|
return new Response("Updated Worker!")
|
|
} // Syntax Error
|
|
}`,
|
|
});
|
|
|
|
await worker.readUntil(/Failed to build Functions/, 5_000);
|
|
|
|
// And then make sure Wrangler hasn't crashed
|
|
await helper.seed({
|
|
"functions/_middleware.js": dedent`
|
|
export async function onRequest() {
|
|
return new Response("Updated Worker!")
|
|
}`,
|
|
});
|
|
await worker.waitForReload();
|
|
|
|
await expect(fetch(url).then((r) => r.text())).resolves.toBe(
|
|
"Updated Worker!"
|
|
);
|
|
});
|
|
|
|
it("should validate _routes.json during dev session, and fallback to default value", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"functions/foo.ts": dedent`
|
|
export async function onRequest() {
|
|
return new Response("FOO");
|
|
}`,
|
|
"_routes.json": dedent`
|
|
{
|
|
"version": 1,
|
|
"include": ["/foo"],
|
|
"exclude": []
|
|
}
|
|
`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const foo = await fetchText(`${url}/foo`);
|
|
|
|
expect(foo).toBe("FOO");
|
|
|
|
// invalid _routes.json because include rule does not start with `/`
|
|
await helper.seed({
|
|
"_routes.json": dedent`
|
|
{
|
|
"version": 1,
|
|
"include": ["foo"],
|
|
"exclude": []
|
|
}
|
|
`,
|
|
});
|
|
|
|
await worker.readUntil(/FatalError: Invalid _routes.json file found/);
|
|
await worker.readUntil(/All rules must start with '\/'/);
|
|
});
|
|
|
|
it("should use top-level configuration specified in `wrangler.toml`", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort}`
|
|
);
|
|
await helper.seed({
|
|
"public/_worker.js": dedent`
|
|
export default {
|
|
async fetch(request, env) {
|
|
return new Response(env.PAGES + " " + "supports wrangler.toml")
|
|
}
|
|
}`,
|
|
"wrangler.toml": dedent`
|
|
name = "pages-project"
|
|
pages_build_output_dir = "public"
|
|
# commenting this out would result in a warning. If there is no "compatibility_date"
|
|
# related warning in stdout, then this value was picked up
|
|
compatibility_date = "2023-01-01"
|
|
|
|
[vars]
|
|
PAGES = "⚡️ Pages ⚡️"
|
|
|
|
[[kv_namespaces]]
|
|
binding = "KV_BINDING_TOML"
|
|
id = "KV_ID_TOML"
|
|
`,
|
|
});
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const text = await fetchText(url);
|
|
|
|
expect(text).toBe("⚡️ Pages ⚡️ supports wrangler.toml");
|
|
|
|
await worker.readUntil(/GET \/ 200 OK/);
|
|
|
|
await waitFor(() =>
|
|
expect(worker.currentOutput).toContain(
|
|
"Your Worker has access to the following bindings:"
|
|
)
|
|
);
|
|
|
|
expect(normalizeOutput(worker.currentOutput)).toMatchInlineSnapshot(`
|
|
"✨ Compiled Worker successfully
|
|
Your Worker has access to the following bindings:
|
|
Binding Resource Mode
|
|
env.KV_BINDING_TOML (KV_ID_TOML) KV Namespace local
|
|
env.CF_PAGES ("1") Environment Variable local
|
|
env.CF_PAGES_BRANCH ("local") Environment Variable local
|
|
env.CF_PAGES_COMMIT_SHA ("0000000000000000000000000000000000000...") Environment Variable local
|
|
env.CF_PAGES_URL ("https://00000000.pages-project.pages....") Environment Variable local
|
|
env.PAGES ("⚡️ Pages ⚡️") Environment Variable local
|
|
⎔ Starting local server...
|
|
[wrangler:info] Ready on http://<HOST>:<PORT>
|
|
[wrangler:info] GET / 200 OK (TIMINGS)"
|
|
`);
|
|
});
|
|
|
|
it("should merge (with override) `wrangler.toml` configuration with configuration provided via the command line, with command line args taking precedence", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
const flags = [
|
|
` --binding VAR1=NEW_VAR_1 VAR3=VAR_3_ARGS`,
|
|
` --kv KV_BINDING_1_TOML=NEW_KV_ID_1 KV_BINDING_3_ARGS=KV_ID_3_ARGS`,
|
|
` --do DO_BINDING_1_TOML=NEW_DO_1@NEW_DO_SCRIPT_1 DO_BINDING_3_ARGS=DO_3_ARGS@DO_SCRIPT_3_ARGS`,
|
|
` --d1 D1_BINDING_1_TOML=NEW_D1_NAME_1 D1_BINDING_3_ARGS=D1_NAME_3_ARGS`,
|
|
` --r2 R2_BINDING_1_TOML=new-r2-bucket-1 R2_BINDING_3_TOML=r2-bucket-3-args`,
|
|
` --service SERVICE_BINDING_1_TOML=NEW_SERVICE_NAME_1 SERVICE_BINDING_3_TOML=SERVICE_NAME_3_ARGS`,
|
|
];
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} ${flags.join("")}`
|
|
);
|
|
await helper.seed({
|
|
"public/_worker.js": dedent`
|
|
export default {
|
|
async fetch(request, env) {
|
|
return new Response("Pages supports wrangler.toml ⚡️")
|
|
}
|
|
}`,
|
|
"wrangler.toml": dedent`
|
|
name = "pages-project"
|
|
pages_build_output_dir = "public"
|
|
compatibility_date = "2023-01-01"
|
|
|
|
[vars]
|
|
VAR1 = "VAR_1_TOML" # to override
|
|
VAR2 = "VAR_2_TOML" # to merge
|
|
|
|
# to override
|
|
[[kv_namespaces]]
|
|
binding = "KV_BINDING_1_TOML"
|
|
id = "KV_ID_1_TOML"
|
|
|
|
# to merge as is
|
|
[[kv_namespaces]]
|
|
binding = "KV_BINDING_2_TOML"
|
|
id = "KV_ID_2_TOML"
|
|
|
|
# to override
|
|
[[durable_objects.bindings]]
|
|
name = "DO_BINDING_1_TOML"
|
|
class_name = "DO_1_TOML"
|
|
script_name = "DO_SCRIPT_1_TOML"
|
|
|
|
# to merge as is
|
|
[[durable_objects.bindings]]
|
|
name = "DO_BINDING_2_TOML"
|
|
class_name = "DO_2_TOML"
|
|
script_name = "DO_SCRIPT_2_TOML"
|
|
|
|
# to override
|
|
[[d1_databases]]
|
|
binding = "D1_BINDING_1_TOML"
|
|
database_id = "D1_ID_1_TOML"
|
|
database_name = "D1_NAME_1_TOML"
|
|
|
|
# to merge as is
|
|
[[d1_databases]]
|
|
binding = "D1_BINDING_2_TOML"
|
|
database_id = "D1_ID_2_TOML"
|
|
database_name = "D1_NAME_2_TOML"
|
|
|
|
# to override
|
|
[[r2_buckets]]
|
|
binding = 'R2_BINDING_1_TOML'
|
|
bucket_name = 'r2-bucket-1-toml'
|
|
|
|
# to merge as is
|
|
[[r2_buckets]]
|
|
binding = 'R2_BINDING_2_TOML'
|
|
bucket_name = 'r2-bucket-2-toml'
|
|
|
|
# to override
|
|
[[services]]
|
|
binding = "SERVICE_BINDING_1_TOML"
|
|
service = "SERVICE_NAME_1_TOML"
|
|
|
|
# to merge as is
|
|
[[services]]
|
|
binding = "SERVICE_BINDING_2_TOML"
|
|
service = "SERVICE_NAME_2_TOML"
|
|
`,
|
|
});
|
|
await worker.waitForReady();
|
|
|
|
await waitFor(() =>
|
|
expect(worker.currentOutput).toContain(
|
|
"Your Worker has access to the following bindings:"
|
|
)
|
|
);
|
|
|
|
// We only care about the list of bindings and warnings, so strip other output
|
|
const [prestartOutput] = normalizeOutput(worker.currentOutput).split(
|
|
"⎔ Starting local server..."
|
|
);
|
|
|
|
expect(prestartOutput).toMatchInlineSnapshot(`
|
|
"✨ Compiled Worker successfully
|
|
Your Worker has access to the following bindings:
|
|
Binding Resource Mode
|
|
env.DO_BINDING_1_TOML (NEW_DO_1, defined in NEW_DO_SCRIPT_1) Durable Object local [not connected]
|
|
env.DO_BINDING_2_TOML (DO_2_TOML, defined in DO_SCRIPT_2_TOML) Durable Object local [not connected]
|
|
env.DO_BINDING_3_ARGS (DO_3_ARGS, defined in DO_SCRIPT_3_ARGS) Durable Object local [not connected]
|
|
env.KV_BINDING_1_TOML (NEW_KV_ID_1) KV Namespace local
|
|
env.KV_BINDING_2_TOML (KV_ID_2_TOML) KV Namespace local
|
|
env.KV_BINDING_3_ARGS (KV_ID_3_ARGS) KV Namespace local
|
|
env.D1_BINDING_1_TOML (local-D1_BINDING_1_TOML=NEW_D1_NAME_1) D1 Database local
|
|
env.D1_BINDING_2_TOML (D1_NAME_2_TOML) D1 Database local
|
|
env.D1_BINDING_3_ARGS (local-D1_BINDING_3_ARGS=D1_NAME_3_ARGS) D1 Database local
|
|
env.R2_BINDING_1_TOML (new-r2-bucket-1) R2 Bucket local
|
|
env.R2_BINDING_2_TOML (r2-bucket-2-toml) R2 Bucket local
|
|
env.R2_BINDING_3_TOML (r2-bucket-3-args) R2 Bucket local
|
|
env.SERVICE_BINDING_1_TOML (NEW_SERVICE_NAME_1) Worker local [not connected]
|
|
env.SERVICE_BINDING_2_TOML (SERVICE_NAME_2_TOML) Worker local [not connected]
|
|
env.SERVICE_BINDING_3_TOML (SERVICE_NAME_3_ARGS) Worker local [not connected]
|
|
env.CF_PAGES ("1") Environment Variable local
|
|
env.CF_PAGES_BRANCH ("local") Environment Variable local
|
|
env.CF_PAGES_COMMIT_SHA ("0000000000000000000000000000000000000...") Environment Variable local
|
|
env.CF_PAGES_URL ("https://00000000.pages-project.pages....") Environment Variable local
|
|
env.VAR1 ("(hidden)") Environment Variable local
|
|
env.VAR2 ("VAR_2_TOML") Environment Variable local
|
|
env.VAR3 ("(hidden)") Environment Variable local
|
|
Service bindings, Durable Object bindings, and Tail consumers connect to other Wrangler or Vite dev processes running locally, with their connection status indicated by [connected] or [not connected]. For more details, refer to https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#local-development
|
|
"
|
|
`);
|
|
});
|
|
|
|
it("should pick up wrangler.toml configuration even in cases when `pages_build_output_dir` was not specified, but the <directory> command argument was", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"public/_worker.js": dedent`
|
|
export default {
|
|
async fetch(request, env) {
|
|
return new Response(env.PAGES_EMOJI + " Pages supports wrangler.toml" + " " + env.PAGES_EMOJI)
|
|
}
|
|
}`,
|
|
"wrangler.toml": dedent`
|
|
name = "pages-project"
|
|
compatibility_date = "2023-01-01"
|
|
|
|
[vars]
|
|
PAGES_EMOJI = "⚡️"
|
|
`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} public`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
await expect(fetchText(url)).resolves.toBe(
|
|
"⚡️ Pages supports wrangler.toml ⚡️"
|
|
);
|
|
});
|
|
|
|
it("should inject CF_PAGES environment variables", async ({ expect }) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return Response.json({
|
|
CF_PAGES: env.CF_PAGES,
|
|
CF_PAGES_BRANCH: env.CF_PAGES_BRANCH,
|
|
CF_PAGES_COMMIT_SHA: env.CF_PAGES_COMMIT_SHA,
|
|
CF_PAGES_URL: env.CF_PAGES_URL,
|
|
});
|
|
}
|
|
}`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const response = await fetch(url);
|
|
const data = (await response.json()) as Record<string, string>;
|
|
|
|
expect(data).toEqual({
|
|
CF_PAGES: "1",
|
|
CF_PAGES_BRANCH: expect.any(String),
|
|
CF_PAGES_COMMIT_SHA: expect.any(String),
|
|
CF_PAGES_URL: expect.stringMatching(
|
|
/^https:\/\/[a-f0-9]{8}\..*\.pages\.dev$/
|
|
),
|
|
});
|
|
});
|
|
|
|
it("should allow user to override CF_PAGES... environment variables", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response(env.CF_PAGES_BRANCH + " " + env.CF_PAGES_COMMIT_SHA);
|
|
}
|
|
}`,
|
|
"wrangler.toml": dedent`
|
|
name = "test-pages"
|
|
pages_build_output_dir = "."
|
|
compatibility_date = "2024-01-01"
|
|
|
|
[vars]
|
|
CF_PAGES_BRANCH = "custom-branch"
|
|
CF_PAGES_COMMIT_SHA = "custom-sha"
|
|
`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort}`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const text = await fetchText(url);
|
|
expect(text).toBe("custom-branch custom-sha");
|
|
});
|
|
|
|
describe("watch mode", () => {
|
|
it("should modify worker during dev session (Functions)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"functions/_middleware.js": dedent`
|
|
export async function onRequest() {
|
|
return new Response("Hello World!")
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let text = await fetchText(url);
|
|
expect(text).toBe("Hello World!");
|
|
|
|
await helper.seed({
|
|
"functions/_middleware.js": dedent`
|
|
export async function onRequest() {
|
|
return new Response("Updated Worker!")
|
|
}`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
text = await fetchText(url);
|
|
expect(text).toBe("Updated Worker!");
|
|
|
|
// Ensure Wrangler doesn't write tmp files in the functions directory—regression test for https://github.com/cloudflare/workers-sdk/issues/7440
|
|
expect(
|
|
existsSync(path.join(helper.tmpPath, "functions/.wrangler"))
|
|
).toBeFalsy();
|
|
});
|
|
|
|
it("should support modifying dependencies during dev session (Functions)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"utils/greetings.js": dedent`
|
|
export const hello = "Hello World!"
|
|
export const hi = "Hi there!"
|
|
`,
|
|
"functions/greetings/_middleware.js": dedent`
|
|
import { hello } from "../../utils/greetings"
|
|
export async function onRequest() {
|
|
return new Response(hello)
|
|
}`,
|
|
"functions/hi.js": dedent`
|
|
import { hi } from "../utils/greetings"
|
|
export async function onRequest() {
|
|
return new Response(hi)
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let hello = await fetchText(`${url}/greetings/hello`);
|
|
expect(hello).toBe("Hello World!");
|
|
|
|
let hi = await fetchText(`${url}/hi`);
|
|
expect(hi).toBe("Hi there!");
|
|
|
|
await helper.seed({
|
|
"utils/greetings.js": dedent`
|
|
export const hello = "Hey World!"
|
|
export const hi = "Hey there!"
|
|
`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
hello = await fetchText(`${url}/greetings/hello`);
|
|
expect(hello).toBe("Hey World!");
|
|
|
|
hi = await fetchText(`${url}/hi`);
|
|
expect(hi).toBe("Hey there!");
|
|
});
|
|
|
|
it("should support modifying external modules during dev session (Functions)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"modules/my-html.html": dedent`
|
|
<h1>Hello HTML World!</h1>
|
|
`,
|
|
"functions/hello.js": dedent`
|
|
import html from "../modules/my-html.html";
|
|
export async function onRequest() {
|
|
return new Response(html);
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let hello = await fetchText(`${url}/hello`);
|
|
expect(hello).toBe("<h1>Hello HTML World!</h1>");
|
|
|
|
await helper.seed({
|
|
"modules/my-html.html": dedent`
|
|
<h1>Updated HTML!</h1>
|
|
`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
hello = await fetchText(`${url}/hello`);
|
|
expect(hello).toBe("<h1>Updated HTML!</h1>");
|
|
});
|
|
|
|
it("should modify worker during dev session (_worker)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Hello World!")
|
|
}
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let hello = await fetchText(url);
|
|
expect(hello).toBe("Hello World!");
|
|
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response("Updated Worker!")
|
|
}
|
|
}`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
hello = await fetchText(url);
|
|
expect(hello).toBe("Updated Worker!");
|
|
});
|
|
|
|
it("should support modifying dependencies during dev session (_worker)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"pets/bear.js": dedent`
|
|
export const bear = "BEAR!"
|
|
`,
|
|
"_worker.js": dedent`
|
|
import { bear } from "./pets/bear"
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response(bear)
|
|
}
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let bear = await fetchText(url);
|
|
expect(bear).toBe("BEAR!");
|
|
|
|
await helper.seed({
|
|
"pets/bear.js": dedent`
|
|
export const bear = "We love BEAR!"
|
|
`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
bear = await fetchText(url);
|
|
expect(bear).toBe("We love BEAR!");
|
|
});
|
|
|
|
it("should support modifying external modules during dev session (_worker)", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"graham.html": dedent`
|
|
<h1>Graham the dog</h1>
|
|
`,
|
|
"_worker.js": dedent`
|
|
import html from "./graham.html"
|
|
export default {
|
|
fetch(request, env) {
|
|
return new Response(html)
|
|
}
|
|
}`,
|
|
});
|
|
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
let graham = await fetchText(url);
|
|
expect(graham).toBe("<h1>Graham the dog</h1>");
|
|
|
|
await helper.seed({
|
|
"graham.html": dedent`
|
|
<h1>Graham is the bestest doggo</h1>
|
|
`,
|
|
});
|
|
|
|
await worker.waitForReload();
|
|
|
|
graham = await fetchText(url);
|
|
expect(graham).toBe("<h1>Graham is the bestest doggo</h1>");
|
|
});
|
|
|
|
it("should support modifying _routes.json during dev session", async ({
|
|
expect,
|
|
}) => {
|
|
const helper = new WranglerE2ETestHelper();
|
|
|
|
await helper.seed({
|
|
"_worker.js": dedent`
|
|
export default {
|
|
async fetch(request, env) {
|
|
const url = new URL(request.url);
|
|
if (url.pathname === "/foo") {
|
|
return new Response("foo");
|
|
}
|
|
if (url.pathname === "/bar") {
|
|
return new Response("bar");
|
|
}
|
|
return new Response("Hello _routes.json")
|
|
}
|
|
}`,
|
|
"_routes.json": dedent`
|
|
{
|
|
"version": 1,
|
|
"include": ["/foo", "/bar"],
|
|
"exclude": []
|
|
}
|
|
`,
|
|
"index.html": dedent`
|
|
hello world
|
|
`,
|
|
});
|
|
const worker = helper.runLongLived(
|
|
`${cmd} --port ${port} --inspector-port ${inspectorPort} .`
|
|
);
|
|
const { url } = await worker.waitForReady();
|
|
|
|
const foo = await fetchText(`${url}/foo`);
|
|
expect(foo).toBe("foo");
|
|
|
|
const bar = await fetchText(`${url}/bar`);
|
|
expect(bar).toBe("bar");
|
|
|
|
await helper.seed({
|
|
"_routes.json": dedent`
|
|
{
|
|
"version": 1,
|
|
"include": ["/foo"],
|
|
"exclude": ["/bar"]
|
|
}
|
|
`,
|
|
});
|
|
await worker.waitForReload();
|
|
|
|
const foo2 = await fetchText(`${url}/foo`);
|
|
expect(foo2).toBe("foo");
|
|
|
|
const bar2 = await fetchText(`${url}/bar`);
|
|
expect(bar2).toBe("hello world");
|
|
});
|
|
});
|
|
});
|