Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
penalosa committed Feb 7, 2024
1 parent e0fc8b0 commit 205f4ad
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 5 deletions.
193 changes: 193 additions & 0 deletions packages/wrangler/src/__tests__/navigator-user-agent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import assert from "node:assert";
import { mkdir, writeFile, readFile } from "node:fs/promises";
import path from "node:path";
import dedent from "ts-dedent";
import { bundleWorker } from "../deployment-bundle/bundle";
import { noopModuleCollector } from "../deployment-bundle/module-collection";
import { isNavigatorDefined } from "../navigator-user-agent";
import { mockConsoleMethods } from "./helpers/mock-console";
import { runInTempDir } from "./helpers/run-in-tmp";

/*
* This file contains inline comments with the word "javascript"
* This signals to a compatible editor extension that the template string
* contents should be syntax-highlighted as JavaScript. One such extension
* is zjcompt.es6-string-javascript, but there are others.
*/

async function seedFs(files: Record<string, string>): Promise<void> {
for (const [location, contents] of Object.entries(files)) {
await mkdir(path.dirname(location), { recursive: true });
await writeFile(location, contents);
}
}

describe("isNavigatorDefined", () => {
test("default", () => {
expect(isNavigatorDefined(undefined)).toBe(false);
});

test("modern date", () => {
expect(isNavigatorDefined("2024-01-01")).toBe(true);
});

test("old date", () => {
expect(isNavigatorDefined("2000-01-01")).toBe(false);
});

test("switch date", () => {
expect(isNavigatorDefined("2022-03-21")).toBe(true);
});

test("before date", () => {
expect(isNavigatorDefined("2022-03-20")).toBe(false);
});

test("old date, but with flag", () => {
expect(isNavigatorDefined("2000-01-01", ["global_navigator"])).toBe(true);
});

test("old date, with disable flag", () => {
expect(isNavigatorDefined("2000-01-01", ["no_global_navigator"])).toBe(
false
);
});

test("new date, but with disable flag", () => {
expect(isNavigatorDefined("2024-01-01", ["no_global_navigator"])).toBe(
false
);
});

test("new date, with enable flag", () => {
expect(isNavigatorDefined("2024-01-01", ["global_navigator"])).toBe(true);
});

test("errors with disable and enable flags specified", () => {
try {
isNavigatorDefined("2024-01-01", [
"no_global_navigator",
"global_navigator",
]);
assert(false, "Unreachable");
} catch (e) {
expect(e).toMatchInlineSnapshot(
`[AssertionError: Can't both enable and disable a flag]`
);
}
});
});

// Does bundleWorker respect the value of `defineNavigatorUserAgent`?
describe("defineNavigatorUserAgent is respected", () => {
runInTempDir();
const std = mockConsoleMethods();

it("defineNavigatorUserAgent = false, navigator preserved", async () => {
await seedFs({
"src/index.js": dedent/* javascript */ `
function randomBytes(length) {
if (navigator.userAgent !== "Cloudflare-Workers") {
return new Uint8Array(require("node:crypto").randomBytes(length));
} else {
return crypto.getRandomValues(new Uint8Array(length));
}
}
export default {
async fetch(request, env) {
return new Response(randomBytes(10))
},
};
`,
});

await bundleWorker(
{
file: path.resolve("src/index.js"),
directory: process.cwd(),
format: "modules",
moduleRoot: path.dirname(path.resolve("src/index.js")),
},
path.resolve("dist"),
{
bundle: true,
additionalModules: [],
moduleCollector: noopModuleCollector,
serveAssetsFromWorker: false,
doBindings: [],
define: {},
checkFetch: false,
targetConsumer: "deploy",
local: true,
projectRoot: process.cwd(),
defineNavigatorUserAgent: false,
}
);

// Build time warning that the dynamic import of `require("node:crypto")` may not be safe
expect(std.warn).toMatchInlineSnapshot(`
"▲ [WARNING] The package \\"node:crypto\\" wasn't found on the file system but is built into node.
Your Worker may throw errors at runtime unless you enable the \\"nodejs_compat\\" compatibility flag.
Refer to https://developers.cloudflare.com/workers/runtime-apis/nodejs/ for more details. Imported
from:
- src/index.js
"
`);
const fileContents = await readFile("dist/index.js", "utf8");

// navigator.userAgent should have been preserved as-is
expect(fileContents).toContain("navigator.userAgent");
});

it("defineNavigatorUserAgent = true, navigator treeshaken", async () => {
await seedFs({
"src/index.js": dedent/* javascript */ `
function randomBytes(length) {
if (navigator.userAgent !== "Cloudflare-Workers") {
return new Uint8Array(require("node:crypto").randomBytes(length));
} else {
return crypto.getRandomValues(new Uint8Array(length));
}
}
export default {
async fetch(request, env) {
return new Response(randomBytes(10))
},
};
`,
});

await bundleWorker(
{
file: path.resolve("src/index.js"),
directory: process.cwd(),
format: "modules",
moduleRoot: path.dirname(path.resolve("src/index.js")),
},
path.resolve("dist"),
{
bundle: true,
additionalModules: [],
moduleCollector: noopModuleCollector,
serveAssetsFromWorker: false,
doBindings: [],
define: {},
checkFetch: false,
targetConsumer: "deploy",
local: true,
projectRoot: process.cwd(),
defineNavigatorUserAgent: true,
}
);

// Build time warning is suppressed, because esbuild treeshakes the relevant code path
expect(std.warn).toMatchInlineSnapshot(`""`);

const fileContents = await readFile("dist/index.js", "utf8");

// navigator.userAgent should have been defined, and so should not be present in the bundle
expect(fileContents).not.toContain("navigator.userAgent");
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { relative } from "path";
import chalk from "chalk";
import { logger } from "../../logger";
import type { Plugin } from "esbuild";
import { relative } from "path";

// Infinite loop detection
const seen = new Set();
Expand All @@ -17,6 +17,8 @@ export const nodejsCompatPlugin: (silenceWarnings: boolean) => Plugin = (
) => ({
name: "nodejs_compat imports plugin",
setup(pluginBuild) {
seen.clear();
warnedPackaged.clear();
pluginBuild.onResolve(
{ filter: /node:.*/ },
async ({ path, kind, resolveDir, ...opts }) => {
Expand Down
8 changes: 5 additions & 3 deletions packages/wrangler/src/navigator-user-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ export function isNavigatorDefined(
compatibility_flags: string[] = []
) {
assert(
compatibility_flags.includes("global_navigator") &&
compatibility_flags.includes("no_global_navigator"),
!(
compatibility_flags.includes("global_navigator") &&
compatibility_flags.includes("no_global_navigator")
),
"Can't both enable and disable a flag"
);
if (compatibility_flags.includes("global_navigator")) {
Expand All @@ -15,5 +17,5 @@ export function isNavigatorDefined(
if (compatibility_flags.includes("no_global_navigator")) {
return false;
}
return !compatibility_date || compatibility_date >= "2022-03-21";
return !!compatibility_date && compatibility_date >= "2022-03-21";
}
2 changes: 1 addition & 1 deletion packages/wrangler/src/pages/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { writeAdditionalModules } from "../deployment-bundle/find-additional-mod
import { FatalError, UserError } from "../errors";
import { logger } from "../logger";
import * as metrics from "../metrics";
import { isNavigatorDefined } from "../navigator-user-agent";
import { buildFunctions } from "./buildFunctions";
import {
EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR,
Expand All @@ -21,7 +22,6 @@ import type {
CommonYargsArgv,
StrictYargsOptionsToInterface,
} from "../yargs-types";
import { isNavigatorDefined } from "../navigator-user-agent";

export type PagesBuildArgs = StrictYargsOptionsToInterface<typeof Options>;

Expand Down

0 comments on commit 205f4ad

Please sign in to comment.