Skip to content

Commit 6be530b

Browse files
chore: add a test harness for <Dev/> (#615)
This adds a basic test harness for the `<Dev/>` component. We'll add more tests and flesh out the harness a little more later, but figured we could land this and iterate on it.
1 parent 3a445b3 commit 6be530b

File tree

7 files changed

+262
-32
lines changed

7 files changed

+262
-32
lines changed

package-lock.json

+73-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wrangler/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@types/mime": "^2.0.3",
5757
"@types/prompts": "^2.0.14",
5858
"@types/react": "^17.0.37",
59+
"@types/react-test-renderer": "^17.0.1",
5960
"@types/serve-static": "^1.13.10",
6061
"@types/signal-exit": "^3.0.1",
6162
"@types/ws": "^8.2.1",
@@ -83,6 +84,7 @@
8384
"open": "^8.4.0",
8485
"prompts": "^2.4.2",
8586
"react": "^17.0.2",
87+
"react-test-renderer": "^17.0.2",
8688
"react-error-boundary": "^3.1.4",
8789
"serve-static": "^1.14.1",
8890
"signal-exit": "^3.0.6",
@@ -122,7 +124,8 @@
122124
"node_modules/(?!find-up|locate-path|p-locate|p-limit|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port)"
123125
],
124126
"moduleNameMapper": {
125-
"clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js"
127+
"clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
128+
"miniflare/cli": "<rootDir>/../../node_modules/miniflare/dist/src/cli.js"
126129
},
127130
"transform": {
128131
"^.+\\.c?(t|j)sx?$": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import * as fs from "node:fs";
2+
import React from "react";
3+
import TestRenderer from "react-test-renderer";
4+
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
5+
import { unsetAllMocks } from "./helpers/mock-cfetch";
6+
import { mockConsoleMethods } from "./helpers/mock-console";
7+
import { runInTempDir } from "./helpers/run-in-tmp";
8+
import type { DevProps, default as DevType } from "../dev/dev";
9+
10+
function sleep(period = 100): Promise<void> {
11+
return new Promise((resolve) => setTimeout(resolve, period));
12+
}
13+
14+
// we use this ["mock"] form to avoid esbuild-jest from rewriting it
15+
jest["mock"]("../proxy", () => {
16+
return {
17+
usePreviewServer() {},
18+
waitForPortToBeAvailable() {},
19+
};
20+
});
21+
jest["mock"]("../inspect", () => {
22+
return () => {};
23+
});
24+
25+
const Dev: typeof DevType = jest.requireActual("../dev/dev").DevImplementation;
26+
27+
mockAccountId();
28+
mockApiToken();
29+
runInTempDir();
30+
mockConsoleMethods();
31+
afterEach(() => {
32+
unsetAllMocks();
33+
});
34+
35+
describe("dev", () => {
36+
it("should render", async () => {
37+
fs.writeFileSync("./index.js", `export default {}`);
38+
39+
const testRenderer = await renderDev({
40+
entry: {
41+
file: "./index.js",
42+
directory: process.cwd(),
43+
format: "modules",
44+
},
45+
});
46+
expect(testRenderer.toJSON()).toMatchInlineSnapshot(`
47+
<ink-box
48+
style={
49+
Object {
50+
"borderStyle": "round",
51+
"flexDirection": "row",
52+
"flexGrow": 0,
53+
"flexShrink": 1,
54+
"marginBottom": 0,
55+
"marginLeft": 0,
56+
"marginRight": 0,
57+
"marginTop": 0,
58+
"paddingBottom": 0,
59+
"paddingLeft": 1,
60+
"paddingRight": 1,
61+
"paddingTop": 0,
62+
}
63+
}
64+
>
65+
<ink-text
66+
internal_transform={[Function]}
67+
style={
68+
Object {
69+
"flexDirection": "row",
70+
"flexGrow": 0,
71+
"flexShrink": 1,
72+
"textWrap": "wrap",
73+
}
74+
}
75+
>
76+
B to open a browser, D to open Devtools, L to turn off local mode, X to exit
77+
</ink-text>
78+
</ink-box>
79+
`);
80+
await TestRenderer.act(async () => {
81+
// TODO: get rid of these sleep statements
82+
await sleep(50);
83+
testRenderer.unmount();
84+
await sleep(50);
85+
});
86+
await sleep(50);
87+
});
88+
});
89+
90+
async function renderDev({
91+
name,
92+
entry = { file: "index.js", directory: "", format: "modules" },
93+
port,
94+
inspectorPort = 9229,
95+
accountId,
96+
legacyEnv = true,
97+
initialMode = "local",
98+
jsxFactory,
99+
jsxFragment,
100+
localProtocol = "http",
101+
upstreamProtocol = "https",
102+
rules = [],
103+
bindings = {
104+
kv_namespaces: [],
105+
vars: {},
106+
durable_objects: { bindings: [] },
107+
r2_buckets: [],
108+
wasm_modules: undefined,
109+
text_blobs: undefined,
110+
unsafe: [],
111+
},
112+
public: publicDir,
113+
assetPaths,
114+
compatibilityDate,
115+
compatibilityFlags,
116+
usageModel,
117+
buildCommand = {},
118+
enableLocalPersistence = false,
119+
env,
120+
zone,
121+
}: Partial<DevProps>): Promise<TestRenderer.ReactTestRenderer> {
122+
let instance: TestRenderer.ReactTestRenderer | undefined;
123+
await TestRenderer.act(async () => {
124+
instance = TestRenderer.create(
125+
<Dev
126+
name={name}
127+
entry={entry}
128+
env={env}
129+
rules={rules}
130+
port={port}
131+
inspectorPort={inspectorPort}
132+
legacyEnv={legacyEnv}
133+
buildCommand={buildCommand}
134+
initialMode={initialMode}
135+
localProtocol={localProtocol}
136+
upstreamProtocol={upstreamProtocol}
137+
jsxFactory={jsxFactory}
138+
jsxFragment={jsxFragment}
139+
accountId={accountId}
140+
assetPaths={assetPaths}
141+
public={publicDir}
142+
compatibilityDate={compatibilityDate}
143+
compatibilityFlags={compatibilityFlags}
144+
usageModel={usageModel}
145+
bindings={bindings}
146+
enableLocalPersistence={enableLocalPersistence}
147+
zone={zone}
148+
/>
149+
);
150+
});
151+
return instance as TestRenderer.ReactTestRenderer;
152+
}

0 commit comments

Comments
 (0)