wrangler 2.7.1 → 2.8.0
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.
- package/package.json +1 -1
- package/src/__tests__/helpers/mock-dialogs.ts +2 -0
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
- package/src/__tests__/helpers/mock-known-routes.ts +7 -2
- package/src/__tests__/helpers/mock-kv.ts +29 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +90 -98
- package/src/__tests__/helpers/msw/handlers/deployments.ts +18 -0
- package/src/__tests__/helpers/msw/index.ts +30 -1
- package/src/__tests__/init.test.ts +3 -14
- package/src/__tests__/jest.setup.ts +0 -23
- package/src/__tests__/pages-deployment-tail.test.ts +72 -1
- package/src/__tests__/pages.test.ts +18 -16
- package/src/__tests__/publish.test.ts +744 -522
- package/src/__tests__/secret.test.ts +1 -9
- package/src/__tests__/tail.test.ts +66 -1
- package/src/__tests__/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/user.test.ts +5 -15
- package/src/cfetch/internal.ts +0 -3
- package/src/d1/backups.tsx +1 -5
- package/src/dev/remote.tsx +2 -0
- package/src/docs/index.ts +2 -1
- package/src/init.ts +1 -1
- package/src/pages/dev.ts +57 -62
- package/src/pages/functions/buildPlugin.ts +2 -20
- package/src/pages/functions/buildWorker.ts +136 -20
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
- package/src/pages/publish.tsx +36 -9
- package/src/publish/publish.ts +29 -4
- package/src/tail/createTail.ts +28 -1
- package/src/tail/printing.ts +15 -0
- package/templates/checked-fetch.js +1 -3
- package/wrangler-dist/cli.js +2841 -2737
- package/src/__tests__/helpers/mock-cfetch.ts +0 -278
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import prompts from "prompts";
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* The expected values for a confirmation request.
|
|
4
5
|
*/
|
|
@@ -30,6 +31,7 @@ export function mockConfirm(...expectations: ConfirmExpectation[]) {
|
|
|
30
31
|
if (expectation.options) {
|
|
31
32
|
expect(initial).toStrictEqual(expectation.options?.defaultValue);
|
|
32
33
|
}
|
|
34
|
+
|
|
33
35
|
return Promise.resolve({ value: expectation.result });
|
|
34
36
|
}
|
|
35
37
|
);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rest } from "msw";
|
|
2
|
+
import { msw, createFetchResult } from "./msw";
|
|
2
3
|
|
|
3
4
|
export function mockGetZoneFromHostRequest(host: string, zone?: string) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
msw.use(
|
|
6
|
+
rest.get("*/zones", (req, res, ctx) => {
|
|
7
|
+
expect(req.url.searchParams.get("name")).toEqual(host);
|
|
8
|
+
return res(ctx.json(createFetchResult(zone ? [{ id: zone }] : [])));
|
|
9
|
+
})
|
|
10
|
+
);
|
|
8
11
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rest } from "msw";
|
|
2
|
+
import { createFetchResult, msw } from "./msw";
|
|
2
3
|
|
|
3
4
|
export function mockCollectKnownRoutesRequest(
|
|
4
5
|
routes: { pattern: string; script: string }[]
|
|
5
6
|
) {
|
|
6
|
-
|
|
7
|
+
msw.use(
|
|
8
|
+
rest.get(`*/zones/:zoneId/workers/routes`, (_, res, ctx) =>
|
|
9
|
+
res.once(ctx.json(createFetchResult(routes)))
|
|
10
|
+
)
|
|
11
|
+
);
|
|
7
12
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rest } from "msw";
|
|
2
|
+
import { createFetchResult, msw } from "./msw";
|
|
2
3
|
import type { NamespaceKeyInfo } from "../../kv/helpers";
|
|
3
4
|
|
|
4
5
|
export function mockKeyListRequest(
|
|
@@ -9,25 +10,37 @@ export function mockKeyListRequest(
|
|
|
9
10
|
) {
|
|
10
11
|
const requests = { count: 0 };
|
|
11
12
|
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
|
|
13
|
+
msw.use(
|
|
14
|
+
rest.get(
|
|
15
|
+
"*/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
16
|
+
(req, res, ctx) => {
|
|
17
|
+
requests.count++;
|
|
18
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
19
|
+
expect(req.params.namespaceId).toEqual(expectedNamespaceId);
|
|
20
|
+
let response: undefined | NamespaceKeyInfo[];
|
|
21
|
+
if (expectedKeys.length <= keysPerRequest) {
|
|
22
|
+
response = expectedKeys;
|
|
23
|
+
}
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
15
|
-
"GET",
|
|
16
|
-
([_url, accountId, namespaceId], _init, query) => {
|
|
17
|
-
requests.count++;
|
|
18
|
-
expect(accountId).toEqual("some-account-id");
|
|
19
|
-
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
20
|
-
if (expectedKeys.length <= keysPerRequest) {
|
|
21
|
-
return createFetchResult(expectedKeys);
|
|
22
|
-
} else {
|
|
23
|
-
const start = parseInt(query.get("cursor") ?? "0") || 0;
|
|
25
|
+
const start = parseInt(req.url.searchParams.get("cursor") ?? "0") || 0;
|
|
24
26
|
const end = start + keysPerRequest;
|
|
25
27
|
const cursor = end < expectedKeys.length ? end : blankCursorValue;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
return res(
|
|
30
|
+
ctx.json(
|
|
31
|
+
createFetchResult(
|
|
32
|
+
response ? response : expectedKeys.slice(start, end),
|
|
33
|
+
true,
|
|
34
|
+
[],
|
|
35
|
+
[],
|
|
36
|
+
{
|
|
37
|
+
cursor,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
);
|
|
29
42
|
}
|
|
30
|
-
|
|
43
|
+
)
|
|
31
44
|
);
|
|
32
45
|
return requests;
|
|
33
46
|
}
|
|
@@ -1,30 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { rest } from "msw";
|
|
2
2
|
import { Request } from "undici";
|
|
3
3
|
import openInBrowser from "../../open-in-browser";
|
|
4
|
-
import { setMockResponse } from "./mock-cfetch";
|
|
5
4
|
import { mockHttpServer } from "./mock-http-server";
|
|
5
|
+
import { createFetchResult, msw } from "./msw";
|
|
6
6
|
|
|
7
7
|
export function mockGetMemberships(
|
|
8
8
|
accounts: { id: string; account: { id: string; name: string } }[]
|
|
9
9
|
) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
msw.use(
|
|
11
|
+
rest.get("*/memberships", (_, res, ctx) => {
|
|
12
|
+
return res.once(ctx.json(createFetchResult(accounts)));
|
|
13
|
+
})
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
export function mockGetMembershipsFail() {
|
|
17
|
+
msw.use(
|
|
18
|
+
rest.get("*/memberships", (_, res, ctx) => {
|
|
19
|
+
return res.once(ctx.json(createFetchResult([], false)));
|
|
20
|
+
})
|
|
21
|
+
);
|
|
13
22
|
}
|
|
14
|
-
|
|
15
|
-
// the response to send when wrangler wants an oauth grant
|
|
16
|
-
let oauthGrantResponse: GrantResponseOptions | "timeout" = {};
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Functions to help with mocking various parts of the OAuth Flow
|
|
20
26
|
*/
|
|
21
27
|
export const mockOAuthFlow = () => {
|
|
28
|
+
// the response to send when wrangler wants an oauth grant
|
|
29
|
+
let oauthGrantResponse: GrantResponseOptions | "timeout" = {};
|
|
22
30
|
const fetchHttp = mockHttpServer();
|
|
23
31
|
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
fetchMock.resetMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
32
|
/**
|
|
29
33
|
* Mock out the callback from a browser to our HttpServer.
|
|
30
34
|
*
|
|
@@ -61,7 +65,7 @@ export const mockOAuthFlow = () => {
|
|
|
61
65
|
});
|
|
62
66
|
};
|
|
63
67
|
|
|
64
|
-
//
|
|
68
|
+
// Handled in `mockOAuthServerCallback`
|
|
65
69
|
const mockGrantAuthorization = ({
|
|
66
70
|
respondWith,
|
|
67
71
|
}: {
|
|
@@ -82,25 +86,6 @@ export const mockOAuthFlow = () => {
|
|
|
82
86
|
}
|
|
83
87
|
};
|
|
84
88
|
|
|
85
|
-
const mockRevokeAuthorization = ({
|
|
86
|
-
domain = "dash.cloudflare.com",
|
|
87
|
-
}: { domain?: string } = {}) => {
|
|
88
|
-
const outcome = {
|
|
89
|
-
actual: new Request("https://example.org"),
|
|
90
|
-
expected: new Request(`https://${domain}/oauth2/revoke`, {
|
|
91
|
-
method: "POST",
|
|
92
|
-
}),
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
fetchMock.mockIf(outcome.expected.url, async (req) => {
|
|
96
|
-
// TODO: update Miniflare typings to match full undici Request
|
|
97
|
-
outcome.actual = req as unknown as Request;
|
|
98
|
-
return "";
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
return outcome;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
89
|
const mockGrantAccessToken = ({
|
|
105
90
|
respondWith,
|
|
106
91
|
domain = "dash.cloudflare.com",
|
|
@@ -109,94 +94,101 @@ export const mockOAuthFlow = () => {
|
|
|
109
94
|
domain?: string;
|
|
110
95
|
}) => {
|
|
111
96
|
const outcome = {
|
|
112
|
-
actual:
|
|
113
|
-
expected:
|
|
114
|
-
method: "POST",
|
|
115
|
-
}),
|
|
97
|
+
actual: "https://example.org",
|
|
98
|
+
expected: `https://${domain}/oauth2/token`,
|
|
116
99
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
100
|
+
msw.use(
|
|
101
|
+
rest.post(outcome.expected, async (req, res, ctx) => {
|
|
102
|
+
// TODO: update Miniflare typings to match full undici Request
|
|
103
|
+
outcome.actual = req.url.toString();
|
|
104
|
+
return res.once(ctx.json(makeTokenResponse(respondWith)));
|
|
105
|
+
})
|
|
106
|
+
);
|
|
123
107
|
|
|
124
108
|
return outcome;
|
|
125
109
|
};
|
|
126
110
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
domain = "dash.
|
|
111
|
+
function mockDomainUsesAccess({
|
|
112
|
+
usesAccess,
|
|
113
|
+
domain = "dash.cloudflare.com",
|
|
130
114
|
}: {
|
|
131
|
-
|
|
115
|
+
usesAccess: boolean;
|
|
132
116
|
domain?: string;
|
|
133
|
-
})
|
|
134
|
-
|
|
117
|
+
}) {
|
|
118
|
+
// If the domain relies upon Cloudflare Access, then a request to the domain
|
|
119
|
+
// will result in a redirect to the `cloudflareaccess.com` domain.
|
|
120
|
+
msw.use(
|
|
121
|
+
rest.get(`https://${domain}/`, (_, res, ctx) => {
|
|
122
|
+
let status = 200;
|
|
123
|
+
let headers: Record<string, string> = {
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
};
|
|
126
|
+
if (usesAccess) {
|
|
127
|
+
status = 302;
|
|
128
|
+
headers = { location: "cloudflareaccess.com" };
|
|
129
|
+
}
|
|
130
|
+
return res.once(ctx.status(status), ctx.set(headers));
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
mockHttpServer,
|
|
137
|
+
mockDomainUsesAccess,
|
|
138
|
+
mockGrantAccessToken,
|
|
139
|
+
mockOAuthServerCallback,
|
|
140
|
+
mockExchangeRefreshTokenForAccessToken,
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export function mockExchangeRefreshTokenForAccessToken({
|
|
145
|
+
respondWith,
|
|
146
|
+
domain = "dash.cloudflare.com",
|
|
147
|
+
}: {
|
|
148
|
+
respondWith: "refreshSuccess" | "refreshError" | "badResponse";
|
|
149
|
+
domain?: string;
|
|
150
|
+
}) {
|
|
151
|
+
msw.use(
|
|
152
|
+
rest.post(`https://${domain}/oauth2/token`, async (_, res, ctx) => {
|
|
135
153
|
switch (respondWith) {
|
|
136
154
|
case "refreshSuccess":
|
|
137
|
-
return
|
|
138
|
-
status
|
|
139
|
-
|
|
155
|
+
return res.once(
|
|
156
|
+
ctx.status(200),
|
|
157
|
+
ctx.json({
|
|
140
158
|
access_token: "access_token_success_mock",
|
|
141
159
|
expires_in: 1701,
|
|
142
160
|
refresh_token: "refresh_token_success_mock",
|
|
143
161
|
scope: "scope_success_mock",
|
|
144
162
|
token_type: "bearer",
|
|
145
|
-
})
|
|
146
|
-
|
|
163
|
+
})
|
|
164
|
+
);
|
|
147
165
|
case "refreshError":
|
|
148
|
-
return
|
|
149
|
-
status
|
|
150
|
-
|
|
166
|
+
return res.once(
|
|
167
|
+
ctx.status(400),
|
|
168
|
+
ctx.json({
|
|
151
169
|
error: "invalid_request",
|
|
152
170
|
error_description: "error_description_mock",
|
|
153
171
|
error_hint: "error_hint_mock",
|
|
154
172
|
error_verbose: "error_verbose_mock",
|
|
155
173
|
status_code: 400,
|
|
156
|
-
})
|
|
157
|
-
|
|
174
|
+
})
|
|
175
|
+
);
|
|
158
176
|
case "badResponse":
|
|
159
|
-
return
|
|
160
|
-
status
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
return res.once(
|
|
178
|
+
ctx.status(400),
|
|
179
|
+
ctx.text(
|
|
180
|
+
`<html> <body> This shouldn't be sent, but should be handled </body> </html>`
|
|
181
|
+
)
|
|
182
|
+
);
|
|
163
183
|
|
|
164
184
|
default:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const mockDomainUsesAccess = ({
|
|
171
|
-
usesAccess,
|
|
172
|
-
domain = "dash.cloudflare.com",
|
|
173
|
-
}: {
|
|
174
|
-
usesAccess: boolean;
|
|
175
|
-
domain?: string;
|
|
176
|
-
}) => {
|
|
177
|
-
// If the domain relies upon Cloudflare Access, then a request to the domain
|
|
178
|
-
// will result in a redirect to the `cloudflareaccess.com` domain.
|
|
179
|
-
fetchMock.mockOnceIf(`https://${domain}/`, async () => {
|
|
180
|
-
if (usesAccess) {
|
|
181
|
-
return {
|
|
182
|
-
status: 302,
|
|
183
|
-
headers: { location: "cloudflareaccess.com" },
|
|
184
|
-
};
|
|
185
|
-
} else {
|
|
186
|
-
return { status: 200 };
|
|
185
|
+
throw new Error(
|
|
186
|
+
"Not a respondWith option for `mockExchangeRefreshTokenForAccessToken`"
|
|
187
|
+
);
|
|
187
188
|
}
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
mockDomainUsesAccess,
|
|
193
|
-
mockGrantAccessToken,
|
|
194
|
-
mockGrantAuthorization,
|
|
195
|
-
mockOAuthServerCallback,
|
|
196
|
-
mockRevokeAuthorization,
|
|
197
|
-
mockExchangeRefreshTokenForAccessToken,
|
|
198
|
-
};
|
|
199
|
-
};
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
}
|
|
200
192
|
|
|
201
193
|
type GrantResponseOptions = {
|
|
202
194
|
code?: string;
|
|
@@ -249,7 +241,7 @@ type MockTokenResponse =
|
|
|
249
241
|
error: ErrorType;
|
|
250
242
|
};
|
|
251
243
|
|
|
252
|
-
const makeTokenResponse = (partialResponse: MockTokenResponse)
|
|
244
|
+
const makeTokenResponse = (partialResponse: MockTokenResponse) => {
|
|
253
245
|
let fullResponse: MockTokenResponse;
|
|
254
246
|
|
|
255
247
|
if (partialResponse === "ok") {
|
|
@@ -267,5 +259,5 @@ const makeTokenResponse = (partialResponse: MockTokenResponse): string => {
|
|
|
267
259
|
fullResponse = partialResponse;
|
|
268
260
|
}
|
|
269
261
|
|
|
270
|
-
return
|
|
262
|
+
return fullResponse;
|
|
271
263
|
};
|
|
@@ -57,3 +57,21 @@ export const mswSuccessDeployments = [
|
|
|
57
57
|
)
|
|
58
58
|
),
|
|
59
59
|
];
|
|
60
|
+
|
|
61
|
+
export const mswSuccessLastDeployment = [
|
|
62
|
+
rest.get(
|
|
63
|
+
"*/accounts/:accountId/workers/services/:scriptName",
|
|
64
|
+
(req, res, ctx) => {
|
|
65
|
+
return res.once(
|
|
66
|
+
ctx.json({
|
|
67
|
+
success: true,
|
|
68
|
+
messages: [],
|
|
69
|
+
errors: [],
|
|
70
|
+
result: {
|
|
71
|
+
default_environment: { script: { last_deployed_from: "wrangler" } },
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
];
|
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import { setupServer } from "msw/node";
|
|
2
2
|
import { default as mswAccessHandlers } from "./handlers/access";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
mswSuccessDeployments,
|
|
5
|
+
mswSuccessLastDeployment,
|
|
6
|
+
} from "./handlers/deployments";
|
|
4
7
|
import { mswSuccessNamespacesHandlers } from "./handlers/namespaces";
|
|
5
8
|
import { mswSuccessOauthHandlers } from "./handlers/oauth";
|
|
6
9
|
import { mswSuccessR2handlers } from "./handlers/r2";
|
|
7
10
|
import { default as mswSucessScriptHandlers } from "./handlers/script";
|
|
8
11
|
import { mswSuccessUserHandlers } from "./handlers/user";
|
|
9
12
|
import { default as mswZoneHandlers } from "./handlers/zones";
|
|
13
|
+
|
|
10
14
|
export const msw = setupServer();
|
|
11
15
|
|
|
16
|
+
function createFetchResult(
|
|
17
|
+
result: unknown,
|
|
18
|
+
success = true,
|
|
19
|
+
errors: unknown[] = [],
|
|
20
|
+
messages: unknown[] = [],
|
|
21
|
+
result_info?: Record<string, unknown>
|
|
22
|
+
) {
|
|
23
|
+
return result_info
|
|
24
|
+
? {
|
|
25
|
+
result,
|
|
26
|
+
success,
|
|
27
|
+
errors,
|
|
28
|
+
messages,
|
|
29
|
+
result_info,
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
result,
|
|
33
|
+
success,
|
|
34
|
+
errors,
|
|
35
|
+
messages,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
12
39
|
export {
|
|
40
|
+
createFetchResult,
|
|
13
41
|
mswSuccessUserHandlers,
|
|
14
42
|
mswSuccessR2handlers,
|
|
15
43
|
mswSuccessOauthHandlers,
|
|
@@ -18,4 +46,5 @@ export {
|
|
|
18
46
|
mswZoneHandlers,
|
|
19
47
|
mswSuccessDeployments,
|
|
20
48
|
mswAccessHandlers,
|
|
49
|
+
mswSuccessLastDeployment,
|
|
21
50
|
};
|
|
@@ -4,9 +4,7 @@ import * as TOML from "@iarna/toml";
|
|
|
4
4
|
import { execa, execaSync } from "execa";
|
|
5
5
|
import { rest } from "msw";
|
|
6
6
|
import { parseConfigFileTextToJson } from "typescript";
|
|
7
|
-
import { FormData } from "undici";
|
|
8
7
|
import { version as wranglerVersion } from "../../package.json";
|
|
9
|
-
import { fetchDashboardScript } from "../cfetch/internal";
|
|
10
8
|
import { getPackageManager } from "../package-manager";
|
|
11
9
|
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
|
|
12
10
|
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
@@ -46,8 +44,6 @@ describe("init", () => {
|
|
|
46
44
|
|
|
47
45
|
afterEach(() => {
|
|
48
46
|
clearDialogs();
|
|
49
|
-
msw.resetHandlers();
|
|
50
|
-
msw.restoreHandlers();
|
|
51
47
|
});
|
|
52
48
|
|
|
53
49
|
const std = mockConsoleMethods();
|
|
@@ -2798,6 +2794,7 @@ describe("init", () => {
|
|
|
2798
2794
|
"isolinear-optical-chip/wrangler.toml": wranglerToml({
|
|
2799
2795
|
...mockConfigExpected,
|
|
2800
2796
|
name: "isolinear-optical-chip",
|
|
2797
|
+
main: "src/index.js",
|
|
2801
2798
|
}),
|
|
2802
2799
|
},
|
|
2803
2800
|
});
|
|
@@ -3070,19 +3067,11 @@ export function setMockFetchDashScript(mockResponse: string) {
|
|
|
3070
3067
|
msw.use(
|
|
3071
3068
|
rest.get(
|
|
3072
3069
|
`*/accounts/:accountId/workers/services/:fromDashScriptName/environments/:environment/content`,
|
|
3073
|
-
(
|
|
3074
|
-
|
|
3075
|
-
const mockForm = new FormData();
|
|
3076
|
-
mockForm.append("name", mockResponse);
|
|
3077
|
-
// Sending it as JSON will result in empty object, but cause the fetchDashboardScript to execute its code
|
|
3078
|
-
return res(ctx.status(200), ctx.json(mockForm));
|
|
3070
|
+
(_, res, ctx) => {
|
|
3071
|
+
return res(ctx.text(mockResponse));
|
|
3079
3072
|
}
|
|
3080
3073
|
)
|
|
3081
3074
|
);
|
|
3082
|
-
// Mock the actual return value that FormData would return
|
|
3083
|
-
(fetchDashboardScript as jest.Mock).mockImplementationOnce(() => {
|
|
3084
|
-
return mockResponse;
|
|
3085
|
-
});
|
|
3086
3075
|
}
|
|
3087
3076
|
|
|
3088
3077
|
/**
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import fetchMock from "jest-fetch-mock";
|
|
2
|
-
import {
|
|
3
|
-
fetchDashboardScript,
|
|
4
|
-
fetchInternal,
|
|
5
|
-
fetchKVGetValue,
|
|
6
|
-
fetchR2Objects,
|
|
7
|
-
performApiFetch,
|
|
8
|
-
} from "../cfetch/internal";
|
|
9
|
-
import {
|
|
10
|
-
mockFetchInternal,
|
|
11
|
-
mockFetchR2Objects,
|
|
12
|
-
mockPerformApiFetch,
|
|
13
|
-
} from "./helpers/mock-cfetch";
|
|
14
2
|
import { MockWebSocket } from "./helpers/mock-web-socket";
|
|
15
3
|
import { msw } from "./helpers/msw";
|
|
16
4
|
|
|
@@ -89,17 +77,6 @@ afterEach(() => {
|
|
|
89
77
|
});
|
|
90
78
|
afterAll(() => msw.close());
|
|
91
79
|
|
|
92
|
-
jest.mock("../cfetch/internal");
|
|
93
|
-
(fetchDashboardScript as jest.Mock).mockImplementation(
|
|
94
|
-
jest.requireActual("../cfetch/internal")["fetchDashboardScript"]
|
|
95
|
-
);
|
|
96
|
-
(fetchInternal as jest.Mock).mockImplementation(mockFetchInternal);
|
|
97
|
-
(fetchKVGetValue as jest.Mock).mockImplementation(
|
|
98
|
-
jest.requireActual("../cfetch/internal").fetchKVGetValue
|
|
99
|
-
);
|
|
100
|
-
(fetchR2Objects as jest.Mock).mockImplementation(mockFetchR2Objects);
|
|
101
|
-
(performApiFetch as jest.Mock).mockImplementation(mockPerformApiFetch);
|
|
102
|
-
|
|
103
80
|
jest.mock("../dev/dev", () => {
|
|
104
81
|
const { useApp } = jest.requireActual("ink");
|
|
105
82
|
const { useEffect } = jest.requireActual("react");
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
RequestEvent,
|
|
13
13
|
ScheduledEvent,
|
|
14
14
|
AlarmEvent,
|
|
15
|
+
EmailEvent,
|
|
15
16
|
} from "../tail/createTail";
|
|
16
17
|
import type { RequestInit } from "undici";
|
|
17
18
|
import type WebSocket from "ws";
|
|
@@ -355,6 +356,20 @@ describe("pages deployment tail", () => {
|
|
|
355
356
|
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
356
357
|
});
|
|
357
358
|
|
|
359
|
+
it("logs email messages in json format", async () => {
|
|
360
|
+
const api = mockTailAPIs();
|
|
361
|
+
await runWrangler(
|
|
362
|
+
"pages deployment tail mock-deployment-id --project-name mock-project --format json"
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const event = generateMockEmailEvent();
|
|
366
|
+
const message = generateMockEventMessage({ event });
|
|
367
|
+
const serializedMessage = serialize(message);
|
|
368
|
+
|
|
369
|
+
api.ws.send(serializedMessage);
|
|
370
|
+
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
371
|
+
});
|
|
372
|
+
|
|
358
373
|
it("logs request messages in pretty format", async () => {
|
|
359
374
|
const api = mockTailAPIs();
|
|
360
375
|
await runWrangler(
|
|
@@ -436,6 +451,33 @@ describe("pages deployment tail", () => {
|
|
|
436
451
|
`);
|
|
437
452
|
});
|
|
438
453
|
|
|
454
|
+
it("logs email messages in pretty format", async () => {
|
|
455
|
+
const api = mockTailAPIs();
|
|
456
|
+
await runWrangler(
|
|
457
|
+
"pages deployment tail mock-deployment-id --project-name mock-project --format pretty"
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
const event = generateMockEmailEvent();
|
|
461
|
+
const message = generateMockEventMessage({ event });
|
|
462
|
+
const serializedMessage = serialize(message);
|
|
463
|
+
|
|
464
|
+
api.ws.send(serializedMessage);
|
|
465
|
+
expect(
|
|
466
|
+
std.out
|
|
467
|
+
.replace(
|
|
468
|
+
new Date(mockEventTimestamp).toLocaleString(),
|
|
469
|
+
"[mock event timestamp]"
|
|
470
|
+
)
|
|
471
|
+
.replace(
|
|
472
|
+
mockTailExpiration.toLocaleString(),
|
|
473
|
+
"[mock expiration date]"
|
|
474
|
+
)
|
|
475
|
+
).toMatchInlineSnapshot(`
|
|
476
|
+
"Connected to deployment mock-deployment-id, waiting for logs...
|
|
477
|
+
Email from:${mockEmailEventFrom} to:${mockEmailEventTo} size:${mockEmailEventSize} @ [mock event timestamp] - Ok"
|
|
478
|
+
`);
|
|
479
|
+
});
|
|
480
|
+
|
|
439
481
|
it("should not crash when the tail message has a void event", async () => {
|
|
440
482
|
const api = mockTailAPIs();
|
|
441
483
|
await runWrangler(
|
|
@@ -608,7 +650,13 @@ function serialize(message: TailEventMessage): WebSocket.RawData {
|
|
|
608
650
|
* @returns true if `event` is a RequestEvent
|
|
609
651
|
*/
|
|
610
652
|
function isRequest(
|
|
611
|
-
event:
|
|
653
|
+
event:
|
|
654
|
+
| ScheduledEvent
|
|
655
|
+
| RequestEvent
|
|
656
|
+
| AlarmEvent
|
|
657
|
+
| EmailEvent
|
|
658
|
+
| undefined
|
|
659
|
+
| null
|
|
612
660
|
): event is RequestEvent {
|
|
613
661
|
return Boolean(event && "request" in event);
|
|
614
662
|
}
|
|
@@ -742,6 +790,21 @@ const mockEventTimestamp = 1645454470467;
|
|
|
742
790
|
*/
|
|
743
791
|
const mockEventScheduledTime = new Date(mockEventTimestamp).toISOString();
|
|
744
792
|
|
|
793
|
+
/**
|
|
794
|
+
* Default value for email event from
|
|
795
|
+
*/
|
|
796
|
+
const mockEmailEventFrom = "from@example.com";
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Default value for email event to
|
|
800
|
+
*/
|
|
801
|
+
const mockEmailEventTo = "to@example.com";
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Default value for email event mail size
|
|
805
|
+
*/
|
|
806
|
+
const mockEmailEventSize = 45416;
|
|
807
|
+
|
|
745
808
|
/**
|
|
746
809
|
* Mock out the API hit during Tail deletion
|
|
747
810
|
*
|
|
@@ -882,3 +945,11 @@ function generateMockAlarmEvent(opts?: Partial<AlarmEvent>): AlarmEvent {
|
|
|
882
945
|
scheduledTime: opts?.scheduledTime || mockEventScheduledTime,
|
|
883
946
|
};
|
|
884
947
|
}
|
|
948
|
+
|
|
949
|
+
function generateMockEmailEvent(opts?: Partial<EmailEvent>): EmailEvent {
|
|
950
|
+
return {
|
|
951
|
+
mailFrom: opts?.mailFrom || mockEmailEventFrom,
|
|
952
|
+
rcptTo: opts?.rcptTo || mockEmailEventTo,
|
|
953
|
+
rawSize: opts?.rawSize || mockEmailEventSize,
|
|
954
|
+
};
|
|
955
|
+
}
|