wrangler 2.0.12 → 2.0.16

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 (149) hide show
  1. package/README.md +7 -1
  2. package/bin/wrangler.js +111 -57
  3. package/miniflare-dist/index.mjs +9 -2
  4. package/package.json +156 -154
  5. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  6. package/src/__tests__/config-cache.test.ts +30 -24
  7. package/src/__tests__/configuration.test.ts +3935 -3476
  8. package/src/__tests__/dev.test.tsx +1128 -979
  9. package/src/__tests__/guess-worker-format.test.ts +68 -68
  10. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  11. package/src/__tests__/helpers/faye-websocket.d.ts +4 -4
  12. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  13. package/src/__tests__/helpers/mock-bin.ts +20 -20
  14. package/src/__tests__/helpers/mock-cfetch.ts +92 -92
  15. package/src/__tests__/helpers/mock-console.ts +49 -39
  16. package/src/__tests__/helpers/mock-dialogs.ts +94 -71
  17. package/src/__tests__/helpers/mock-http-server.ts +30 -30
  18. package/src/__tests__/helpers/mock-istty.ts +65 -18
  19. package/src/__tests__/helpers/mock-kv.ts +26 -26
  20. package/src/__tests__/helpers/mock-oauth-flow.ts +223 -228
  21. package/src/__tests__/helpers/mock-process.ts +39 -0
  22. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  23. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  24. package/src/__tests__/helpers/run-in-tmp.ts +27 -27
  25. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  26. package/src/__tests__/helpers/write-worker-source.ts +16 -16
  27. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  28. package/src/__tests__/https-options.test.ts +104 -104
  29. package/src/__tests__/index.test.ts +239 -234
  30. package/src/__tests__/init.test.ts +1605 -1250
  31. package/src/__tests__/jest.setup.ts +63 -33
  32. package/src/__tests__/kv.test.ts +1128 -1011
  33. package/src/__tests__/logger.test.ts +100 -74
  34. package/src/__tests__/package-manager.test.ts +303 -303
  35. package/src/__tests__/pages.test.ts +1152 -652
  36. package/src/__tests__/parse.test.ts +252 -252
  37. package/src/__tests__/publish.test.ts +6371 -5622
  38. package/src/__tests__/pubsub.test.ts +367 -0
  39. package/src/__tests__/r2.test.ts +133 -133
  40. package/src/__tests__/route.test.ts +18 -18
  41. package/src/__tests__/secret.test.ts +382 -377
  42. package/src/__tests__/tail.test.ts +530 -530
  43. package/src/__tests__/user.test.ts +123 -111
  44. package/src/__tests__/whoami.test.tsx +198 -117
  45. package/src/__tests__/worker-namespace.test.ts +327 -0
  46. package/src/abort.d.ts +1 -1
  47. package/src/api/dev.ts +49 -0
  48. package/src/api/index.ts +1 -0
  49. package/src/bundle-reporter.tsx +29 -0
  50. package/src/bundle.ts +157 -149
  51. package/src/cfetch/index.ts +80 -80
  52. package/src/cfetch/internal.ts +90 -83
  53. package/src/cli.ts +21 -7
  54. package/src/config/config.ts +204 -195
  55. package/src/config/diagnostics.ts +61 -61
  56. package/src/config/environment.ts +390 -357
  57. package/src/config/index.ts +206 -193
  58. package/src/config/validation-helpers.ts +366 -366
  59. package/src/config/validation.ts +1573 -1376
  60. package/src/config-cache.ts +79 -41
  61. package/src/create-worker-preview.ts +206 -136
  62. package/src/create-worker-upload-form.ts +247 -238
  63. package/src/dev/dev-vars.ts +13 -13
  64. package/src/dev/dev.tsx +329 -307
  65. package/src/dev/local.tsx +304 -275
  66. package/src/dev/remote.tsx +366 -224
  67. package/src/dev/use-esbuild.ts +126 -91
  68. package/src/dev.tsx +538 -0
  69. package/src/dialogs.tsx +97 -97
  70. package/src/durable.ts +87 -87
  71. package/src/entry.ts +234 -228
  72. package/src/environment-variables.ts +23 -23
  73. package/src/errors.ts +6 -6
  74. package/src/generate.ts +33 -0
  75. package/src/git-client.ts +42 -0
  76. package/src/https-options.ts +79 -79
  77. package/src/index.tsx +1775 -2763
  78. package/src/init.ts +549 -0
  79. package/src/inspect.ts +593 -593
  80. package/src/intl-polyfill.d.ts +123 -123
  81. package/src/is-interactive.ts +12 -0
  82. package/src/kv.ts +277 -277
  83. package/src/logger.ts +46 -39
  84. package/src/miniflare-cli/enum-keys.ts +8 -8
  85. package/src/miniflare-cli/index.ts +42 -31
  86. package/src/miniflare-cli/request-context.ts +18 -18
  87. package/src/module-collection.ts +212 -212
  88. package/src/open-in-browser.ts +4 -6
  89. package/src/package-manager.ts +123 -123
  90. package/src/pages/build.tsx +202 -0
  91. package/src/pages/constants.ts +7 -0
  92. package/src/pages/deployments.tsx +101 -0
  93. package/src/pages/dev.tsx +964 -0
  94. package/src/pages/functions/buildPlugin.ts +105 -0
  95. package/src/pages/functions/buildWorker.ts +151 -0
  96. package/{pages → src/pages}/functions/filepath-routing.test.ts +113 -113
  97. package/src/pages/functions/filepath-routing.ts +189 -0
  98. package/src/pages/functions/identifiers.ts +78 -0
  99. package/src/pages/functions/routes.ts +151 -0
  100. package/src/pages/index.tsx +84 -0
  101. package/src/pages/projects.tsx +157 -0
  102. package/src/pages/publish.tsx +335 -0
  103. package/src/pages/types.ts +40 -0
  104. package/src/pages/upload.tsx +384 -0
  105. package/src/pages/utils.ts +12 -0
  106. package/src/parse.ts +202 -138
  107. package/src/paths.ts +6 -6
  108. package/src/preview.ts +31 -0
  109. package/src/proxy.ts +400 -402
  110. package/src/publish.ts +667 -621
  111. package/src/pubsub/index.ts +286 -0
  112. package/src/pubsub/pubsub-commands.tsx +577 -0
  113. package/src/r2.ts +19 -19
  114. package/src/selfsigned.d.ts +23 -23
  115. package/src/sites.tsx +271 -225
  116. package/src/tail/filters.ts +108 -108
  117. package/src/tail/index.ts +217 -217
  118. package/src/tail/printing.ts +45 -45
  119. package/src/update-check.ts +11 -11
  120. package/src/user/choose-account.tsx +60 -0
  121. package/src/user/env-vars.ts +46 -0
  122. package/src/user/generate-auth-url.ts +33 -0
  123. package/src/user/generate-random-state.ts +16 -0
  124. package/src/user/index.ts +3 -0
  125. package/src/user/user.tsx +1161 -0
  126. package/src/whoami.tsx +61 -42
  127. package/src/worker-namespace.ts +190 -0
  128. package/src/worker.ts +110 -100
  129. package/src/zones.ts +39 -36
  130. package/templates/checked-fetch.js +17 -0
  131. package/templates/new-worker-scheduled.js +3 -3
  132. package/templates/new-worker-scheduled.ts +15 -15
  133. package/templates/new-worker.js +3 -3
  134. package/templates/new-worker.ts +15 -15
  135. package/templates/no-op-worker.js +10 -0
  136. package/templates/pages-template-plugin.ts +155 -0
  137. package/templates/pages-template-worker.ts +161 -0
  138. package/templates/static-asset-facade.js +31 -31
  139. package/templates/tsconfig.json +95 -95
  140. package/wrangler-dist/cli.js +55383 -54138
  141. package/pages/functions/buildPlugin.ts +0 -105
  142. package/pages/functions/buildWorker.ts +0 -151
  143. package/pages/functions/filepath-routing.ts +0 -189
  144. package/pages/functions/identifiers.ts +0 -78
  145. package/pages/functions/routes.ts +0 -156
  146. package/pages/functions/template-plugin.ts +0 -147
  147. package/pages/functions/template-worker.ts +0 -143
  148. package/src/pages.tsx +0 -2093
  149. package/src/user.tsx +0 -1214
@@ -1,54 +1,55 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { chdir } from "node:process";
2
3
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
3
4
  import {
4
- createFetchResult,
5
- setMockRawResponse,
6
- setMockResponse,
7
- unsetAllMocks,
5
+ createFetchResult,
6
+ setMockRawResponse,
7
+ setMockResponse,
8
+ unsetAllMocks,
8
9
  } from "./helpers/mock-cfetch";
9
10
  import { mockConsoleMethods } from "./helpers/mock-console";
10
11
  import { runInTempDir } from "./helpers/run-in-tmp";
11
12
  import { runWrangler } from "./helpers/run-wrangler";
12
- import type { Deployment, Project, UploadPayloadFile } from "../pages";
13
+ import type { Deployment, Project, UploadPayloadFile } from "../pages/types";
13
14
  import type { FormData, RequestInit } from "undici";
14
15
 
15
16
  // Asserting within mock responses get swallowed, so run them out-of-band
16
17
  const outOfBandTests: (() => void)[] = [];
17
18
  function assertLater(fn: () => void) {
18
- outOfBandTests.push(fn);
19
+ outOfBandTests.push(fn);
19
20
  }
20
21
 
21
22
  function mockGetToken(jwt: string) {
22
- return setMockResponse(
23
- "/accounts/:accountId/pages/projects/foo/upload-token",
24
- async ([_url, accountId]) => {
25
- assertLater(() => {
26
- expect(accountId).toEqual("some-account-id");
27
- });
28
-
29
- return { jwt };
30
- }
31
- );
23
+ return setMockResponse(
24
+ "/accounts/:accountId/pages/projects/foo/upload-token",
25
+ async ([_url, accountId]) => {
26
+ assertLater(() => {
27
+ expect(accountId).toEqual("some-account-id");
28
+ });
29
+
30
+ return { jwt };
31
+ }
32
+ );
32
33
  }
33
34
 
34
35
  describe("pages", () => {
35
- runInTempDir();
36
- const std = mockConsoleMethods();
37
- function endEventLoop() {
38
- return new Promise((resolve) => setImmediate(resolve));
39
- }
40
- beforeEach(() => {
41
- outOfBandTests.length = 0;
42
- });
43
- afterEach(() => {
44
- outOfBandTests.forEach((fn) => fn());
45
- });
46
-
47
- it("should should display a list of available subcommands, for pages with no subcommand", async () => {
48
- await runWrangler("pages");
49
- await endEventLoop();
50
-
51
- expect(std.out).toMatchInlineSnapshot(`
36
+ runInTempDir();
37
+ const std = mockConsoleMethods();
38
+ function endEventLoop() {
39
+ return new Promise((resolve) => setImmediate(resolve));
40
+ }
41
+ beforeEach(() => {
42
+ outOfBandTests.length = 0;
43
+ });
44
+ afterEach(() => {
45
+ outOfBandTests.forEach((fn) => fn());
46
+ });
47
+
48
+ it("should should display a list of available subcommands, for pages with no subcommand", async () => {
49
+ await runWrangler("pages");
50
+ await endEventLoop();
51
+
52
+ expect(std.out).toMatchInlineSnapshot(`
52
53
  "wrangler pages
53
54
 
54
55
  ⚡️ Configure Cloudflare Pages
@@ -66,230 +67,230 @@ describe("pages", () => {
66
67
 
67
68
  🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"
68
69
  `);
69
- });
70
+ });
70
71
 
71
- describe("beta message for subcommands", () => {
72
- it("should display for pages:dev", async () => {
73
- await expect(
74
- runWrangler("pages dev")
75
- ).rejects.toThrowErrorMatchingInlineSnapshot(
76
- `"Must specify a directory of static assets to serve or a command to run."`
77
- );
72
+ describe("beta message for subcommands", () => {
73
+ it("should display for pages:dev", async () => {
74
+ await expect(
75
+ runWrangler("pages dev")
76
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
77
+ `"Must specify a directory of static assets to serve or a command to run."`
78
+ );
78
79
 
79
- expect(std.out).toMatchInlineSnapshot(`
80
+ expect(std.out).toMatchInlineSnapshot(`
80
81
  "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
81
82
 
82
83
  If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
83
84
  `);
84
- });
85
+ });
85
86
 
86
- it("should display for pages:functions:build", async () => {
87
- await expect(runWrangler("pages functions build")).rejects.toThrowError();
87
+ it("should display for pages:functions:build", async () => {
88
+ await expect(runWrangler("pages functions build")).rejects.toThrowError();
88
89
 
89
- expect(std.out).toMatchInlineSnapshot(`
90
+ expect(std.out).toMatchInlineSnapshot(`
90
91
  "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
91
92
 
92
93
  If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
93
94
  `);
94
- });
95
- });
96
-
97
- describe("project list", () => {
98
- mockAccountId();
99
- mockApiToken();
100
-
101
- afterEach(() => {
102
- unsetAllMocks();
103
- });
104
-
105
- function mockListRequest(projects: unknown[]) {
106
- const requests = { count: 0 };
107
- setMockResponse(
108
- "/accounts/:accountId/pages/projects",
109
- ([_url, accountId], init, query) => {
110
- requests.count++;
111
- const pageSize = Number(query.get("per_page"));
112
- const page = Number(query.get("page"));
113
- const expectedPageSize = 10;
114
- const expectedPage = requests.count;
115
- assertLater(() => {
116
- expect(accountId).toEqual("some-account-id");
117
- expect(pageSize).toEqual(expectedPageSize);
118
- expect(page).toEqual(expectedPage);
119
- expect(init).toEqual({});
120
- });
121
- return projects.slice((page - 1) * pageSize, page * pageSize);
122
- }
123
- );
124
- return requests;
125
- }
126
-
127
- it("should make request to list projects", async () => {
128
- const projects: Project[] = [
129
- {
130
- name: "dogs",
131
- subdomain: "docs.pages.dev",
132
- domains: ["dogs.pages.dev"],
133
- source: {
134
- type: "github",
135
- },
136
- latest_deployment: {
137
- modified_on: "2021-11-17T14:52:26.133835Z",
138
- },
139
- created_on: "2021-11-17T14:52:26.133835Z",
140
- production_branch: "main",
141
- },
142
- {
143
- name: "cats",
144
- subdomain: "cats.pages.dev",
145
- domains: ["cats.pages.dev", "kitten.com"],
146
- latest_deployment: {
147
- modified_on: "2021-11-17T14:52:26.133835Z",
148
- },
149
- created_on: "2021-11-17T14:52:26.133835Z",
150
- production_branch: "main",
151
- },
152
- ];
153
-
154
- const requests = mockListRequest(projects);
155
- await runWrangler("pages project list");
156
-
157
- expect(requests.count).toBe(1);
158
- });
159
-
160
- it("should make multiple requests for paginated results", async () => {
161
- const projects: Project[] = [];
162
- for (let i = 0; i < 15; i++) {
163
- projects.push({
164
- name: "dogs" + i,
165
- subdomain: i + "dogs.pages.dev",
166
- domains: [i + "dogs.pages.dev"],
167
- source: {
168
- type: "github",
169
- },
170
- latest_deployment: {
171
- modified_on: "2021-11-17T14:52:26.133835Z",
172
- },
173
- created_on: "2021-11-17T14:52:26.133835Z",
174
- production_branch: "main",
175
- });
176
- }
177
- const requests = mockListRequest(projects);
178
- await runWrangler("pages project list");
179
- expect(requests.count).toEqual(2);
180
- });
181
- });
182
-
183
- describe("project create", () => {
184
- mockAccountId();
185
- mockApiToken();
186
-
187
- afterEach(() => {
188
- unsetAllMocks();
189
- });
190
-
191
- it("should create a project with a production branch", async () => {
192
- setMockResponse(
193
- "/accounts/:accountId/pages/projects",
194
- ([_url, accountId], init) => {
195
- const body = JSON.parse(init.body as string);
196
- assertLater(() => {
197
- expect(accountId).toEqual("some-account-id");
198
- expect(init.method).toEqual("POST");
199
- expect(body).toEqual({
200
- name: "a-new-project",
201
- production_branch: "main",
202
- });
203
- });
204
- return {
205
- name: "a-new-project",
206
- subdomain: "a-new-project.pages.dev",
207
- production_branch: "main",
208
- };
209
- }
210
- );
211
- await runWrangler(
212
- "pages project create a-new-project --production-branch=main"
213
- );
214
- expect(std.out).toMatchInlineSnapshot(`
95
+ });
96
+ });
97
+
98
+ describe("project list", () => {
99
+ mockAccountId();
100
+ mockApiToken();
101
+
102
+ afterEach(() => {
103
+ unsetAllMocks();
104
+ });
105
+
106
+ function mockListRequest(projects: unknown[]) {
107
+ const requests = { count: 0 };
108
+ setMockResponse(
109
+ "/accounts/:accountId/pages/projects",
110
+ ([_url, accountId], init, query) => {
111
+ requests.count++;
112
+ const pageSize = Number(query.get("per_page"));
113
+ const page = Number(query.get("page"));
114
+ const expectedPageSize = 10;
115
+ const expectedPage = requests.count;
116
+ assertLater(() => {
117
+ expect(accountId).toEqual("some-account-id");
118
+ expect(pageSize).toEqual(expectedPageSize);
119
+ expect(page).toEqual(expectedPage);
120
+ expect(init).toEqual({});
121
+ });
122
+ return projects.slice((page - 1) * pageSize, page * pageSize);
123
+ }
124
+ );
125
+ return requests;
126
+ }
127
+
128
+ it("should make request to list projects", async () => {
129
+ const projects: Project[] = [
130
+ {
131
+ name: "dogs",
132
+ subdomain: "docs.pages.dev",
133
+ domains: ["dogs.pages.dev"],
134
+ source: {
135
+ type: "github",
136
+ },
137
+ latest_deployment: {
138
+ modified_on: "2021-11-17T14:52:26.133835Z",
139
+ },
140
+ created_on: "2021-11-17T14:52:26.133835Z",
141
+ production_branch: "main",
142
+ },
143
+ {
144
+ name: "cats",
145
+ subdomain: "cats.pages.dev",
146
+ domains: ["cats.pages.dev", "kitten.com"],
147
+ latest_deployment: {
148
+ modified_on: "2021-11-17T14:52:26.133835Z",
149
+ },
150
+ created_on: "2021-11-17T14:52:26.133835Z",
151
+ production_branch: "main",
152
+ },
153
+ ];
154
+
155
+ const requests = mockListRequest(projects);
156
+ await runWrangler("pages project list");
157
+
158
+ expect(requests.count).toBe(1);
159
+ });
160
+
161
+ it("should make multiple requests for paginated results", async () => {
162
+ const projects: Project[] = [];
163
+ for (let i = 0; i < 15; i++) {
164
+ projects.push({
165
+ name: "dogs" + i,
166
+ subdomain: i + "dogs.pages.dev",
167
+ domains: [i + "dogs.pages.dev"],
168
+ source: {
169
+ type: "github",
170
+ },
171
+ latest_deployment: {
172
+ modified_on: "2021-11-17T14:52:26.133835Z",
173
+ },
174
+ created_on: "2021-11-17T14:52:26.133835Z",
175
+ production_branch: "main",
176
+ });
177
+ }
178
+ const requests = mockListRequest(projects);
179
+ await runWrangler("pages project list");
180
+ expect(requests.count).toEqual(2);
181
+ });
182
+ });
183
+
184
+ describe("project create", () => {
185
+ mockAccountId();
186
+ mockApiToken();
187
+
188
+ afterEach(() => {
189
+ unsetAllMocks();
190
+ });
191
+
192
+ it("should create a project with a production branch", async () => {
193
+ setMockResponse(
194
+ "/accounts/:accountId/pages/projects",
195
+ ([_url, accountId], init) => {
196
+ const body = JSON.parse(init.body as string);
197
+ assertLater(() => {
198
+ expect(accountId).toEqual("some-account-id");
199
+ expect(init.method).toEqual("POST");
200
+ expect(body).toEqual({
201
+ name: "a-new-project",
202
+ production_branch: "main",
203
+ });
204
+ });
205
+ return {
206
+ name: "a-new-project",
207
+ subdomain: "a-new-project.pages.dev",
208
+ production_branch: "main",
209
+ };
210
+ }
211
+ );
212
+ await runWrangler(
213
+ "pages project create a-new-project --production-branch=main"
214
+ );
215
+ expect(std.out).toMatchInlineSnapshot(`
215
216
  "✨ Successfully created the 'a-new-project' project. It will be available at https://a-new-project.pages.dev/ once you create your first deployment.
216
217
  To deploy a folder of assets, run 'wrangler pages publish [directory]'."
217
218
  `);
218
- });
219
- });
220
-
221
- describe("deployment list", () => {
222
- mockAccountId();
223
- mockApiToken();
224
-
225
- afterEach(() => {
226
- unsetAllMocks();
227
- });
228
- function mockListRequest(deployments: unknown[]) {
229
- const requests = { count: 0 };
230
- setMockResponse(
231
- "/accounts/:accountId/pages/projects/:project/deployments",
232
- ([_url, accountId, project]) => {
233
- requests.count++;
234
- assertLater(() => {
235
- expect(project).toEqual("images");
236
- expect(accountId).toEqual("some-account-id");
237
- });
238
- return deployments;
239
- }
240
- );
241
- return requests;
242
- }
243
-
244
- it("should make request to list deployments", async () => {
245
- const deployments: Deployment[] = [
246
- {
247
- id: "87bbc8fe-16be-45cd-81e0-63d722e82cdf",
248
- url: "https://87bbc8fe.images.pages.dev",
249
- environment: "preview",
250
- latest_stage: {
251
- ended_on: "2021-11-17T14:52:26.133835Z",
252
- status: "success",
253
- },
254
- deployment_trigger: {
255
- metadata: {
256
- branch: "main",
257
- commit_hash: "c7649364c4cb32ad4f65b530b9424e8be5bec9d6",
258
- },
259
- },
260
- project_name: "images",
261
- },
262
- ];
263
-
264
- const requests = mockListRequest(deployments);
265
- await runWrangler("pages deployment list --project-name=images");
266
-
267
- expect(requests.count).toBe(1);
268
- });
269
- });
270
-
271
- describe("deployment create", () => {
272
- let actualProcessEnvCI: string | undefined;
273
-
274
- mockAccountId();
275
- mockApiToken();
276
- runInTempDir();
277
-
278
- beforeEach(() => {
279
- actualProcessEnvCI = process.env.CI;
280
- process.env.CI = "true";
281
- });
282
-
283
- afterEach(() => {
284
- unsetAllMocks();
285
- process.env.CI = actualProcessEnvCI;
286
- });
287
-
288
- it("should be aliased with 'wrangler pages publish'", async () => {
289
- await runWrangler("pages publish --help");
290
- await endEventLoop();
291
-
292
- expect(std.out).toMatchInlineSnapshot(`
219
+ });
220
+ });
221
+
222
+ describe("deployment list", () => {
223
+ mockAccountId();
224
+ mockApiToken();
225
+
226
+ afterEach(() => {
227
+ unsetAllMocks();
228
+ });
229
+ function mockListRequest(deployments: unknown[]) {
230
+ const requests = { count: 0 };
231
+ setMockResponse(
232
+ "/accounts/:accountId/pages/projects/:project/deployments",
233
+ ([_url, accountId, project]) => {
234
+ requests.count++;
235
+ assertLater(() => {
236
+ expect(project).toEqual("images");
237
+ expect(accountId).toEqual("some-account-id");
238
+ });
239
+ return deployments;
240
+ }
241
+ );
242
+ return requests;
243
+ }
244
+
245
+ it("should make request to list deployments", async () => {
246
+ const deployments: Deployment[] = [
247
+ {
248
+ id: "87bbc8fe-16be-45cd-81e0-63d722e82cdf",
249
+ url: "https://87bbc8fe.images.pages.dev",
250
+ environment: "preview",
251
+ latest_stage: {
252
+ ended_on: "2021-11-17T14:52:26.133835Z",
253
+ status: "success",
254
+ },
255
+ deployment_trigger: {
256
+ metadata: {
257
+ branch: "main",
258
+ commit_hash: "c7649364c4cb32ad4f65b530b9424e8be5bec9d6",
259
+ },
260
+ },
261
+ project_name: "images",
262
+ },
263
+ ];
264
+
265
+ const requests = mockListRequest(deployments);
266
+ await runWrangler("pages deployment list --project-name=images");
267
+
268
+ expect(requests.count).toBe(1);
269
+ });
270
+ });
271
+
272
+ describe("deployment create", () => {
273
+ let actualProcessEnvCI: string | undefined;
274
+
275
+ mockAccountId();
276
+ mockApiToken();
277
+ runInTempDir();
278
+
279
+ beforeEach(() => {
280
+ actualProcessEnvCI = process.env.CI;
281
+ process.env.CI = "true";
282
+ });
283
+
284
+ afterEach(() => {
285
+ unsetAllMocks();
286
+ process.env.CI = actualProcessEnvCI;
287
+ });
288
+
289
+ it("should be aliased with 'wrangler pages publish'", async () => {
290
+ await runWrangler("pages publish --help");
291
+ await endEventLoop();
292
+
293
+ expect(std.out).toMatchInlineSnapshot(`
293
294
  "wrangler pages publish [directory]
294
295
 
295
296
  🆙 Publish a directory of static assets as a Pages deployment
@@ -298,7 +299,6 @@ describe("pages", () => {
298
299
  directory The directory of static files to upload [string]
299
300
 
300
301
  Flags:
301
- -c, --config Path to .toml configuration file [string]
302
302
  -h, --help Show help [boolean]
303
303
  -v, --version Show version number [boolean]
304
304
 
@@ -311,325 +311,314 @@ describe("pages", () => {
311
311
 
312
312
  🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose"
313
313
  `);
314
- });
315
-
316
- it("should upload a directory of files", async () => {
317
- writeFileSync("logo.png", "foobar");
318
-
319
- setMockResponse(
320
- "/accounts/:accountId/pages/projects/foo/upload-token",
321
- async ([_url, accountId]) => {
322
- assertLater(() => {
323
- expect(accountId).toEqual("some-account-id");
324
- });
325
-
326
- return {
327
- jwt: "<<funfetti-auth-jwt>>",
328
- };
329
- }
330
- );
331
-
332
- setMockResponse(
333
- "/pages/assets/check-missing",
334
- "POST",
335
- async (_, init) => {
336
- const body = JSON.parse(init.body as string) as { hashes: string[] };
337
- assertLater(() => {
338
- expect(init.headers).toMatchObject({
339
- Authorization: "Bearer <<funfetti-auth-jwt>>",
340
- });
341
- expect(body).toMatchObject({
342
- hashes: ["2082190357cfd3617ccfe04f340c6247"],
343
- });
344
- });
345
- return body.hashes;
346
- }
347
- );
348
-
349
- setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
350
- assertLater(() => {
351
- expect(init.headers).toMatchObject({
352
- Authorization: "Bearer <<funfetti-auth-jwt>>",
353
- });
354
- const body = JSON.parse(init.body as string) as UploadPayloadFile[];
355
- expect(body).toMatchObject([
356
- {
357
- key: "2082190357cfd3617ccfe04f340c6247",
358
- value: Buffer.from("foobar").toString("base64"),
359
- metadata: {
360
- contentType: "image/png",
361
- },
362
- base64: true,
363
- },
364
- ]);
365
- });
366
- });
367
-
368
- setMockResponse(
369
- "/accounts/:accountId/pages/projects/foo/deployments",
370
- async ([_url, accountId], init) => {
371
- assertLater(() => {
372
- expect(accountId).toEqual("some-account-id");
373
- expect(init.method).toEqual("POST");
374
- const body = init.body as FormData;
375
- const manifest = JSON.parse(body.get("manifest") as string);
376
- expect(manifest).toMatchInlineSnapshot(`
314
+ });
315
+
316
+ it("should upload a directory of files", async () => {
317
+ writeFileSync("logo.png", "foobar");
318
+
319
+ mockGetToken("<<funfetti-auth-jwt>>");
320
+
321
+ setMockResponse(
322
+ "/pages/assets/check-missing",
323
+ "POST",
324
+ async (_, init) => {
325
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
326
+ assertLater(() => {
327
+ expect(init.headers).toMatchObject({
328
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
329
+ });
330
+ expect(body).toMatchObject({
331
+ hashes: ["2082190357cfd3617ccfe04f340c6247"],
332
+ });
333
+ });
334
+ return body.hashes;
335
+ }
336
+ );
337
+
338
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
339
+ assertLater(() => {
340
+ expect(init.headers).toMatchObject({
341
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
342
+ });
343
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
344
+ expect(body).toMatchObject([
345
+ {
346
+ key: "2082190357cfd3617ccfe04f340c6247",
347
+ value: Buffer.from("foobar").toString("base64"),
348
+ metadata: {
349
+ contentType: "image/png",
350
+ },
351
+ base64: true,
352
+ },
353
+ ]);
354
+ });
355
+ });
356
+
357
+ setMockResponse(
358
+ "/accounts/:accountId/pages/projects/foo/deployments",
359
+ async ([_url, accountId], init) => {
360
+ assertLater(() => {
361
+ expect(accountId).toEqual("some-account-id");
362
+ expect(init.method).toEqual("POST");
363
+ const body = init.body as FormData;
364
+ const manifest = JSON.parse(body.get("manifest") as string);
365
+ expect(manifest).toMatchInlineSnapshot(`
377
366
  Object {
378
367
  "/logo.png": "2082190357cfd3617ccfe04f340c6247",
379
368
  }
380
369
  `);
381
- });
382
-
383
- return {
384
- url: "https://abcxyz.foo.pages.dev/",
385
- };
386
- }
387
- );
388
-
389
- await runWrangler("pages publish . --project-name=foo");
390
-
391
- // TODO: Unmounting somehow loses this output
392
-
393
- // expect(std.out).toMatchInlineSnapshot(`
394
- // "✨ Success! Uploaded 1 files (TIMINGS)
395
-
396
- // ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
397
- // `);
398
- });
399
-
400
- it("should retry uploads", async () => {
401
- writeFileSync("logo.txt", "foobar");
402
-
403
- mockGetToken("<<funfetti-auth-jwt>>");
404
-
405
- setMockResponse(
406
- "/pages/assets/check-missing",
407
- "POST",
408
- async (_, init) => {
409
- const body = JSON.parse(init.body as string) as { hashes: string[] };
410
- assertLater(() => {
411
- expect(init.headers).toMatchObject({
412
- Authorization: "Bearer <<funfetti-auth-jwt>>",
413
- });
414
- expect(body).toMatchObject({
415
- hashes: ["1a98fb08af91aca4a7df1764a2c4ddb0"],
416
- });
417
- });
418
- return body.hashes;
419
- }
420
- );
421
-
422
- // Accumulate multiple requests then assert afterwards
423
- const requests: RequestInit[] = [];
424
- setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
425
- requests.push(init);
426
-
427
- if (requests.length < 2) {
428
- return createFetchResult(null, false, [
429
- {
430
- code: 800000,
431
- message: "Something exploded, please retry",
432
- },
433
- ]);
434
- } else {
435
- return createFetchResult(null, true);
436
- }
437
- });
438
-
439
- setMockResponse(
440
- "/accounts/:accountId/pages/projects/foo/deployments",
441
- async ([_url, accountId], init) => {
442
- assertLater(() => {
443
- expect(accountId).toEqual("some-account-id");
444
- expect(init.method).toEqual("POST");
445
- const body = init.body as FormData;
446
- const manifest = JSON.parse(body.get("manifest") as string);
447
- expect(manifest).toMatchInlineSnapshot(`
370
+ });
371
+
372
+ return {
373
+ url: "https://abcxyz.foo.pages.dev/",
374
+ };
375
+ }
376
+ );
377
+
378
+ await runWrangler("pages publish . --project-name=foo");
379
+
380
+ // TODO: Unmounting somehow loses this output
381
+
382
+ // expect(std.out).toMatchInlineSnapshot(`
383
+ // "✨ Success! Uploaded 1 files (TIMINGS)
384
+
385
+ // ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
386
+ // `);
387
+ });
388
+
389
+ it("should retry uploads", async () => {
390
+ writeFileSync("logo.txt", "foobar");
391
+
392
+ mockGetToken("<<funfetti-auth-jwt>>");
393
+
394
+ setMockResponse(
395
+ "/pages/assets/check-missing",
396
+ "POST",
397
+ async (_, init) => {
398
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
399
+ assertLater(() => {
400
+ expect(init.headers).toMatchObject({
401
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
402
+ });
403
+ expect(body).toMatchObject({
404
+ hashes: ["1a98fb08af91aca4a7df1764a2c4ddb0"],
405
+ });
406
+ });
407
+ return body.hashes;
408
+ }
409
+ );
410
+
411
+ // Accumulate multiple requests then assert afterwards
412
+ const requests: RequestInit[] = [];
413
+ setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
414
+ requests.push(init);
415
+
416
+ if (requests.length < 2) {
417
+ return createFetchResult(null, false, [
418
+ {
419
+ code: 800000,
420
+ message: "Something exploded, please retry",
421
+ },
422
+ ]);
423
+ } else {
424
+ return createFetchResult(null, true);
425
+ }
426
+ });
427
+
428
+ setMockResponse(
429
+ "/accounts/:accountId/pages/projects/foo/deployments",
430
+ async ([_url, accountId], init) => {
431
+ assertLater(() => {
432
+ expect(accountId).toEqual("some-account-id");
433
+ expect(init.method).toEqual("POST");
434
+ const body = init.body as FormData;
435
+ const manifest = JSON.parse(body.get("manifest") as string);
436
+ expect(manifest).toMatchInlineSnapshot(`
448
437
  Object {
449
438
  "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
450
439
  }
451
440
  `);
452
- });
453
-
454
- return {
455
- url: "https://abcxyz.foo.pages.dev/",
456
- };
457
- }
458
- );
459
-
460
- await runWrangler("pages publish . --project-name=foo");
461
-
462
- // Assert two identical requests
463
- expect(requests.length).toBe(2);
464
- for (const init of requests) {
465
- assertLater(() => {
466
- expect(init.headers).toMatchObject({
467
- Authorization: "Bearer <<funfetti-auth-jwt>>",
468
- });
469
-
470
- const body = JSON.parse(init.body as string) as UploadPayloadFile[];
471
- expect(body).toMatchObject([
472
- {
473
- key: "1a98fb08af91aca4a7df1764a2c4ddb0",
474
- value: Buffer.from("foobar").toString("base64"),
475
- metadata: {
476
- contentType: "text/plain",
477
- },
478
- base64: true,
479
- },
480
- ]);
481
- });
482
- }
483
-
484
- expect(std.out).toMatchInlineSnapshot(`
441
+ });
442
+
443
+ return {
444
+ url: "https://abcxyz.foo.pages.dev/",
445
+ };
446
+ }
447
+ );
448
+
449
+ await runWrangler("pages publish . --project-name=foo");
450
+
451
+ // Assert two identical requests
452
+ expect(requests.length).toBe(2);
453
+ for (const init of requests) {
454
+ assertLater(() => {
455
+ expect(init.headers).toMatchObject({
456
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
457
+ });
458
+
459
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
460
+ expect(body).toMatchObject([
461
+ {
462
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
463
+ value: Buffer.from("foobar").toString("base64"),
464
+ metadata: {
465
+ contentType: "text/plain",
466
+ },
467
+ base64: true,
468
+ },
469
+ ]);
470
+ });
471
+ }
472
+
473
+ expect(std.out).toMatchInlineSnapshot(`
485
474
  "✨ Success! Uploaded 1 files (TIMINGS)
486
475
 
487
476
  ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
488
477
  `);
489
- });
490
-
491
- it("should refetch a JWT if it expires while uploading", async () => {
492
- writeFileSync("logo.txt", "foobar");
493
-
494
- const cancelMockGetToken = mockGetToken("<<funfetti-auth-jwt>>");
495
-
496
- setMockResponse(
497
- "/pages/assets/check-missing",
498
- "POST",
499
- async (_, init) => {
500
- const body = JSON.parse(init.body as string) as { hashes: string[] };
501
- assertLater(() => {
502
- expect(init.headers).toMatchObject({
503
- Authorization: "Bearer <<funfetti-auth-jwt>>",
504
- });
505
- expect(body).toMatchObject({
506
- hashes: ["1a98fb08af91aca4a7df1764a2c4ddb0"],
507
- });
508
- });
509
- return body.hashes;
510
- }
511
- );
512
-
513
- // Accumulate multiple requests then assert afterwards
514
- const requests: RequestInit[] = [];
515
- setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
516
- requests.push(init);
517
-
518
- // Fail just the first request
519
- if (requests.length < 2) {
520
- cancelMockGetToken();
521
- mockGetToken("<<funfetti-auth-jwt2>>");
522
- return createFetchResult(null, false, [
523
- {
524
- code: 8000013,
525
- message: "Authorization failed",
526
- },
527
- ]);
528
- } else {
529
- return createFetchResult(null, true);
530
- }
531
- });
532
-
533
- setMockResponse(
534
- "/accounts/:accountId/pages/projects/foo/deployments",
535
- async ([_url, accountId], init) => {
536
- assertLater(() => {
537
- expect(accountId).toEqual("some-account-id");
538
- expect(init.method).toEqual("POST");
539
- const body = init.body as FormData;
540
- const manifest = JSON.parse(body.get("manifest") as string);
541
- expect(manifest).toMatchInlineSnapshot(`
478
+ });
479
+
480
+ it("should refetch a JWT if it expires while uploading", async () => {
481
+ writeFileSync("logo.txt", "foobar");
482
+
483
+ const cancelMockGetToken = mockGetToken("<<funfetti-auth-jwt>>");
484
+
485
+ setMockResponse(
486
+ "/pages/assets/check-missing",
487
+ "POST",
488
+ async (_, init) => {
489
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
490
+ assertLater(() => {
491
+ expect(init.headers).toMatchObject({
492
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
493
+ });
494
+ expect(body).toMatchObject({
495
+ hashes: ["1a98fb08af91aca4a7df1764a2c4ddb0"],
496
+ });
497
+ });
498
+ return body.hashes;
499
+ }
500
+ );
501
+
502
+ // Accumulate multiple requests then assert afterwards
503
+ const requests: RequestInit[] = [];
504
+ setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
505
+ requests.push(init);
506
+
507
+ // Fail just the first request
508
+ if (requests.length < 2) {
509
+ cancelMockGetToken();
510
+ mockGetToken("<<funfetti-auth-jwt2>>");
511
+ return createFetchResult(null, false, [
512
+ {
513
+ code: 8000013,
514
+ message: "Authorization failed",
515
+ },
516
+ ]);
517
+ } else {
518
+ return createFetchResult(null, true);
519
+ }
520
+ });
521
+
522
+ setMockResponse(
523
+ "/accounts/:accountId/pages/projects/foo/deployments",
524
+ async ([_url, accountId], init) => {
525
+ assertLater(() => {
526
+ expect(accountId).toEqual("some-account-id");
527
+ expect(init.method).toEqual("POST");
528
+ const body = init.body as FormData;
529
+ const manifest = JSON.parse(body.get("manifest") as string);
530
+ expect(manifest).toMatchInlineSnapshot(`
542
531
  Object {
543
532
  "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
544
533
  }
545
534
  `);
546
- });
547
-
548
- return {
549
- url: "https://abcxyz.foo.pages.dev/",
550
- };
551
- }
552
- );
553
-
554
- await runWrangler("pages publish . --project-name=foo");
555
-
556
- // Assert two requests
557
- expect(requests.length).toBe(2);
558
-
559
- expect(requests[0].headers).toMatchObject({
560
- Authorization: "Bearer <<funfetti-auth-jwt>>",
561
- });
562
-
563
- expect(requests[1].headers).toMatchObject({
564
- Authorization: "Bearer <<funfetti-auth-jwt2>>",
565
- });
566
-
567
- for (const init of requests) {
568
- const body = JSON.parse(init.body as string) as UploadPayloadFile[];
569
- expect(body).toMatchObject([
570
- {
571
- key: "1a98fb08af91aca4a7df1764a2c4ddb0",
572
- value: Buffer.from("foobar").toString("base64"),
573
- metadata: {
574
- contentType: "text/plain",
575
- },
576
- base64: true,
577
- },
578
- ]);
579
- }
580
-
581
- expect(std.out).toMatchInlineSnapshot(`
535
+ });
536
+
537
+ return {
538
+ url: "https://abcxyz.foo.pages.dev/",
539
+ };
540
+ }
541
+ );
542
+
543
+ await runWrangler("pages publish . --project-name=foo");
544
+
545
+ // Assert two requests
546
+ expect(requests.length).toBe(2);
547
+
548
+ expect(requests[0].headers).toMatchObject({
549
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
550
+ });
551
+
552
+ expect(requests[1].headers).toMatchObject({
553
+ Authorization: "Bearer <<funfetti-auth-jwt2>>",
554
+ });
555
+
556
+ for (const init of requests) {
557
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
558
+ expect(body).toMatchObject([
559
+ {
560
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
561
+ value: Buffer.from("foobar").toString("base64"),
562
+ metadata: {
563
+ contentType: "text/plain",
564
+ },
565
+ base64: true,
566
+ },
567
+ ]);
568
+ }
569
+
570
+ expect(std.out).toMatchInlineSnapshot(`
582
571
  "✨ Success! Uploaded 1 files (TIMINGS)
583
572
 
584
573
  ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
585
574
  `);
586
- });
587
-
588
- it("should try to use multiple buckets (up to the max concurrency)", async () => {
589
- writeFileSync("logo.txt", "foobar");
590
- writeFileSync("logo.png", "foobar");
591
- writeFileSync("logo.html", "foobar");
592
- writeFileSync("logo.js", "foobar");
593
-
594
- mockGetToken("<<funfetti-auth-jwt>>");
595
-
596
- setMockResponse(
597
- "/pages/assets/check-missing",
598
- "POST",
599
- async (_, init) => {
600
- const body = JSON.parse(init.body as string) as { hashes: string[] };
601
- assertLater(() => {
602
- expect(init.headers).toMatchObject({
603
- Authorization: "Bearer <<funfetti-auth-jwt>>",
604
- });
605
- expect(body).toMatchObject({
606
- hashes: expect.arrayContaining([
607
- "d96fef225537c9f5e44a3cb27fd0b492",
608
- "2082190357cfd3617ccfe04f340c6247",
609
- "6be321bef99e758250dac034474ddbb8",
610
- "1a98fb08af91aca4a7df1764a2c4ddb0",
611
- ]),
612
- });
613
- });
614
- return body.hashes;
615
- }
616
- );
617
-
618
- // Accumulate multiple requests then assert afterwards
619
- const requests: RequestInit[] = [];
620
- setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
621
- requests.push(init);
622
- });
623
-
624
- setMockResponse(
625
- "/accounts/:accountId/pages/projects/foo/deployments",
626
- async ([_url, accountId], init) => {
627
- assertLater(() => {
628
- expect(accountId).toEqual("some-account-id");
629
- expect(init.method).toEqual("POST");
630
- const body = init.body as FormData;
631
- const manifest = JSON.parse(body.get("manifest") as string);
632
- expect(manifest).toMatchInlineSnapshot(`
575
+ });
576
+
577
+ it("should try to use multiple buckets (up to the max concurrency)", async () => {
578
+ writeFileSync("logo.txt", "foobar");
579
+ writeFileSync("logo.png", "foobar");
580
+ writeFileSync("logo.html", "foobar");
581
+ writeFileSync("logo.js", "foobar");
582
+
583
+ mockGetToken("<<funfetti-auth-jwt>>");
584
+
585
+ setMockResponse(
586
+ "/pages/assets/check-missing",
587
+ "POST",
588
+ async (_, init) => {
589
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
590
+ assertLater(() => {
591
+ expect(init.headers).toMatchObject({
592
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
593
+ });
594
+ expect(body).toMatchObject({
595
+ hashes: expect.arrayContaining([
596
+ "d96fef225537c9f5e44a3cb27fd0b492",
597
+ "2082190357cfd3617ccfe04f340c6247",
598
+ "6be321bef99e758250dac034474ddbb8",
599
+ "1a98fb08af91aca4a7df1764a2c4ddb0",
600
+ ]),
601
+ });
602
+ });
603
+ return body.hashes;
604
+ }
605
+ );
606
+
607
+ // Accumulate multiple requests then assert afterwards
608
+ const requests: RequestInit[] = [];
609
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
610
+ requests.push(init);
611
+ });
612
+
613
+ setMockResponse(
614
+ "/accounts/:accountId/pages/projects/foo/deployments",
615
+ async ([_url, accountId], init) => {
616
+ assertLater(() => {
617
+ expect(accountId).toEqual("some-account-id");
618
+ expect(init.method).toEqual("POST");
619
+ const body = init.body as FormData;
620
+ const manifest = JSON.parse(body.get("manifest") as string);
621
+ expect(manifest).toMatchInlineSnapshot(`
633
622
  Object {
634
623
  "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
635
624
  "/logo.js": "6be321bef99e758250dac034474ddbb8",
@@ -637,118 +626,629 @@ describe("pages", () => {
637
626
  "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
638
627
  }
639
628
  `);
640
- });
641
-
642
- return {
643
- url: "https://abcxyz.foo.pages.dev/",
644
- };
645
- }
646
- );
647
-
648
- await runWrangler("pages publish . --project-name=foo");
649
-
650
- // We have 3 buckets, so expect 3 uploads
651
- expect(requests.length).toBe(3);
652
- const bodies: UploadPayloadFile[][] = [];
653
- for (const init of requests) {
654
- expect(init.headers).toMatchObject({
655
- Authorization: "Bearer <<funfetti-auth-jwt>>",
656
- });
657
- bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
658
- }
659
- // First bucket should end up with 2 files
660
- expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
661
- // But we don't know the order, so flatten and test without ordering
662
- expect(bodies.flatMap((b) => b)).toEqual(
663
- expect.arrayContaining([
664
- {
665
- base64: true,
666
- key: "d96fef225537c9f5e44a3cb27fd0b492",
667
- metadata: { contentType: "text/html" },
668
- value: "Zm9vYmFy",
669
- },
670
- {
671
- base64: true,
672
- key: "1a98fb08af91aca4a7df1764a2c4ddb0",
673
- metadata: { contentType: "text/plain" },
674
- value: "Zm9vYmFy",
675
- },
676
- {
677
- base64: true,
678
- key: "6be321bef99e758250dac034474ddbb8",
679
- metadata: { contentType: "application/javascript" },
680
- value: "Zm9vYmFy",
681
- },
682
- {
683
- base64: true,
684
- key: "2082190357cfd3617ccfe04f340c6247",
685
- metadata: { contentType: "image/png" },
686
- value: "Zm9vYmFy",
687
- },
688
- ])
689
- );
690
-
691
- expect(std.out).toMatchInlineSnapshot(`
629
+ });
630
+
631
+ return {
632
+ url: "https://abcxyz.foo.pages.dev/",
633
+ };
634
+ }
635
+ );
636
+
637
+ await runWrangler("pages publish . --project-name=foo");
638
+
639
+ // We have 3 buckets, so expect 3 uploads
640
+ expect(requests.length).toBe(3);
641
+ const bodies: UploadPayloadFile[][] = [];
642
+ for (const init of requests) {
643
+ expect(init.headers).toMatchObject({
644
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
645
+ });
646
+ bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
647
+ }
648
+ // First bucket should end up with 2 files
649
+ expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
650
+ // But we don't know the order, so flatten and test without ordering
651
+ expect(bodies.flatMap((b) => b)).toEqual(
652
+ expect.arrayContaining([
653
+ {
654
+ base64: true,
655
+ key: "d96fef225537c9f5e44a3cb27fd0b492",
656
+ metadata: { contentType: "text/html" },
657
+ value: "Zm9vYmFy",
658
+ },
659
+ {
660
+ base64: true,
661
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
662
+ metadata: { contentType: "text/plain" },
663
+ value: "Zm9vYmFy",
664
+ },
665
+ {
666
+ base64: true,
667
+ key: "6be321bef99e758250dac034474ddbb8",
668
+ metadata: { contentType: "application/javascript" },
669
+ value: "Zm9vYmFy",
670
+ },
671
+ {
672
+ base64: true,
673
+ key: "2082190357cfd3617ccfe04f340c6247",
674
+ metadata: { contentType: "image/png" },
675
+ value: "Zm9vYmFy",
676
+ },
677
+ ])
678
+ );
679
+
680
+ expect(std.out).toMatchInlineSnapshot(`
692
681
  "✨ Success! Uploaded 4 files (TIMINGS)
693
682
 
694
683
  ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
695
684
  `);
696
- });
697
-
698
- it("should not error when directory names contain periods and houses a extensionless file", async () => {
699
- mkdirSync(".well-known");
700
- // Note: same content as previous test, but since it's a different extension,
701
- // it hashes to a different value
702
- writeFileSync(".well-known/foobar", "foobar");
703
-
704
- mockGetToken("<<funfetti-auth-jwt>>");
705
-
706
- setMockResponse(
707
- "/pages/assets/check-missing",
708
- "POST",
709
- async (_, init) => {
710
- const body = JSON.parse(init.body as string) as { hashes: string[] };
711
- assertLater(() => {
712
- expect(init.headers).toMatchObject({
713
- Authorization: "Bearer <<funfetti-auth-jwt>>",
714
- });
715
- expect(body).toMatchObject({
716
- hashes: ["7b764dacfd211bebd8077828a7ddefd7"],
717
- });
718
- });
719
- return body.hashes;
720
- }
721
- );
722
-
723
- setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
724
- assertLater(() => {
725
- expect(init.headers).toMatchObject({
726
- Authorization: "Bearer <<funfetti-auth-jwt>>",
727
- });
728
- const body = JSON.parse(init.body as string) as UploadPayloadFile[];
729
- expect(body).toMatchObject([
730
- {
731
- key: "7b764dacfd211bebd8077828a7ddefd7",
732
- value: Buffer.from("foobar").toString("base64"),
733
- metadata: {
734
- contentType: "application/octet-stream",
735
- },
736
- base64: true,
737
- },
738
- ]);
739
- });
740
- });
741
-
742
- setMockResponse(
743
- "/accounts/:accountId/pages/projects/foo/deployments",
744
- async () => ({
745
- url: "https://abcxyz.foo.pages.dev/",
746
- })
747
- );
748
-
749
- await runWrangler("pages publish . --project-name=foo");
750
-
751
- expect(std.err).toMatchInlineSnapshot(`""`);
752
- });
753
- });
685
+ });
686
+
687
+ it("should resolve child directories correctly", async () => {
688
+ mkdirSync("public");
689
+ mkdirSync("public/imgs");
690
+ writeFileSync("public/logo.txt", "foobar");
691
+ writeFileSync("public/imgs/logo.png", "foobar");
692
+ writeFileSync("public/logo.html", "foobar");
693
+ writeFileSync("public/logo.js", "foobar");
694
+
695
+ mockGetToken("<<funfetti-auth-jwt>>");
696
+
697
+ setMockResponse(
698
+ "/pages/assets/check-missing",
699
+ "POST",
700
+ async (_, init) => {
701
+ const body = JSON.parse(init.body as string) as {
702
+ hashes: string[];
703
+ };
704
+ assertLater(() => {
705
+ expect(init.headers).toMatchObject({
706
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
707
+ });
708
+ expect(body).toMatchObject({
709
+ hashes: expect.arrayContaining([
710
+ "d96fef225537c9f5e44a3cb27fd0b492",
711
+ "2082190357cfd3617ccfe04f340c6247",
712
+ "6be321bef99e758250dac034474ddbb8",
713
+ "1a98fb08af91aca4a7df1764a2c4ddb0",
714
+ ]),
715
+ });
716
+ });
717
+ return body.hashes;
718
+ }
719
+ );
720
+
721
+ // Accumulate multiple requests then assert afterwards
722
+ const requests: RequestInit[] = [];
723
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
724
+ requests.push(init);
725
+ });
726
+
727
+ setMockResponse(
728
+ "/accounts/:accountId/pages/projects/foo/deployments",
729
+ async ([_url, accountId], init) => {
730
+ assertLater(() => {
731
+ expect(accountId).toEqual("some-account-id");
732
+ expect(init.method).toEqual("POST");
733
+ const body = init.body as FormData;
734
+ const manifest = JSON.parse(body.get("manifest") as string);
735
+ expect(manifest).toMatchInlineSnapshot(`
736
+ Object {
737
+ "/imgs/logo.png": "2082190357cfd3617ccfe04f340c6247",
738
+ "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
739
+ "/logo.js": "6be321bef99e758250dac034474ddbb8",
740
+ "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
741
+ }
742
+ `);
743
+ });
744
+
745
+ return {
746
+ url: "https://abcxyz.foo.pages.dev/",
747
+ };
748
+ }
749
+ );
750
+
751
+ await runWrangler(`pages publish public --project-name=foo`);
752
+
753
+ // We have 3 buckets, so expect 3 uploads
754
+ expect(requests.length).toBe(3);
755
+ const bodies: UploadPayloadFile[][] = [];
756
+ for (const init of requests) {
757
+ expect(init.headers).toMatchObject({
758
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
759
+ });
760
+ bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
761
+ }
762
+ // First bucket should end up with 2 files
763
+ expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
764
+ // But we don't know the order, so flatten and test without ordering
765
+ expect(bodies.flatMap((b) => b)).toEqual(
766
+ expect.arrayContaining([
767
+ {
768
+ base64: true,
769
+ key: "d96fef225537c9f5e44a3cb27fd0b492",
770
+ metadata: { contentType: "text/html" },
771
+ value: "Zm9vYmFy",
772
+ },
773
+ {
774
+ base64: true,
775
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
776
+ metadata: { contentType: "text/plain" },
777
+ value: "Zm9vYmFy",
778
+ },
779
+ {
780
+ base64: true,
781
+ key: "6be321bef99e758250dac034474ddbb8",
782
+ metadata: { contentType: "application/javascript" },
783
+ value: "Zm9vYmFy",
784
+ },
785
+ {
786
+ base64: true,
787
+ key: "2082190357cfd3617ccfe04f340c6247",
788
+ metadata: { contentType: "image/png" },
789
+ value: "Zm9vYmFy",
790
+ },
791
+ ])
792
+ );
793
+
794
+ expect(std.out).toMatchInlineSnapshot(`
795
+ "✨ Success! Uploaded 4 files (TIMINGS)
796
+
797
+ ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
798
+ `);
799
+ });
800
+
801
+ it("should resolve the current directory correctly", async () => {
802
+ mkdirSync("public");
803
+ mkdirSync("public/imgs");
804
+ writeFileSync("public/logo.txt", "foobar");
805
+ writeFileSync("public/imgs/logo.png", "foobar");
806
+ writeFileSync("public/logo.html", "foobar");
807
+ writeFileSync("public/logo.js", "foobar");
808
+
809
+ mockGetToken("<<funfetti-auth-jwt>>");
810
+
811
+ setMockResponse(
812
+ "/pages/assets/check-missing",
813
+ "POST",
814
+ async (_, init) => {
815
+ const body = JSON.parse(init.body as string) as {
816
+ hashes: string[];
817
+ };
818
+ assertLater(() => {
819
+ expect(init.headers).toMatchObject({
820
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
821
+ });
822
+ expect(body).toMatchObject({
823
+ hashes: expect.arrayContaining([
824
+ "d96fef225537c9f5e44a3cb27fd0b492",
825
+ "2082190357cfd3617ccfe04f340c6247",
826
+ "6be321bef99e758250dac034474ddbb8",
827
+ "1a98fb08af91aca4a7df1764a2c4ddb0",
828
+ ]),
829
+ });
830
+ });
831
+ return body.hashes;
832
+ }
833
+ );
834
+
835
+ // Accumulate multiple requests then assert afterwards
836
+ const requests: RequestInit[] = [];
837
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
838
+ requests.push(init);
839
+ });
840
+
841
+ setMockResponse(
842
+ "/accounts/:accountId/pages/projects/foo/deployments",
843
+ async ([_url, accountId], init) => {
844
+ assertLater(() => {
845
+ expect(accountId).toEqual("some-account-id");
846
+ expect(init.method).toEqual("POST");
847
+ const body = init.body as FormData;
848
+ const manifest = JSON.parse(body.get("manifest") as string);
849
+ expect(manifest).toMatchInlineSnapshot(`
850
+ Object {
851
+ "/imgs/logo.png": "2082190357cfd3617ccfe04f340c6247",
852
+ "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
853
+ "/logo.js": "6be321bef99e758250dac034474ddbb8",
854
+ "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
855
+ }
856
+ `);
857
+ });
858
+
859
+ return {
860
+ url: "https://abcxyz.foo.pages.dev/",
861
+ };
862
+ }
863
+ );
864
+
865
+ chdir("public");
866
+ await runWrangler(`pages publish . --project-name=foo`);
867
+
868
+ // We have 3 buckets, so expect 3 uploads
869
+ expect(requests.length).toBe(3);
870
+ const bodies: UploadPayloadFile[][] = [];
871
+ for (const init of requests) {
872
+ expect(init.headers).toMatchObject({
873
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
874
+ });
875
+ bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
876
+ }
877
+ // First bucket should end up with 2 files
878
+ expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
879
+ // But we don't know the order, so flatten and test without ordering
880
+ expect(bodies.flatMap((b) => b)).toEqual(
881
+ expect.arrayContaining([
882
+ {
883
+ base64: true,
884
+ key: "d96fef225537c9f5e44a3cb27fd0b492",
885
+ metadata: { contentType: "text/html" },
886
+ value: "Zm9vYmFy",
887
+ },
888
+ {
889
+ base64: true,
890
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
891
+ metadata: { contentType: "text/plain" },
892
+ value: "Zm9vYmFy",
893
+ },
894
+ {
895
+ base64: true,
896
+ key: "6be321bef99e758250dac034474ddbb8",
897
+ metadata: { contentType: "application/javascript" },
898
+ value: "Zm9vYmFy",
899
+ },
900
+ {
901
+ base64: true,
902
+ key: "2082190357cfd3617ccfe04f340c6247",
903
+ metadata: { contentType: "image/png" },
904
+ value: "Zm9vYmFy",
905
+ },
906
+ ])
907
+ );
908
+
909
+ expect(std.out).toMatchInlineSnapshot(`
910
+ "✨ Success! Uploaded 4 files (TIMINGS)
911
+
912
+ ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
913
+ `);
914
+ });
915
+
916
+ it("should not error when directory names contain periods and houses a extensionless file", async () => {
917
+ mkdirSync(".well-known");
918
+ // Note: same content as previous test, but since it's a different extension,
919
+ // it hashes to a different value
920
+ writeFileSync(".well-known/foobar", "foobar");
921
+
922
+ mockGetToken("<<funfetti-auth-jwt>>");
923
+
924
+ setMockResponse(
925
+ "/pages/assets/check-missing",
926
+ "POST",
927
+ async (_, init) => {
928
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
929
+ assertLater(() => {
930
+ expect(init.headers).toMatchObject({
931
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
932
+ });
933
+ expect(body).toMatchObject({
934
+ hashes: ["7b764dacfd211bebd8077828a7ddefd7"],
935
+ });
936
+ });
937
+ return body.hashes;
938
+ }
939
+ );
940
+
941
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
942
+ assertLater(() => {
943
+ expect(init.headers).toMatchObject({
944
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
945
+ });
946
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
947
+ expect(body).toMatchObject([
948
+ {
949
+ key: "7b764dacfd211bebd8077828a7ddefd7",
950
+ value: Buffer.from("foobar").toString("base64"),
951
+ metadata: {
952
+ contentType: "application/octet-stream",
953
+ },
954
+ base64: true,
955
+ },
956
+ ]);
957
+ });
958
+ });
959
+
960
+ setMockResponse(
961
+ "/accounts/:accountId/pages/projects/foo/deployments",
962
+ async () => ({
963
+ url: "https://abcxyz.foo.pages.dev/",
964
+ })
965
+ );
966
+
967
+ await runWrangler("pages publish . --project-name=foo");
968
+
969
+ expect(std.err).toMatchInlineSnapshot(`""`);
970
+ });
971
+
972
+ it("should throw an error if user attempts to use config with pages", async () => {
973
+ await expect(
974
+ runWrangler("pages dev --config foo.toml")
975
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
976
+ `"Pages does not support wrangler.toml"`
977
+ );
978
+ await expect(
979
+ runWrangler("pages publish --config foo.toml")
980
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
981
+ `"Pages does not support wrangler.toml"`
982
+ );
983
+ });
984
+ });
985
+
986
+ describe("project upload", () => {
987
+ const ENV_COPY = process.env;
988
+
989
+ mockAccountId();
990
+ mockApiToken();
991
+ runInTempDir();
992
+
993
+ beforeEach(() => {
994
+ process.env.CI = "true";
995
+ process.env.CF_PAGES_UPLOAD_JWT = "<<funfetti-auth-jwt>>";
996
+ });
997
+
998
+ afterEach(() => {
999
+ unsetAllMocks();
1000
+ process.env = ENV_COPY;
1001
+ });
1002
+
1003
+ it("should upload a directory of files with a provided JWT", async () => {
1004
+ writeFileSync("logo.png", "foobar");
1005
+
1006
+ setMockResponse(
1007
+ "/pages/assets/check-missing",
1008
+ "POST",
1009
+ async (_, init) => {
1010
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
1011
+ assertLater(() => {
1012
+ expect(init.headers).toMatchObject({
1013
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1014
+ });
1015
+ expect(body).toMatchObject({
1016
+ hashes: ["2082190357cfd3617ccfe04f340c6247"],
1017
+ });
1018
+ });
1019
+ return body.hashes;
1020
+ }
1021
+ );
1022
+
1023
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
1024
+ assertLater(() => {
1025
+ expect(init.headers).toMatchObject({
1026
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1027
+ });
1028
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
1029
+ expect(body).toMatchObject([
1030
+ {
1031
+ key: "2082190357cfd3617ccfe04f340c6247",
1032
+ value: Buffer.from("foobar").toString("base64"),
1033
+ metadata: {
1034
+ contentType: "image/png",
1035
+ },
1036
+ base64: true,
1037
+ },
1038
+ ]);
1039
+ });
1040
+ });
1041
+
1042
+ await runWrangler("pages project upload .");
1043
+
1044
+ expect(std.out).toMatchInlineSnapshot(`
1045
+ "✨ Success! Uploaded 1 files (TIMINGS)
1046
+
1047
+ ✨ Upload complete!"
1048
+ `);
1049
+ });
1050
+
1051
+ it("should retry uploads", async () => {
1052
+ writeFileSync("logo.txt", "foobar");
1053
+
1054
+ setMockResponse(
1055
+ "/pages/assets/check-missing",
1056
+ "POST",
1057
+ async (_, init) => {
1058
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
1059
+ assertLater(() => {
1060
+ expect(init.headers).toMatchObject({
1061
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1062
+ });
1063
+ expect(body).toMatchObject({
1064
+ hashes: ["1a98fb08af91aca4a7df1764a2c4ddb0"],
1065
+ });
1066
+ });
1067
+ return body.hashes;
1068
+ }
1069
+ );
1070
+
1071
+ // Accumulate multiple requests then assert afterwards
1072
+ const requests: RequestInit[] = [];
1073
+ setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
1074
+ requests.push(init);
1075
+
1076
+ if (requests.length < 2) {
1077
+ return createFetchResult(null, false, [
1078
+ {
1079
+ code: 800000,
1080
+ message: "Something exploded, please retry",
1081
+ },
1082
+ ]);
1083
+ } else {
1084
+ return createFetchResult(null, true);
1085
+ }
1086
+ });
1087
+
1088
+ await runWrangler("pages project upload .");
1089
+
1090
+ // Assert two identical requests
1091
+ expect(requests.length).toBe(2);
1092
+ for (const init of requests) {
1093
+ assertLater(() => {
1094
+ expect(init.headers).toMatchObject({
1095
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1096
+ });
1097
+
1098
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
1099
+ expect(body).toMatchObject([
1100
+ {
1101
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
1102
+ value: Buffer.from("foobar").toString("base64"),
1103
+ metadata: {
1104
+ contentType: "text/plain",
1105
+ },
1106
+ base64: true,
1107
+ },
1108
+ ]);
1109
+ });
1110
+ }
1111
+
1112
+ expect(std.out).toMatchInlineSnapshot(`
1113
+ "✨ Success! Uploaded 1 files (TIMINGS)
1114
+
1115
+ ✨ Upload complete!"
1116
+ `);
1117
+ });
1118
+
1119
+ it("should try to use multiple buckets (up to the max concurrency)", async () => {
1120
+ writeFileSync("logo.txt", "foobar");
1121
+ writeFileSync("logo.png", "foobar");
1122
+ writeFileSync("logo.html", "foobar");
1123
+ writeFileSync("logo.js", "foobar");
1124
+
1125
+ mockGetToken("<<funfetti-auth-jwt>>");
1126
+
1127
+ setMockResponse(
1128
+ "/pages/assets/check-missing",
1129
+ "POST",
1130
+ async (_, init) => {
1131
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
1132
+ assertLater(() => {
1133
+ expect(init.headers).toMatchObject({
1134
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1135
+ });
1136
+ expect(body).toMatchObject({
1137
+ hashes: expect.arrayContaining([
1138
+ "d96fef225537c9f5e44a3cb27fd0b492",
1139
+ "2082190357cfd3617ccfe04f340c6247",
1140
+ "6be321bef99e758250dac034474ddbb8",
1141
+ "1a98fb08af91aca4a7df1764a2c4ddb0",
1142
+ ]),
1143
+ });
1144
+ });
1145
+ return body.hashes;
1146
+ }
1147
+ );
1148
+
1149
+ // Accumulate multiple requests then assert afterwards
1150
+ const requests: RequestInit[] = [];
1151
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
1152
+ requests.push(init);
1153
+ });
1154
+
1155
+ await runWrangler("pages project upload .");
1156
+
1157
+ // We have 3 buckets, so expect 3 uploads
1158
+ expect(requests.length).toBe(3);
1159
+ const bodies: UploadPayloadFile[][] = [];
1160
+ for (const init of requests) {
1161
+ expect(init.headers).toMatchObject({
1162
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1163
+ });
1164
+ bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
1165
+ }
1166
+ // First bucket should end up with 2 files
1167
+ expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
1168
+ // But we don't know the order, so flatten and test without ordering
1169
+ expect(bodies.flatMap((b) => b)).toEqual(
1170
+ expect.arrayContaining([
1171
+ {
1172
+ base64: true,
1173
+ key: "d96fef225537c9f5e44a3cb27fd0b492",
1174
+ metadata: { contentType: "text/html" },
1175
+ value: "Zm9vYmFy",
1176
+ },
1177
+ {
1178
+ base64: true,
1179
+ key: "1a98fb08af91aca4a7df1764a2c4ddb0",
1180
+ metadata: { contentType: "text/plain" },
1181
+ value: "Zm9vYmFy",
1182
+ },
1183
+ {
1184
+ base64: true,
1185
+ key: "6be321bef99e758250dac034474ddbb8",
1186
+ metadata: { contentType: "application/javascript" },
1187
+ value: "Zm9vYmFy",
1188
+ },
1189
+ {
1190
+ base64: true,
1191
+ key: "2082190357cfd3617ccfe04f340c6247",
1192
+ metadata: { contentType: "image/png" },
1193
+ value: "Zm9vYmFy",
1194
+ },
1195
+ ])
1196
+ );
1197
+
1198
+ expect(std.out).toMatchInlineSnapshot(`
1199
+ "✨ Success! Uploaded 4 files (TIMINGS)
1200
+
1201
+ ✨ Upload complete!"
1202
+ `);
1203
+ });
1204
+
1205
+ it("should not error when directory names contain periods and houses a extensionless file", async () => {
1206
+ mkdirSync(".well-known");
1207
+ // Note: same content as previous test, but since it's a different extension,
1208
+ // it hashes to a different value
1209
+ writeFileSync(".well-known/foobar", "foobar");
1210
+
1211
+ mockGetToken("<<funfetti-auth-jwt>>");
1212
+
1213
+ setMockResponse(
1214
+ "/pages/assets/check-missing",
1215
+ "POST",
1216
+ async (_, init) => {
1217
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
1218
+ assertLater(() => {
1219
+ expect(init.headers).toMatchObject({
1220
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1221
+ });
1222
+ expect(body).toMatchObject({
1223
+ hashes: ["7b764dacfd211bebd8077828a7ddefd7"],
1224
+ });
1225
+ });
1226
+ return body.hashes;
1227
+ }
1228
+ );
1229
+
1230
+ setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
1231
+ assertLater(() => {
1232
+ expect(init.headers).toMatchObject({
1233
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1234
+ });
1235
+ const body = JSON.parse(init.body as string) as UploadPayloadFile[];
1236
+ expect(body).toMatchObject([
1237
+ {
1238
+ key: "7b764dacfd211bebd8077828a7ddefd7",
1239
+ value: Buffer.from("foobar").toString("base64"),
1240
+ metadata: {
1241
+ contentType: "application/octet-stream",
1242
+ },
1243
+ base64: true,
1244
+ },
1245
+ ]);
1246
+ });
1247
+ });
1248
+
1249
+ await runWrangler("pages project upload .");
1250
+
1251
+ expect(std.err).toMatchInlineSnapshot(`""`);
1252
+ });
1253
+ });
754
1254
  });