wrangler 0.0.0-e7a0cd5b → 0.0.0-e87ad068

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +254 -128
  4. package/package.json +27 -19
  5. package/src/__tests__/api-dev.test.ts +65 -36
  6. package/src/__tests__/api-devregistry.test.ts +121 -0
  7. package/src/__tests__/api.test.ts +27 -5
  8. package/src/__tests__/configuration.test.ts +797 -98
  9. package/src/__tests__/constellation.test.ts +371 -0
  10. package/src/__tests__/d1/d1.test.ts +82 -0
  11. package/src/__tests__/d1/execute.test.ts +66 -0
  12. package/src/__tests__/d1/migrate.test.ts +257 -0
  13. package/src/__tests__/d1/splitter.test.ts +255 -0
  14. package/src/__tests__/delete.test.ts +91 -49
  15. package/src/__tests__/deployments.test.ts +330 -56
  16. package/src/__tests__/dev.test.tsx +198 -90
  17. package/src/__tests__/generate.test.ts +12 -10
  18. package/src/__tests__/guess-worker-format.test.ts +35 -0
  19. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  20. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  21. package/src/__tests__/helpers/mock-console.ts +6 -0
  22. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  23. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  24. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  25. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  26. package/src/__tests__/helpers/mock-kv.ts +29 -16
  27. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  28. package/src/__tests__/helpers/mock-process.ts +8 -13
  29. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  30. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  31. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  32. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  33. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  34. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  35. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  36. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  37. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  38. package/src/__tests__/helpers/msw/index.ts +32 -1
  39. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  40. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  41. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  42. package/src/__tests__/index.test.ts +92 -72
  43. package/src/__tests__/init.test.ts +1033 -631
  44. package/src/__tests__/jest.setup.ts +19 -42
  45. package/src/__tests__/kv.test.ts +355 -231
  46. package/src/__tests__/logger.test.ts +68 -2
  47. package/src/__tests__/logout.test.ts +47 -0
  48. package/src/__tests__/metrics.test.ts +94 -51
  49. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  50. package/src/__tests__/middleware.test.ts +212 -122
  51. package/src/__tests__/mtls-certificates.test.ts +589 -0
  52. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  53. package/src/__tests__/pages/functions-build.test.ts +529 -0
  54. package/src/__tests__/pages/pages.test.ts +81 -0
  55. package/src/__tests__/pages/project-create.test.ts +63 -0
  56. package/src/__tests__/pages/project-list.test.ts +110 -0
  57. package/src/__tests__/pages/project-upload.test.ts +500 -0
  58. package/src/__tests__/pages/publish.test.ts +2864 -0
  59. package/src/__tests__/pages-deployment-tail.test.ts +957 -0
  60. package/src/__tests__/parse.test.ts +106 -0
  61. package/src/__tests__/publish.test.ts +2227 -1005
  62. package/src/__tests__/pubsub.test.ts +226 -100
  63. package/src/__tests__/queues.test.ts +238 -77
  64. package/src/__tests__/r2.test.ts +55 -57
  65. package/src/__tests__/secret.test.ts +179 -130
  66. package/src/__tests__/tail.test.ts +313 -113
  67. package/src/__tests__/test-old-node-version.js +3 -3
  68. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  69. package/src/__tests__/tsconfig-sanity.ts +12 -0
  70. package/src/__tests__/tsconfig.json +8 -0
  71. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  72. package/src/__tests__/type-generation.test.ts +43 -26
  73. package/src/__tests__/unstableDev.test.ts +61 -0
  74. package/src/__tests__/user.test.ts +35 -72
  75. package/src/__tests__/whoami.test.tsx +8 -114
  76. package/src/__tests__/worker-namespace.test.ts +83 -69
  77. package/src/api/dev.ts +180 -60
  78. package/src/api/index.ts +10 -1
  79. package/src/api/mtls-certificate.ts +148 -0
  80. package/src/api/pages/create-worker-bundle-contents.ts +80 -0
  81. package/src/api/pages/index.ts +5 -0
  82. package/src/api/pages/publish.tsx +371 -0
  83. package/src/bundle.ts +315 -148
  84. package/src/cfetch/index.ts +0 -2
  85. package/src/cfetch/internal.ts +33 -33
  86. package/src/cli.ts +4 -4
  87. package/src/config/config.ts +14 -7
  88. package/src/config/environment.ts +120 -24
  89. package/src/config/index.ts +117 -35
  90. package/src/config/validation-helpers.ts +1 -1
  91. package/src/config/validation.ts +430 -19
  92. package/src/constellation/createProject.tsx +51 -0
  93. package/src/constellation/deleteProject.ts +51 -0
  94. package/src/constellation/deleteProjectModel.ts +76 -0
  95. package/src/constellation/index.ts +75 -0
  96. package/src/constellation/listCatalog.tsx +35 -0
  97. package/src/constellation/listModel.tsx +49 -0
  98. package/src/constellation/listProject.tsx +28 -0
  99. package/src/constellation/listRuntime.tsx +28 -0
  100. package/src/constellation/options.ts +17 -0
  101. package/src/constellation/types.ts +17 -0
  102. package/src/constellation/uploadModel.tsx +84 -0
  103. package/src/constellation/utils.ts +90 -0
  104. package/src/create-worker-preview.ts +19 -8
  105. package/src/create-worker-upload-form.ts +81 -18
  106. package/src/d1/backups.tsx +47 -46
  107. package/src/d1/constants.ts +5 -0
  108. package/src/d1/create.tsx +85 -37
  109. package/src/d1/{delete.tsx → delete.ts} +12 -15
  110. package/src/d1/execute.tsx +228 -156
  111. package/src/d1/index.ts +31 -7
  112. package/src/d1/list.tsx +27 -13
  113. package/src/d1/migrations/apply.tsx +235 -0
  114. package/src/d1/migrations/create.tsx +79 -0
  115. package/src/d1/migrations/helpers.ts +171 -0
  116. package/src/d1/migrations/index.ts +3 -0
  117. package/src/d1/migrations/list.tsx +95 -0
  118. package/src/d1/migrations/options.ts +23 -0
  119. package/src/d1/options.ts +12 -2
  120. package/src/d1/splitter.ts +161 -0
  121. package/src/d1/types.ts +32 -0
  122. package/src/d1/utils.ts +12 -2
  123. package/src/delete.ts +8 -16
  124. package/src/deployments.ts +340 -46
  125. package/src/deprecated/index.ts +8 -302
  126. package/src/dev/dev.tsx +66 -46
  127. package/src/dev/local.tsx +339 -104
  128. package/src/dev/remote.tsx +36 -3
  129. package/src/dev/start-server.ts +146 -101
  130. package/src/dev/use-esbuild.ts +82 -56
  131. package/src/dev/validate-dev-props.ts +9 -0
  132. package/src/{dev-registry.tsx → dev-registry.ts} +13 -1
  133. package/src/dev.tsx +202 -125
  134. package/src/dialogs.ts +136 -0
  135. package/src/dispatch-namespace.ts +21 -17
  136. package/src/docs/helpers.ts +50 -0
  137. package/src/docs/index.ts +54 -0
  138. package/src/entry.ts +26 -11
  139. package/src/environment-variables/factory.ts +89 -0
  140. package/src/environment-variables/misc-variables.ts +30 -0
  141. package/src/generate/index.ts +303 -0
  142. package/src/git-client.ts +15 -4
  143. package/src/{index.tsx → index.ts} +148 -63
  144. package/src/init.ts +301 -197
  145. package/src/inspect.ts +122 -6
  146. package/src/jest.d.ts +4 -0
  147. package/src/kv/helpers.ts +7 -4
  148. package/src/kv/index.ts +15 -17
  149. package/src/logger.ts +36 -5
  150. package/src/metrics/metrics-config.ts +3 -3
  151. package/src/metrics/send-event.ts +6 -1
  152. package/src/miniflare-cli/assets.ts +64 -28
  153. package/src/miniflare-cli/index.ts +6 -9
  154. package/src/miniflare-cli/tsconfig.json +9 -0
  155. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  156. package/src/miniflare-cli/types.ts +11 -0
  157. package/src/module-collection.ts +92 -26
  158. package/src/mtls-certificate/cli.ts +155 -0
  159. package/src/pages/build.ts +423 -0
  160. package/src/pages/buildFunctions.ts +140 -0
  161. package/src/pages/deployment-tails.ts +281 -0
  162. package/src/pages/deployments.tsx +11 -33
  163. package/src/pages/{dev.tsx → dev.ts} +192 -126
  164. package/src/pages/errors.ts +1 -0
  165. package/src/pages/functions/buildPlugin.ts +73 -69
  166. package/src/pages/functions/buildWorker.ts +342 -117
  167. package/src/pages/functions/tsconfig.json +8 -0
  168. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  169. package/src/pages/{functions.tsx → functions.ts} +8 -4
  170. package/src/pages/{index.tsx → index.ts} +11 -3
  171. package/src/pages/projects.tsx +12 -22
  172. package/src/pages/prompt-select-project.tsx +31 -0
  173. package/src/pages/publish.tsx +45 -214
  174. package/src/pages/types.ts +6 -9
  175. package/src/pages/upload.tsx +58 -14
  176. package/src/pages/utils.ts +14 -3
  177. package/src/parse.ts +23 -1
  178. package/src/paths.ts +1 -1
  179. package/src/proxy.ts +5 -0
  180. package/src/publish/index.ts +20 -16
  181. package/src/publish/publish.ts +81 -48
  182. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  183. package/src/queues/cli/commands/consumer/add.ts +24 -24
  184. package/src/queues/cli/commands/consumer/index.ts +3 -6
  185. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  186. package/src/queues/cli/commands/create.ts +8 -8
  187. package/src/queues/cli/commands/delete.ts +8 -8
  188. package/src/queues/cli/commands/index.ts +6 -4
  189. package/src/queues/cli/commands/list.ts +8 -8
  190. package/src/queues/client.ts +1 -0
  191. package/src/queues/utils.ts +18 -0
  192. package/src/r2/constants.ts +4 -0
  193. package/src/r2/helpers.ts +1 -0
  194. package/src/r2/index.ts +40 -28
  195. package/src/secret/index.ts +18 -17
  196. package/src/{sites.tsx → sites.ts} +165 -53
  197. package/src/tail/createTail.ts +103 -3
  198. package/src/tail/filters.ts +1 -1
  199. package/src/tail/index.ts +8 -11
  200. package/src/tail/printing.ts +27 -0
  201. package/src/traverse-module-graph.ts +54 -0
  202. package/src/tsconfig-sanity.ts +16 -0
  203. package/src/type-generation.ts +85 -57
  204. package/src/user/access.ts +0 -1
  205. package/src/user/auth-variables.ts +113 -0
  206. package/src/user/choose-account.tsx +1 -31
  207. package/src/user/index.ts +0 -1
  208. package/src/user/{user.tsx → user.ts} +124 -82
  209. package/src/utils/render.ts +93 -0
  210. package/src/{whoami.tsx → whoami.ts} +37 -71
  211. package/src/worker.ts +61 -3
  212. package/src/yargs-types.ts +22 -5
  213. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  214. package/templates/__tests__/tsconfig.json +8 -0
  215. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  216. package/templates/checked-fetch.js +9 -3
  217. package/templates/d1-beta-facade.js +95 -25
  218. package/templates/facade.d.ts +14 -0
  219. package/templates/first-party-worker-module-facade.ts +4 -3
  220. package/templates/format-dev-errors.ts +8 -6
  221. package/templates/gitignore +1 -0
  222. package/templates/init-tests/test-jest-new-worker.js +3 -5
  223. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  224. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  225. package/templates/middleware/loader-modules.ts +2 -2
  226. package/templates/middleware/loader-sw.ts +51 -31
  227. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  228. package/templates/new-worker.ts +3 -0
  229. package/templates/pages-dev-pipeline.ts +4 -1
  230. package/templates/pages-dev-util.ts +4 -1
  231. package/templates/pages-shim.ts +4 -1
  232. package/templates/pages-template-plugin.ts +19 -10
  233. package/templates/serve-static-assets.ts +17 -14
  234. package/templates/service-bindings-module-facade.js +1 -0
  235. package/templates/tsconfig-sanity.ts +11 -0
  236. package/templates/tsconfig.init.json +105 -0
  237. package/templates/tsconfig.json +5 -103
  238. package/templates/tsconfig.tsbuildinfo +1 -0
  239. package/wrangler-dist/cli.d.ts +902 -77
  240. package/wrangler-dist/cli.js +131685 -140615
  241. package/wrangler-dist/wasm-sync.wasm +0 -0
  242. package/src/__tests__/api-devregistry.test.js +0 -57
  243. package/src/__tests__/d1.test.ts +0 -72
  244. package/src/__tests__/dialogs.test.tsx +0 -40
  245. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  246. package/src/__tests__/pages.test.ts +0 -2223
  247. package/src/d1/types.tsx +0 -14
  248. package/src/dialogs.tsx +0 -163
  249. package/src/environment-variables.ts +0 -50
  250. package/src/pages/build.tsx +0 -238
  251. package/src/user/env-vars.ts +0 -46
  252. package/templates/experimental-local-cache-stubs.js +0 -27
  253. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  254. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  255. /package/src/pages/{hash.tsx → hash.ts} +0 -0
@@ -0,0 +1,20 @@
1
+ const ORIGINAL_WRANGLER_AUTH_DOMAIN = process.env.WRANGLER_AUTH_DOMAIN;
2
+
3
+ /**
4
+ * Mock the Auth URL domain so that we can control where we attempt to login.
5
+ *
6
+ * Note that you can remove any API token from the environment by setting the value to `null`.
7
+ * This is useful if a higher `describe()` block has already called `mockAuthDomain()`.
8
+ */
9
+ export function mockAuthDomain({ domain }: { domain: string | null }) {
10
+ beforeEach(() => {
11
+ if (domain === null) {
12
+ delete process.env.WRANGLER_AUTH_DOMAIN;
13
+ } else {
14
+ process.env.WRANGLER_AUTH_DOMAIN = domain;
15
+ }
16
+ });
17
+ afterEach(() => {
18
+ process.env.WRANGLER_AUTH_DOMAIN = ORIGINAL_WRANGLER_AUTH_DOMAIN;
19
+ });
20
+ }
@@ -8,6 +8,7 @@ import { logger } from "../../logger";
8
8
 
9
9
  let debugSpy: jest.SpyInstance,
10
10
  logSpy: jest.SpyInstance,
11
+ infoSpy: jest.SpyInstance,
11
12
  errorSpy: jest.SpyInstance,
12
13
  warnSpy: jest.SpyInstance;
13
14
 
@@ -18,6 +19,9 @@ const std = {
18
19
  get out() {
19
20
  return normalizeOutput(logSpy);
20
21
  },
22
+ get info() {
23
+ return normalizeOutput(infoSpy);
24
+ },
21
25
  get err() {
22
26
  return normalizeOutput(errorSpy);
23
27
  },
@@ -47,12 +51,14 @@ export function mockConsoleMethods() {
47
51
  logger.columns = 100;
48
52
  debugSpy = jest.spyOn(console, "debug").mockImplementation();
49
53
  logSpy = jest.spyOn(console, "log").mockImplementation();
54
+ infoSpy = jest.spyOn(console, "info").mockImplementation();
50
55
  errorSpy = jest.spyOn(console, "error").mockImplementation();
51
56
  warnSpy = jest.spyOn(console, "warn").mockImplementation();
52
57
  });
53
58
  afterEach(() => {
54
59
  debugSpy.mockRestore();
55
60
  logSpy.mockRestore();
61
+ infoSpy.mockRestore();
56
62
  errorSpy.mockRestore();
57
63
  warnSpy.mockRestore();
58
64
  });
@@ -1,5 +1,4 @@
1
- import { confirm, prompt, select } from "../../dialogs";
2
- import { normalizeSlashes } from "./mock-console";
1
+ import prompts from "prompts";
3
2
 
4
3
  /**
5
4
  * The expected values for a confirmation request.
@@ -7,6 +6,8 @@ import { normalizeSlashes } from "./mock-console";
7
6
  export interface ConfirmExpectation {
8
7
  /** The text expected to be seen in the confirmation dialog. */
9
8
  text: string;
9
+
10
+ options?: { defaultValue: boolean };
10
11
  /** The mock response send back from the confirmation dialog. */
11
12
  result: boolean;
12
13
  }
@@ -19,35 +20,22 @@ export interface ConfirmExpectation {
19
20
  * then an error is thrown.
20
21
  */
21
22
  export function mockConfirm(...expectations: ConfirmExpectation[]) {
22
- (confirm as jest.Mock).mockImplementation((text: string) => {
23
- for (const expectation of expectations) {
24
- if (normalizeSlashes(text) === normalizeSlashes(expectation.text)) {
25
- expectations = expectations.filter((e) => e !== expectation);
26
- return Promise.resolve(expectation.result);
27
- }
28
- }
29
- throw new Error(`Unexpected confirmation message: ${text}`);
30
- });
31
- return () => {
32
- if (expectations.length > 0) {
33
- throw new Error(
34
- "The following expected confirmation dialogs were not used:\n" +
35
- expectations.map((e) => `- "${e.text}"`).join("\n")
36
- );
37
- }
38
- };
39
- }
23
+ for (const expectation of expectations) {
24
+ (prompts as unknown as jest.Mock).mockImplementationOnce(
25
+ ({ type, name, message, initial }) => {
26
+ expect({ type, name, message }).toStrictEqual({
27
+ type: "confirm",
28
+ name: "value",
29
+ message: expectation.text,
30
+ });
31
+ if (expectation.options) {
32
+ expect(initial).toStrictEqual(expectation.options?.defaultValue);
33
+ }
40
34
 
41
- export function clearConfirmMocks() {
42
- (confirm as jest.Mock).mockReset();
43
- // Because confirm was originally a spy, calling mockReset will simply reset
44
- // it as a function with no return value (!), so we need to accitionally reset
45
- // the mock implementation to the one that throws (from jest.setup.js).
46
- (confirm as jest.Mock).mockImplementation((text: string) => {
47
- throw new Error(
48
- `Unexpected call to \`confirm("${text}")\`.\nYou should use \`mockConfirm()\` to mock calls to \`confirm()\` with expectations. Search the codebase for \`mockConfirm\` to learn more.`
35
+ return Promise.resolve({ value: expectation.result });
36
+ }
49
37
  );
50
- });
38
+ }
51
39
  }
52
40
 
53
41
  /**
@@ -57,7 +45,10 @@ export interface PromptExpectation {
57
45
  /** The text expected to be seen in the prompt dialog. */
58
46
  text: string;
59
47
  /** The type of the prompt. */
60
- type: "text" | "password";
48
+ options?: {
49
+ defaultValue?: string;
50
+ isSecret?: boolean;
51
+ };
61
52
  /** The mock response send back from the prompt dialog. */
62
53
  result: string;
63
54
  }
@@ -70,45 +61,44 @@ export interface PromptExpectation {
70
61
  * then an error is thrown.
71
62
  */
72
63
  export function mockPrompt(...expectations: PromptExpectation[]) {
73
- (prompt as jest.Mock).mockImplementation(
74
- (text: string, type: "text" | "password") => {
75
- for (const expectation of expectations) {
76
- if (text === expectation.text && type == expectation.type) {
77
- expectations = expectations.filter((e) => e !== expectation);
78
- return Promise.resolve(expectation.result);
64
+ for (const expectation of expectations) {
65
+ (prompts as unknown as jest.Mock).mockImplementationOnce(
66
+ ({ type, name, message, initial, style }) => {
67
+ expect({ type, name, message }).toStrictEqual({
68
+ type: "text",
69
+ name: "value",
70
+ message: expectation.text,
71
+ });
72
+ if (expectation.options) {
73
+ expect(initial).toStrictEqual(expectation.options?.defaultValue);
74
+ expect(style).toStrictEqual(
75
+ expectation.options?.isSecret ? "password" : "default"
76
+ );
79
77
  }
78
+ return Promise.resolve({ value: expectation.result });
80
79
  }
81
- throw new Error(`Unexpected confirmation message: ${text}`);
82
- }
83
- );
84
- return () => {
85
- if (expectations.length > 0) {
86
- throw new Error(
87
- "The following expected prompt dialogs were not used:\n" +
88
- expectations.map((e) => `- "${e.text}"`).join("\n")
89
- );
90
- }
91
- };
80
+ );
81
+ }
92
82
  }
93
83
 
94
- export function clearPromptMocks() {
95
- (prompt as jest.Mock).mockReset();
96
- // Because prompt was originally a spy, calling mockReset will simply reset
97
- // it as a function with no return value (!), so we need to accitionally reset
98
- // the mock implementation to the one that throws (from jest.setup.js).
99
- (prompt as jest.Mock).mockImplementation((text: string) => {
100
- throw new Error(
101
- `Unexpected call to \`prompt(${text}, ...)\`.\nYou should use \`mockPrompt()\` to mock calls to \`prompt()\` with expectations. Search the codebase for \`mockPrompt\` to learn more.`
102
- );
103
- });
84
+ interface SelectOptions<Values> {
85
+ choices: SelectOption<Values>[];
86
+ defaultOption?: number;
104
87
  }
105
88
 
89
+ interface SelectOption<Values> {
90
+ title: string;
91
+ description?: string;
92
+ value: Values;
93
+ }
106
94
  /**
107
95
  * The expected values for a select request.
108
96
  */
109
- export interface SelectExpectation {
97
+ export interface SelectExpectation<Values> {
110
98
  /** The text expected to be seen in the select dialog. */
111
99
  text: string;
100
+
101
+ options?: SelectOptions<Values>;
112
102
  /** The mock response send back from the select dialog. */
113
103
  result: string;
114
104
  }
@@ -120,34 +110,30 @@ export interface SelectExpectation {
120
110
  * If there is a call to `select()` that does not match any of the expectations
121
111
  * then an error is thrown.
122
112
  */
123
- export function mockSelect(...expectations: SelectExpectation[]) {
124
- (select as jest.Mock).mockImplementation((text: string) => {
125
- for (const expectation of expectations) {
126
- if (normalizeSlashes(text) === normalizeSlashes(expectation.text)) {
127
- expectations = expectations.filter((e) => e !== expectation);
128
- return Promise.resolve(expectation.result);
113
+ export function mockSelect<Values>(
114
+ ...expectations: SelectExpectation<Values>[]
115
+ ) {
116
+ for (const expectation of expectations) {
117
+ (prompts as unknown as jest.Mock).mockImplementationOnce(
118
+ ({ type, name, message, choices, initial }) => {
119
+ expect({ type, name, message }).toStrictEqual({
120
+ type: "select",
121
+ name: "value",
122
+ message: expectation.text,
123
+ });
124
+ if (expectation.options) {
125
+ expect(choices).toStrictEqual(expectation.options?.choices);
126
+ expect(initial).toStrictEqual(expectation.options?.defaultOption);
127
+ }
128
+ return Promise.resolve({ value: expectation.result });
129
129
  }
130
- }
131
- throw new Error(`Unexpected select message: ${text}`);
132
- });
133
- return () => {
134
- if (expectations.length > 0) {
135
- throw new Error(
136
- "The following expected select dialogs were not used:\n" +
137
- expectations.map((e) => `- "${e.text}"`).join("\n")
138
- );
139
- }
140
- };
130
+ );
131
+ }
141
132
  }
142
133
 
143
- export function clearSelectMocks() {
144
- (select as jest.Mock).mockReset();
145
- // Because select was originally a spy, calling mockReset will simply reset
146
- // it as a function with no return value (!), so we need to additionally reset
147
- // the mock implementation to the one that throws (from jest.setup.js).
148
- (select as jest.Mock).mockImplementation((text: string) => {
149
- throw new Error(
150
- `Unexpected call to \`select("${text}")\`.\nYou should use \`mockSelect()\` to mock calls to \`select()\` with expectations. Search the codebase for \`mockSelect\` to learn more.`
151
- );
152
- });
134
+ export function clearDialogs() {
135
+ // No dialog mocks should be left after each test, and so calling the dialog methods should throw
136
+ expect(() => prompts({ type: "select", name: "unknown" })).toThrow(
137
+ "Unexpected call to "
138
+ );
153
139
  }
@@ -0,0 +1,25 @@
1
+ import { rest } from "msw";
2
+ import { msw } from "./msw";
3
+
4
+ /**
5
+ * Mocks the `/accounts/:accountId/pages/projects/:projectName/upload-token` GET request
6
+ */
7
+ export function mockGetUploadTokenRequest(
8
+ jwt: string,
9
+ accountId: string,
10
+ projectName: string
11
+ ) {
12
+ msw.use(
13
+ rest.get(
14
+ `*/accounts/:accountId/pages/projects/${projectName}/upload-token`,
15
+ (req, res, ctx) => {
16
+ expect(req.params.accountId).toEqual(accountId);
17
+
18
+ return res(
19
+ ctx.status(200),
20
+ ctx.json({ success: true, errors: [], messages: [], result: { jwt } })
21
+ );
22
+ }
23
+ )
24
+ );
25
+ }
@@ -1,8 +1,11 @@
1
- import { setMockResponse } from "./mock-cfetch";
1
+ import { rest } from "msw";
2
+ import { msw, createFetchResult } from "./msw";
2
3
 
3
4
  export function mockGetZoneFromHostRequest(host: string, zone?: string) {
4
- setMockResponse("/zones", (_uri, _init, queryParams) => {
5
- expect(queryParams.get("name")).toEqual(host);
6
- return zone ? [{ id: zone }] : [];
7
- });
5
+ msw.use(
6
+ rest.get("*/zones", (req, res, ctx) => {
7
+ expect(req.url.searchParams.get("name")).toEqual(host);
8
+ return res(ctx.json(createFetchResult(zone ? [{ id: zone }] : [])));
9
+ })
10
+ );
8
11
  }
@@ -1,7 +1,12 @@
1
- import { setMockResponse } from "./mock-cfetch";
1
+ import { rest } from "msw";
2
+ import { createFetchResult, msw } from "./msw";
2
3
 
3
4
  export function mockCollectKnownRoutesRequest(
4
5
  routes: { pattern: string; script: string }[]
5
6
  ) {
6
- setMockResponse(`/zones/:zoneId/workers/routes`, "GET", () => routes);
7
+ msw.use(
8
+ rest.get(`*/zones/:zoneId/workers/routes`, (_, res, ctx) =>
9
+ res.once(ctx.json(createFetchResult(routes)))
10
+ )
11
+ );
7
12
  }
@@ -1,4 +1,5 @@
1
- import { createFetchResult, setMockRawResponse } from "./mock-cfetch";
1
+ import { rest } from "msw";
2
+ import { createFetchResult, msw } from "./msw";
2
3
  import type { NamespaceKeyInfo } from "../../kv/helpers";
3
4
 
4
5
  export function mockKeyListRequest(
@@ -9,25 +10,37 @@ export function mockKeyListRequest(
9
10
  ) {
10
11
  const requests = { count: 0 };
11
12
  // See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
13
+ msw.use(
14
+ rest.get(
15
+ "*/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
16
+ (req, res, ctx) => {
17
+ requests.count++;
18
+ expect(req.params.accountId).toEqual("some-account-id");
19
+ expect(req.params.namespaceId).toEqual(expectedNamespaceId);
20
+ let response: undefined | NamespaceKeyInfo[];
21
+ if (expectedKeys.length <= keysPerRequest) {
22
+ response = expectedKeys;
23
+ }
12
24
 
13
- setMockRawResponse(
14
- "/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
15
- "GET",
16
- ([_url, accountId, namespaceId], _init, query) => {
17
- requests.count++;
18
- expect(accountId).toEqual("some-account-id");
19
- expect(namespaceId).toEqual(expectedNamespaceId);
20
- if (expectedKeys.length <= keysPerRequest) {
21
- return createFetchResult(expectedKeys);
22
- } else {
23
- const start = parseInt(query.get("cursor") ?? "0") || 0;
25
+ const start = parseInt(req.url.searchParams.get("cursor") ?? "0") || 0;
24
26
  const end = start + keysPerRequest;
25
27
  const cursor = end < expectedKeys.length ? end : blankCursorValue;
26
- return createFetchResult(expectedKeys.slice(start, end), true, [], [], {
27
- cursor,
28
- });
28
+
29
+ return res(
30
+ ctx.json(
31
+ createFetchResult(
32
+ response ? response : expectedKeys.slice(start, end),
33
+ true,
34
+ [],
35
+ [],
36
+ {
37
+ cursor,
38
+ }
39
+ )
40
+ )
41
+ );
29
42
  }
30
- }
43
+ )
31
44
  );
32
45
  return requests;
33
46
  }
@@ -1,40 +1,34 @@
1
- import fetchMock from "jest-fetch-mock";
1
+ import { rest } from "msw";
2
2
  import { Request } from "undici";
3
3
  import openInBrowser from "../../open-in-browser";
4
- import {
5
- createFetchResult,
6
- setMockRawResponse,
7
- setMockResponse,
8
- } from "./mock-cfetch";
9
4
  import { mockHttpServer } from "./mock-http-server";
5
+ import { createFetchResult, msw } from "./msw";
10
6
 
11
7
  export function mockGetMemberships(
12
8
  accounts: { id: string; account: { id: string; name: string } }[]
13
9
  ) {
14
- setMockResponse("/memberships", "GET", () => {
15
- return accounts;
16
- });
10
+ msw.use(
11
+ rest.get("*/memberships", (_, res, ctx) => {
12
+ return res.once(ctx.json(createFetchResult(accounts)));
13
+ })
14
+ );
17
15
  }
18
-
19
16
  export function mockGetMembershipsFail() {
20
- setMockRawResponse("/memberships", () => {
21
- return createFetchResult([], false);
22
- });
17
+ msw.use(
18
+ rest.get("*/memberships", (_, res, ctx) => {
19
+ return res.once(ctx.json(createFetchResult([], false)));
20
+ })
21
+ );
23
22
  }
24
23
 
25
- // the response to send when wrangler wants an oauth grant
26
- let oauthGrantResponse: GrantResponseOptions | "timeout" = {};
27
-
28
24
  /**
29
25
  * Functions to help with mocking various parts of the OAuth Flow
30
26
  */
31
27
  export const mockOAuthFlow = () => {
28
+ // the response to send when wrangler wants an oauth grant
29
+ let oauthGrantResponse: GrantResponseOptions | "timeout" = {};
32
30
  const fetchHttp = mockHttpServer();
33
31
 
34
- afterEach(() => {
35
- fetchMock.resetMocks();
36
- });
37
-
38
32
  /**
39
33
  * Mock out the callback from a browser to our HttpServer.
40
34
  *
@@ -42,10 +36,13 @@ export const mockOAuthFlow = () => {
42
36
  * at the OAuth URL, it will automatically trigger the callback URL on the mock HttpServer that
43
37
  * we have created as part of the call to `mockOAuthFlow()`.
44
38
  */
45
- const mockOAuthServerCallback = () => {
39
+ const mockOAuthServerCallback = (
40
+ respondWith?: "timeout" | "success" | "failure" | GrantResponseOptions
41
+ ) => {
46
42
  (
47
43
  openInBrowser as jest.MockedFunction<typeof openInBrowser>
48
44
  ).mockImplementation(async (url: string) => {
45
+ if (respondWith) mockGrantAuthorization({ respondWith });
49
46
  // We don't support the grant response timing out.
50
47
  if (oauthGrantResponse === "timeout") {
51
48
  throw "unimplemented";
@@ -68,6 +65,7 @@ export const mockOAuthFlow = () => {
68
65
  });
69
66
  };
70
67
 
68
+ // Handled in `mockOAuthServerCallback`
71
69
  const mockGrantAuthorization = ({
72
70
  respondWith,
73
71
  }: {
@@ -88,97 +86,110 @@ export const mockOAuthFlow = () => {
88
86
  }
89
87
  };
90
88
 
91
- const mockRevokeAuthorization = () => {
92
- const outcome = {
93
- actual: new Request("https://example.org"),
94
- expected: new Request("https://dash.cloudflare.com/oauth2/revoke", {
95
- method: "POST",
96
- }),
97
- };
98
-
99
- fetchMock.mockIf(outcome.expected.url, async (req) => {
100
- // TODO: update Miniflare typings to match full undici Request
101
- outcome.actual = req as unknown as Request;
102
- return "";
103
- });
104
-
105
- return outcome;
106
- };
107
-
108
89
  const mockGrantAccessToken = ({
109
90
  respondWith,
91
+ domain = "dash.cloudflare.com",
110
92
  }: {
111
93
  respondWith: MockTokenResponse;
94
+ domain?: string;
112
95
  }) => {
113
96
  const outcome = {
114
- actual: new Request("https://example.org"),
115
- expected: new Request("https://dash.cloudflare.com/oauth2/token", {
116
- method: "POST",
117
- }),
97
+ actual: "https://example.org",
98
+ expected: `https://${domain}/oauth2/token`,
118
99
  };
119
-
120
- fetchMock.mockOnceIf(outcome.expected.url, async (req) => {
121
- // TODO: update Miniflare typings to match full undici Request
122
- outcome.actual = req as unknown as Request;
123
- return makeTokenResponse(respondWith);
124
- });
100
+ msw.use(
101
+ rest.post(outcome.expected, async (req, res, ctx) => {
102
+ // TODO: update Miniflare typings to match full undici Request
103
+ outcome.actual = req.url.toString();
104
+ return res.once(ctx.json(makeTokenResponse(respondWith)));
105
+ })
106
+ );
125
107
 
126
108
  return outcome;
127
109
  };
128
110
 
129
- const mockExchangeRefreshTokenForAccessToken = ({
130
- respondWith,
111
+ function mockDomainUsesAccess({
112
+ usesAccess,
113
+ domain = "dash.cloudflare.com",
131
114
  }: {
132
- respondWith: "refreshSuccess" | "refreshError" | "badResponse";
133
- }) => {
134
- fetchMock.mockOnceIf(
135
- "https://dash.cloudflare.com/oauth2/token",
136
- async () => {
137
- switch (respondWith) {
138
- case "refreshSuccess":
139
- return {
140
- status: 200,
141
- body: JSON.stringify({
142
- access_token: "access_token_success_mock",
143
- expires_in: 1701,
144
- refresh_token: "refresh_token_sucess_mock",
145
- scope: "scope_success_mock",
146
- token_type: "bearer",
147
- }),
148
- };
149
- case "refreshError":
150
- return {
151
- status: 400,
152
- body: JSON.stringify({
153
- error: "invalid_request",
154
- error_description: "error_description_mock",
155
- error_hint: "error_hint_mock",
156
- error_verbose: "error_verbose_mock",
157
- status_code: 400,
158
- }),
159
- };
160
- case "badResponse":
161
- return {
162
- status: 400,
163
- body: `<html> <body> This shouldn't be sent, but should be handled </body> </html>`,
164
- };
165
-
166
- default:
167
- return "Not a respondWith option for `mockExchangeRefreshTokenForAccessToken`";
115
+ usesAccess: boolean;
116
+ domain?: string;
117
+ }) {
118
+ // If the domain relies upon Cloudflare Access, then a request to the domain
119
+ // will result in a redirect to the `cloudflareaccess.com` domain.
120
+ msw.use(
121
+ rest.get(`https://${domain}/`, (_, res, ctx) => {
122
+ let status = 200;
123
+ let headers: Record<string, string> = {
124
+ "Content-Type": "application/json",
125
+ };
126
+ if (usesAccess) {
127
+ status = 302;
128
+ headers = { location: "cloudflareaccess.com" };
168
129
  }
169
- }
130
+ return res.once(ctx.status(status), ctx.set(headers));
131
+ })
170
132
  );
171
- };
133
+ }
172
134
 
173
135
  return {
136
+ mockHttpServer,
137
+ mockDomainUsesAccess,
174
138
  mockGrantAccessToken,
175
- mockGrantAuthorization,
176
139
  mockOAuthServerCallback,
177
- mockRevokeAuthorization,
178
140
  mockExchangeRefreshTokenForAccessToken,
179
141
  };
180
142
  };
181
143
 
144
+ export function mockExchangeRefreshTokenForAccessToken({
145
+ respondWith,
146
+ domain = "dash.cloudflare.com",
147
+ }: {
148
+ respondWith: "refreshSuccess" | "refreshError" | "badResponse";
149
+ domain?: string;
150
+ }) {
151
+ msw.use(
152
+ rest.post(`https://${domain}/oauth2/token`, async (_, res, ctx) => {
153
+ switch (respondWith) {
154
+ case "refreshSuccess":
155
+ return res.once(
156
+ ctx.status(200),
157
+ ctx.json({
158
+ access_token: "access_token_success_mock",
159
+ expires_in: 1701,
160
+ refresh_token: "refresh_token_success_mock",
161
+ scope: "scope_success_mock",
162
+ token_type: "bearer",
163
+ })
164
+ );
165
+ case "refreshError":
166
+ return res.once(
167
+ ctx.status(400),
168
+ ctx.json({
169
+ error: "invalid_request",
170
+ error_description: "error_description_mock",
171
+ error_hint: "error_hint_mock",
172
+ error_verbose: "error_verbose_mock",
173
+ status_code: 400,
174
+ })
175
+ );
176
+ case "badResponse":
177
+ return res.once(
178
+ ctx.status(400),
179
+ ctx.text(
180
+ `<html> <body> This shouldn't be sent, but should be handled </body> </html>`
181
+ )
182
+ );
183
+
184
+ default:
185
+ throw new Error(
186
+ "Not a respondWith option for `mockExchangeRefreshTokenForAccessToken`"
187
+ );
188
+ }
189
+ })
190
+ );
191
+ }
192
+
182
193
  type GrantResponseOptions = {
183
194
  code?: string;
184
195
  error?: ErrorType | ErrorType[];
@@ -230,7 +241,7 @@ type MockTokenResponse =
230
241
  error: ErrorType;
231
242
  };
232
243
 
233
- const makeTokenResponse = (partialResponse: MockTokenResponse): string => {
244
+ const makeTokenResponse = (partialResponse: MockTokenResponse) => {
234
245
  let fullResponse: MockTokenResponse;
235
246
 
236
247
  if (partialResponse === "ok") {
@@ -248,5 +259,5 @@ const makeTokenResponse = (partialResponse: MockTokenResponse): string => {
248
259
  fullResponse = partialResponse;
249
260
  }
250
261
 
251
- return JSON.stringify(fullResponse);
262
+ return fullResponse;
252
263
  };