wrangler 0.0.0-e978986 → 0.0.0-e9e9872

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 (186) hide show
  1. package/README.md +7 -1
  2. package/bin/wrangler.js +114 -57
  3. package/kv-asset-handler.js +1 -0
  4. package/miniflare-dist/index.mjs +10 -2
  5. package/package.json +159 -152
  6. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  7. package/src/__tests__/config-cache.test.ts +30 -24
  8. package/src/__tests__/configuration.test.ts +3935 -3363
  9. package/src/__tests__/dev.test.tsx +1128 -949
  10. package/src/__tests__/guess-worker-format.test.ts +68 -68
  11. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  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 -28
  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 +142 -109
  29. package/src/__tests__/index.test.ts +239 -233
  30. package/src/__tests__/init.test.ts +1605 -1180
  31. package/src/__tests__/jest.setup.ts +76 -33
  32. package/src/__tests__/kv.test.ts +1160 -991
  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 +1160 -563
  36. package/src/__tests__/parse.test.ts +252 -252
  37. package/src/__tests__/publish.test.ts +6426 -5299
  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 +470 -459
  42. package/src/__tests__/tail.test.ts +538 -530
  43. package/src/__tests__/user.test.ts +124 -112
  44. package/src/__tests__/whoami.test.tsx +208 -83
  45. package/src/__tests__/worker-namespace.test.ts +327 -0
  46. package/src/abort.d.ts +1 -1
  47. package/src/api/dev.ts +52 -0
  48. package/src/api/index.ts +1 -0
  49. package/src/bundle-reporter.tsx +29 -0
  50. package/src/bundle.ts +158 -146
  51. package/src/cfetch/index.ts +80 -80
  52. package/src/cfetch/internal.ts +92 -88
  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 -357
  59. package/src/config/validation.ts +1573 -1367
  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 +334 -336
  65. package/src/dev/local.tsx +310 -274
  66. package/src/dev/remote.tsx +366 -211
  67. package/src/dev/use-esbuild.ts +126 -94
  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/faye-websocket.d.ts +6 -0
  75. package/src/generate.ts +33 -0
  76. package/src/git-client.ts +42 -0
  77. package/src/global-wrangler-config-path.ts +26 -0
  78. package/src/https-options.ts +84 -80
  79. package/src/index.tsx +1800 -2752
  80. package/src/init.ts +549 -0
  81. package/src/inspect.ts +593 -593
  82. package/src/intl-polyfill.d.ts +123 -123
  83. package/src/is-interactive.ts +12 -0
  84. package/src/kv.ts +299 -277
  85. package/src/logger.ts +46 -39
  86. package/src/miniflare-cli/enum-keys.ts +8 -8
  87. package/src/miniflare-cli/index.ts +43 -31
  88. package/src/miniflare-cli/request-context.ts +18 -18
  89. package/src/module-collection.ts +212 -212
  90. package/src/open-in-browser.ts +4 -6
  91. package/src/package-manager.ts +173 -128
  92. package/src/pages/build.tsx +202 -0
  93. package/src/pages/constants.ts +7 -0
  94. package/src/pages/deployments.tsx +101 -0
  95. package/src/pages/dev.tsx +964 -0
  96. package/src/pages/functions/buildPlugin.ts +105 -0
  97. package/src/pages/functions/buildWorker.ts +151 -0
  98. package/{pages → src/pages}/functions/filepath-routing.test.ts +113 -113
  99. package/src/pages/functions/filepath-routing.ts +189 -0
  100. package/src/pages/functions/identifiers.ts +78 -0
  101. package/src/pages/functions/routes.ts +151 -0
  102. package/src/pages/index.tsx +84 -0
  103. package/src/pages/projects.tsx +157 -0
  104. package/src/pages/publish.tsx +335 -0
  105. package/src/pages/types.ts +40 -0
  106. package/src/pages/upload.tsx +396 -0
  107. package/src/pages/utils.ts +12 -0
  108. package/src/parse.ts +209 -125
  109. package/src/paths.ts +6 -6
  110. package/src/preview.ts +31 -0
  111. package/src/proxy.ts +400 -389
  112. package/src/publish.ts +702 -505
  113. package/src/pubsub/index.ts +286 -0
  114. package/src/pubsub/pubsub-commands.tsx +577 -0
  115. package/src/r2.ts +19 -19
  116. package/src/selfsigned.d.ts +23 -23
  117. package/src/sites.tsx +278 -201
  118. package/src/tail/filters.ts +108 -108
  119. package/src/tail/index.ts +217 -217
  120. package/src/tail/printing.ts +45 -45
  121. package/src/update-check.ts +11 -11
  122. package/src/user/choose-account.tsx +60 -0
  123. package/src/user/env-vars.ts +46 -0
  124. package/src/user/generate-auth-url.ts +33 -0
  125. package/src/user/generate-random-state.ts +16 -0
  126. package/src/user/index.ts +3 -0
  127. package/src/user/user.tsx +1170 -0
  128. package/src/whoami.tsx +61 -41
  129. package/src/worker-namespace.ts +190 -0
  130. package/src/worker.ts +110 -99
  131. package/src/zones.ts +76 -0
  132. package/templates/checked-fetch.js +17 -0
  133. package/templates/new-worker-scheduled.js +4 -4
  134. package/templates/new-worker-scheduled.ts +16 -16
  135. package/templates/new-worker.js +3 -3
  136. package/templates/new-worker.ts +15 -15
  137. package/templates/no-op-worker.js +10 -0
  138. package/templates/pages-template-plugin.ts +155 -0
  139. package/templates/pages-template-worker.ts +161 -0
  140. package/templates/static-asset-facade.js +38 -36
  141. package/templates/tsconfig.json +95 -95
  142. package/wrangler-dist/cli.d.ts +298 -0
  143. package/wrangler-dist/cli.js +33496 -31370
  144. package/pages/functions/buildPlugin.ts +0 -105
  145. package/pages/functions/buildWorker.ts +0 -151
  146. package/pages/functions/filepath-routing.ts +0 -189
  147. package/pages/functions/identifiers.ts +0 -78
  148. package/pages/functions/routes.ts +0 -156
  149. package/pages/functions/template-plugin.ts +0 -147
  150. package/pages/functions/template-worker.ts +0 -143
  151. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  152. package/src/pages.tsx +0 -2027
  153. package/src/user.tsx +0 -1203
  154. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  155. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  156. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  157. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  158. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  159. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  160. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  161. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  162. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  163. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  164. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  165. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  166. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  167. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  168. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  169. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  170. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  171. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  172. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  173. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  174. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  175. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  176. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  177. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  178. package/vendor/wrangler-mime/LICENSE +0 -21
  179. package/vendor/wrangler-mime/Mime.js +0 -97
  180. package/vendor/wrangler-mime/README.md +0 -187
  181. package/vendor/wrangler-mime/cli.js +0 -46
  182. package/vendor/wrangler-mime/index.js +0 -4
  183. package/vendor/wrangler-mime/lite.js +0 -4
  184. package/vendor/wrangler-mime/package.json +0 -52
  185. package/vendor/wrangler-mime/types/other.js +0 -1
  186. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -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
- 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,228 +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 try to use multiple buckets (up to the max concurrency)", async () => {
492
- writeFileSync("logo.txt", "foobar");
493
- writeFileSync("logo.png", "foobar");
494
- writeFileSync("logo.html", "foobar");
495
- writeFileSync("logo.js", "foobar");
496
-
497
- mockGetToken("<<funfetti-auth-jwt>>");
498
-
499
- setMockResponse(
500
- "/pages/assets/check-missing",
501
- "POST",
502
- async (_, init) => {
503
- const body = JSON.parse(init.body as string) as { hashes: string[] };
504
- assertLater(() => {
505
- expect(init.headers).toMatchObject({
506
- Authorization: "Bearer <<funfetti-auth-jwt>>",
507
- });
508
- expect(body).toMatchObject({
509
- hashes: expect.arrayContaining([
510
- "d96fef225537c9f5e44a3cb27fd0b492",
511
- "2082190357cfd3617ccfe04f340c6247",
512
- "6be321bef99e758250dac034474ddbb8",
513
- "1a98fb08af91aca4a7df1764a2c4ddb0",
514
- ]),
515
- });
516
- });
517
- return body.hashes;
518
- }
519
- );
520
-
521
- // Accumulate multiple requests then assert afterwards
522
- const requests: RequestInit[] = [];
523
- setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
524
- requests.push(init);
525
- });
526
-
527
- setMockResponse(
528
- "/accounts/:accountId/pages/projects/foo/deployments",
529
- async ([_url, accountId], init) => {
530
- assertLater(() => {
531
- expect(accountId).toEqual("some-account-id");
532
- expect(init.method).toEqual("POST");
533
- const body = init.body as FormData;
534
- const manifest = JSON.parse(body.get("manifest") as string);
535
- 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(`
531
+ Object {
532
+ "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
533
+ }
534
+ `);
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(`
571
+ "✨ Success! Uploaded 1 files (TIMINGS)
572
+
573
+ ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
574
+ `);
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(`
536
622
  Object {
537
623
  "/logo.html": "d96fef225537c9f5e44a3cb27fd0b492",
538
624
  "/logo.js": "6be321bef99e758250dac034474ddbb8",
@@ -540,118 +626,629 @@ describe("pages", () => {
540
626
  "/logo.txt": "1a98fb08af91aca4a7df1764a2c4ddb0",
541
627
  }
542
628
  `);
543
- });
544
-
545
- return {
546
- url: "https://abcxyz.foo.pages.dev/",
547
- };
548
- }
549
- );
550
-
551
- await runWrangler("pages publish . --project-name=foo");
552
-
553
- // We have 3 buckets, so expect 3 uploads
554
- expect(requests.length).toBe(3);
555
- const bodies: UploadPayloadFile[][] = [];
556
- for (const init of requests) {
557
- expect(init.headers).toMatchObject({
558
- Authorization: "Bearer <<funfetti-auth-jwt>>",
559
- });
560
- bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
561
- }
562
- // First bucket should end up with 2 files
563
- expect(bodies.map((b) => b.length)).toEqual([2, 1, 1]);
564
- // But we don't know the order, so flatten and test without ordering
565
- expect(bodies.flatMap((b) => b)).toEqual(
566
- expect.arrayContaining([
567
- {
568
- base64: true,
569
- key: "d96fef225537c9f5e44a3cb27fd0b492",
570
- metadata: { contentType: "text/html" },
571
- value: "Zm9vYmFy",
572
- },
573
- {
574
- base64: true,
575
- key: "1a98fb08af91aca4a7df1764a2c4ddb0",
576
- metadata: { contentType: "text/plain" },
577
- value: "Zm9vYmFy",
578
- },
579
- {
580
- base64: true,
581
- key: "6be321bef99e758250dac034474ddbb8",
582
- metadata: { contentType: "application/javascript" },
583
- value: "Zm9vYmFy",
584
- },
585
- {
586
- base64: true,
587
- key: "2082190357cfd3617ccfe04f340c6247",
588
- metadata: { contentType: "image/png" },
589
- value: "Zm9vYmFy",
590
- },
591
- ])
592
- );
593
-
594
- 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(`
595
681
  "✨ Success! Uploaded 4 files (TIMINGS)
596
682
 
597
683
  ✨ Deployment complete! Take a peek over at https://abcxyz.foo.pages.dev/"
598
684
  `);
599
- });
600
-
601
- it("should not error when directory names contain periods and houses a extensionless file", async () => {
602
- mkdirSync(".well-known");
603
- // Note: same content as previous test, but since it's a different extension,
604
- // it hashes to a different value
605
- writeFileSync(".well-known/foobar", "foobar");
606
-
607
- mockGetToken("<<funfetti-auth-jwt>>");
608
-
609
- setMockResponse(
610
- "/pages/assets/check-missing",
611
- "POST",
612
- async (_, init) => {
613
- const body = JSON.parse(init.body as string) as { hashes: string[] };
614
- assertLater(() => {
615
- expect(init.headers).toMatchObject({
616
- Authorization: "Bearer <<funfetti-auth-jwt>>",
617
- });
618
- expect(body).toMatchObject({
619
- hashes: ["7b764dacfd211bebd8077828a7ddefd7"],
620
- });
621
- });
622
- return body.hashes;
623
- }
624
- );
625
-
626
- setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
627
- assertLater(() => {
628
- expect(init.headers).toMatchObject({
629
- Authorization: "Bearer <<funfetti-auth-jwt>>",
630
- });
631
- const body = JSON.parse(init.body as string) as UploadPayloadFile[];
632
- expect(body).toMatchObject([
633
- {
634
- key: "7b764dacfd211bebd8077828a7ddefd7",
635
- value: Buffer.from("foobar").toString("base64"),
636
- metadata: {
637
- contentType: "application/octet-stream",
638
- },
639
- base64: true,
640
- },
641
- ]);
642
- });
643
- });
644
-
645
- setMockResponse(
646
- "/accounts/:accountId/pages/projects/foo/deployments",
647
- async () => ({
648
- url: "https://abcxyz.foo.pages.dev/",
649
- })
650
- );
651
-
652
- await runWrangler("pages publish . --project-name=foo");
653
-
654
- expect(std.err).toMatchInlineSnapshot(`""`);
655
- });
656
- });
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
+ });
657
1254
  });