wrangler 0.0.0-e3cab74 → 0.0.0-e3e3243
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/README.md +1 -1
- package/bin/wrangler.js +10 -1
- package/miniflare-dist/index.mjs +79 -0
- package/miniflare-dist/index.mjs.map +7 -0
- package/package.json +10 -7
- package/pages/functions/filepath-routing.test.ts +125 -25
- package/pages/functions/filepath-routing.ts +6 -3
- package/src/__tests__/configuration.test.ts +959 -324
- package/src/__tests__/dev.test.tsx +512 -84
- package/src/__tests__/guess-worker-format.test.ts +19 -17
- package/src/__tests__/helpers/mock-account-id.ts +22 -4
- package/src/__tests__/helpers/mock-cfetch.ts +3 -3
- package/src/__tests__/helpers/mock-console.ts +17 -9
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/run-in-tmp.ts +3 -0
- package/src/__tests__/https-options.test.ts +127 -0
- package/src/__tests__/index.test.ts +128 -11
- package/src/__tests__/jest.setup.ts +16 -4
- package/src/__tests__/kv.test.ts +364 -125
- package/src/__tests__/logout.test.ts +15 -13
- package/src/__tests__/pages.test.ts +82 -0
- package/src/__tests__/publish.test.ts +1838 -735
- package/src/__tests__/r2.test.ts +4 -4
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +167 -68
- package/src/__tests__/tail.test.ts +2 -4
- package/src/__tests__/whoami.test.tsx +21 -7
- package/src/bundle.ts +27 -5
- package/src/cfetch/internal.ts +11 -5
- package/src/config/README.md +6 -6
- package/src/config/config.ts +54 -101
- package/src/config/environment.ts +67 -1
- package/src/config/index.ts +13 -3
- package/src/config/validation-helpers.ts +207 -23
- package/src/config/validation.ts +344 -129
- package/src/{api/preview.ts → create-worker-preview.ts} +39 -20
- package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
- package/src/dev/dev.tsx +392 -0
- package/src/dev/local.tsx +232 -0
- package/src/dev/remote.tsx +203 -0
- package/src/dev/use-esbuild.ts +104 -0
- package/src/entry.ts +192 -9
- package/src/https-options.ts +122 -0
- package/src/index.tsx +577 -515
- package/src/inspect.ts +9 -1
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv.tsx → kv.ts} +97 -53
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +37 -0
- package/src/module-collection.ts +90 -2
- package/src/open-in-browser.ts +14 -2
- package/src/pages.tsx +26 -10
- package/src/proxy.ts +104 -40
- package/src/publish.ts +53 -71
- package/src/reporting.ts +25 -9
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +68 -29
- package/src/user.tsx +163 -85
- package/src/{api/worker.ts → worker.ts} +1 -24
- package/wrangler-dist/cli.js +5933 -3842
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/dev.tsx +0 -961
- package/src/guess-worker-format.ts +0 -68
|
@@ -1,14 +1,25 @@
|
|
|
1
|
+
import { reinitialiseAuthTokens } from "../../user";
|
|
2
|
+
|
|
1
3
|
const ORIGINAL_CLOUDFLARE_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN;
|
|
2
4
|
const ORIGINAL_CLOUDFLARE_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Mock the API token so that we don't need to read it from user configuration files.
|
|
8
|
+
*
|
|
9
|
+
* Note that you can remove any API token from the environment by setting the value to `null`.
|
|
10
|
+
* This is useful if a higher `describe()` block has already called `mockApiToken()`.
|
|
6
11
|
*/
|
|
7
12
|
export function mockApiToken({
|
|
8
13
|
apiToken = "some-api-token",
|
|
9
|
-
}: { apiToken?: string } = {}) {
|
|
14
|
+
}: { apiToken?: string | null } = {}) {
|
|
10
15
|
beforeEach(() => {
|
|
11
|
-
|
|
16
|
+
if (apiToken === null) {
|
|
17
|
+
delete process.env.CLOUDFLARE_API_TOKEN;
|
|
18
|
+
} else {
|
|
19
|
+
process.env.CLOUDFLARE_API_TOKEN = apiToken;
|
|
20
|
+
}
|
|
21
|
+
// Now we have updated the environment, we must reinitialize the user auth state.
|
|
22
|
+
reinitialiseAuthTokens();
|
|
12
23
|
});
|
|
13
24
|
afterEach(() => {
|
|
14
25
|
process.env.CLOUDFLARE_API_TOKEN = ORIGINAL_CLOUDFLARE_API_TOKEN;
|
|
@@ -17,12 +28,19 @@ export function mockApiToken({
|
|
|
17
28
|
|
|
18
29
|
/**
|
|
19
30
|
* Mock the current account ID so that we don't need to read it from configuration files.
|
|
31
|
+
*
|
|
32
|
+
* Note that you can remove any account ID from the environment by setting the value to `null`.
|
|
33
|
+
* This is useful if a higher `describe()` block has already called `mockAccountId()`.
|
|
20
34
|
*/
|
|
21
35
|
export function mockAccountId({
|
|
22
36
|
accountId = "some-account-id",
|
|
23
|
-
}: { accountId?: string } = {}) {
|
|
37
|
+
}: { accountId?: string | null } = {}) {
|
|
24
38
|
beforeEach(() => {
|
|
25
|
-
|
|
39
|
+
if (accountId === null) {
|
|
40
|
+
delete process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
41
|
+
} else {
|
|
42
|
+
process.env.CLOUDFLARE_ACCOUNT_ID = accountId;
|
|
43
|
+
}
|
|
26
44
|
});
|
|
27
45
|
afterEach(() => {
|
|
28
46
|
process.env.CLOUDFLARE_ACCOUNT_ID = ORIGINAL_CLOUDFLARE_ACCOUNT_ID;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { URL, URLSearchParams } from "node:url";
|
|
2
2
|
import { pathToRegexp } from "path-to-regexp";
|
|
3
3
|
import { getCloudflareApiBaseUrl } from "../../cfetch";
|
|
4
|
-
import type { FetchResult } from "../../cfetch";
|
|
4
|
+
import type { FetchResult, FetchError } from "../../cfetch";
|
|
5
5
|
import type { RequestInit } from "undici";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -143,8 +143,8 @@ export function setMockResponse<ResponseType>(
|
|
|
143
143
|
export async function createFetchResult<ResponseType>(
|
|
144
144
|
result: ResponseType | Promise<ResponseType>,
|
|
145
145
|
success = true,
|
|
146
|
-
errors = [],
|
|
147
|
-
messages = [],
|
|
146
|
+
errors: FetchError[] = [],
|
|
147
|
+
messages: string[] = [],
|
|
148
148
|
result_info?: unknown
|
|
149
149
|
): Promise<FetchResult<ResponseType>> {
|
|
150
150
|
return result_info
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as util from "node:util";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* We use this module to mock console methods, and optionally
|
|
3
5
|
* assert on the values they're called with in our tests.
|
|
@@ -9,22 +11,28 @@ let logSpy: jest.SpyInstance,
|
|
|
9
11
|
|
|
10
12
|
const std = {
|
|
11
13
|
get out() {
|
|
12
|
-
return
|
|
13
|
-
normalizeSlashes(stripTimings(logSpy.mock.calls.flat(2).join("\n")))
|
|
14
|
-
);
|
|
14
|
+
return normalizeOutput(logSpy);
|
|
15
15
|
},
|
|
16
16
|
get err() {
|
|
17
|
-
return
|
|
18
|
-
normalizeSlashes(stripTimings(errorSpy.mock.calls.flat(2).join("\n")))
|
|
19
|
-
);
|
|
17
|
+
return normalizeOutput(errorSpy);
|
|
20
18
|
},
|
|
21
19
|
get warn() {
|
|
22
|
-
return
|
|
23
|
-
normalizeSlashes(stripTimings(warnSpy.mock.calls.flat(2).join("\n")))
|
|
24
|
-
);
|
|
20
|
+
return normalizeOutput(warnSpy);
|
|
25
21
|
},
|
|
26
22
|
};
|
|
27
23
|
|
|
24
|
+
function normalizeOutput(spy: jest.SpyInstance): string {
|
|
25
|
+
return stripTrailingWhitespace(
|
|
26
|
+
normalizeSlashes(stripTimings(captureCalls(spy)))
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function captureCalls(spy: jest.SpyInstance): string {
|
|
31
|
+
return spy.mock.calls
|
|
32
|
+
.map((args: unknown[]) => util.format("%s", ...args))
|
|
33
|
+
.join("\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
export function mockConsoleMethods() {
|
|
29
37
|
beforeEach(() => {
|
|
30
38
|
logSpy = jest.spyOn(console, "log").mockImplementation();
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { createFetchResult, setMockRawResponse } from "./mock-cfetch";
|
|
2
|
+
import type { NamespaceKeyInfo } from "../../kv";
|
|
2
3
|
|
|
3
4
|
export function mockKeyListRequest(
|
|
4
5
|
expectedNamespaceId: string,
|
|
5
|
-
expectedKeys:
|
|
6
|
+
expectedKeys: NamespaceKeyInfo[],
|
|
6
7
|
keysPerRequest = 1000,
|
|
7
8
|
blankCursorValue: "" | undefined | null = undefined
|
|
8
9
|
) {
|
|
9
10
|
const requests = { count: 0 };
|
|
10
11
|
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
|
|
11
|
-
|
|
12
|
-
name,
|
|
13
|
-
expiration: 123456789,
|
|
14
|
-
metadata: {},
|
|
15
|
-
}));
|
|
12
|
+
|
|
16
13
|
setMockRawResponse(
|
|
17
14
|
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
18
15
|
"GET",
|
|
@@ -20,19 +17,15 @@ export function mockKeyListRequest(
|
|
|
20
17
|
requests.count++;
|
|
21
18
|
expect(accountId).toEqual("some-account-id");
|
|
22
19
|
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
23
|
-
if (
|
|
24
|
-
return createFetchResult(
|
|
20
|
+
if (expectedKeys.length <= keysPerRequest) {
|
|
21
|
+
return createFetchResult(expectedKeys);
|
|
25
22
|
} else {
|
|
26
23
|
const start = parseInt(query.get("cursor") ?? "0") || 0;
|
|
27
24
|
const end = start + keysPerRequest;
|
|
28
|
-
const cursor = end <
|
|
29
|
-
return createFetchResult(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[],
|
|
33
|
-
[],
|
|
34
|
-
{ cursor }
|
|
35
|
-
);
|
|
25
|
+
const cursor = end < expectedKeys.length ? end : blankCursorValue;
|
|
26
|
+
return createFetchResult(expectedKeys.slice(start, end), true, [], [], {
|
|
27
|
+
cursor,
|
|
28
|
+
});
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
31
|
);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const ORIGINAL_STDIN = process.stdin;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock process.stdin so that we can pipe in text for non-interactive mode tests.
|
|
5
|
+
*/
|
|
6
|
+
export function useMockStdin({ isTTY }: { isTTY: boolean }) {
|
|
7
|
+
const mockStdin = new MockStdIn(isTTY);
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockStdin.reset();
|
|
11
|
+
Object.defineProperty(process, "stdin", {
|
|
12
|
+
value: mockStdin,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: false,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
Object.defineProperty(process, "stdin", {
|
|
20
|
+
value: ORIGINAL_STDIN,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: false,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return mockStdin;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const failCallback: (value: unknown) => void = () => {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"TEST FAILURE: stdin callback called before being initialized."
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A mock version of `process.std`, which can be used to simulate piping data
|
|
37
|
+
* into the wrangler process in non-interactive mode.
|
|
38
|
+
*/
|
|
39
|
+
class MockStdIn {
|
|
40
|
+
private endCallback = failCallback;
|
|
41
|
+
private errorCallback = failCallback;
|
|
42
|
+
private chunks: string[] = [];
|
|
43
|
+
private error: Error | undefined;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set this to true if you want the stdin stream to error.
|
|
47
|
+
*/
|
|
48
|
+
throwError(error: Error) {
|
|
49
|
+
this.error = error;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Call this to clean out the chunks that are queued for sending.
|
|
54
|
+
*/
|
|
55
|
+
reset() {
|
|
56
|
+
this.chunks.length = 0;
|
|
57
|
+
this.error = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Queue up some chunks to be sent.
|
|
62
|
+
*/
|
|
63
|
+
send(...chunks: string[]) {
|
|
64
|
+
this.chunks.push(...chunks);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
/**
|
|
69
|
+
* Used by wrangler to check whether stdin is interactive.
|
|
70
|
+
*/
|
|
71
|
+
readonly isTTY: boolean
|
|
72
|
+
) {}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Used by wrangler to add event listeners.
|
|
76
|
+
*/
|
|
77
|
+
on(eventName: string, callback: () => void) {
|
|
78
|
+
switch (eventName) {
|
|
79
|
+
case "readable":
|
|
80
|
+
setImmediate(callback);
|
|
81
|
+
break;
|
|
82
|
+
case "end":
|
|
83
|
+
this.endCallback = callback;
|
|
84
|
+
break;
|
|
85
|
+
case "error":
|
|
86
|
+
this.errorCallback = callback;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Used by wrangler to get the next chunk of data in the stream.
|
|
93
|
+
*/
|
|
94
|
+
read() {
|
|
95
|
+
if (this.error) {
|
|
96
|
+
setImmediate(() => this.errorCallback(this.error));
|
|
97
|
+
}
|
|
98
|
+
if (this.chunks.length === 0) {
|
|
99
|
+
setImmediate(this.endCallback);
|
|
100
|
+
}
|
|
101
|
+
return this.chunks.shift() ?? null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { reinitialiseAuthTokens } from "../../user";
|
|
4
5
|
|
|
5
6
|
const originalCwd = process.cwd();
|
|
6
7
|
|
|
@@ -19,6 +20,8 @@ export function runInTempDir({ homedir }: { homedir?: string } = {}) {
|
|
|
19
20
|
// This is because the module gets transpiled so that the "method" `homedir()` gets converted to a
|
|
20
21
|
// getter that is not configurable (and so cannot be spied upon).
|
|
21
22
|
jest.spyOn(os, "homedir").mockReturnValue(homedir);
|
|
23
|
+
// Now that we have changed the home directory location, we must reinitialize the user auth state
|
|
24
|
+
reinitialiseAuthTokens();
|
|
22
25
|
}
|
|
23
26
|
});
|
|
24
27
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { getHttpsOptions } from "../https-options";
|
|
5
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
6
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
7
|
+
|
|
8
|
+
describe("getHttpsOptions()", () => {
|
|
9
|
+
runInTempDir({ homedir: "./home" });
|
|
10
|
+
const std = mockConsoleMethods();
|
|
11
|
+
|
|
12
|
+
it("should use cached values if they have not expired", async () => {
|
|
13
|
+
fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
|
|
14
|
+
recursive: true,
|
|
15
|
+
});
|
|
16
|
+
fs.writeFileSync(
|
|
17
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
18
|
+
"PRIVATE KEY"
|
|
19
|
+
);
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
22
|
+
"PUBLIC KEY"
|
|
23
|
+
);
|
|
24
|
+
const result = await getHttpsOptions();
|
|
25
|
+
expect(result.key).toEqual("PRIVATE KEY");
|
|
26
|
+
expect(result.cert).toEqual("PUBLIC KEY");
|
|
27
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
28
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
29
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should generate and cache new keys if none are cached", async () => {
|
|
33
|
+
const result = await getHttpsOptions();
|
|
34
|
+
const key = fs.readFileSync(
|
|
35
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
36
|
+
"utf8"
|
|
37
|
+
);
|
|
38
|
+
const cert = fs.readFileSync(
|
|
39
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
40
|
+
"utf8"
|
|
41
|
+
);
|
|
42
|
+
expect(result.key).toEqual(key);
|
|
43
|
+
expect(result.cert).toEqual(cert);
|
|
44
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
45
|
+
`"Generating new self-signed certificate..."`
|
|
46
|
+
);
|
|
47
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
48
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should generate and cache new keys if cached files have expired", async () => {
|
|
52
|
+
fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
|
|
53
|
+
recursive: true,
|
|
54
|
+
});
|
|
55
|
+
const ORIGINAL_KEY = "EXPIRED PRIVATE KEY";
|
|
56
|
+
const ORIGINAL_CERT = "EXPIRED PUBLIC KEY";
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
59
|
+
ORIGINAL_KEY
|
|
60
|
+
);
|
|
61
|
+
fs.writeFileSync(
|
|
62
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
63
|
+
ORIGINAL_CERT
|
|
64
|
+
);
|
|
65
|
+
mockStatSync(/\.pem$/, { mtimeMs: new Date(2000).valueOf() });
|
|
66
|
+
|
|
67
|
+
const result = await getHttpsOptions();
|
|
68
|
+
const key = fs.readFileSync(
|
|
69
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
70
|
+
"utf8"
|
|
71
|
+
);
|
|
72
|
+
const cert = fs.readFileSync(
|
|
73
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
74
|
+
"utf8"
|
|
75
|
+
);
|
|
76
|
+
expect(key).not.toEqual(ORIGINAL_KEY);
|
|
77
|
+
expect(cert).not.toEqual(ORIGINAL_CERT);
|
|
78
|
+
expect(result.key).toEqual(key);
|
|
79
|
+
expect(result.cert).toEqual(cert);
|
|
80
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
81
|
+
`"Generating new self-signed certificate..."`
|
|
82
|
+
);
|
|
83
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
84
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should warn if not able to write to the cache", async () => {
|
|
88
|
+
mockWriteFileSyncThrow(/\.pem$/);
|
|
89
|
+
await getHttpsOptions();
|
|
90
|
+
expect(
|
|
91
|
+
fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/key.pem"))
|
|
92
|
+
).toBe(false);
|
|
93
|
+
expect(
|
|
94
|
+
fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/cert.pem"))
|
|
95
|
+
).toBe(false);
|
|
96
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
97
|
+
`"Generating new self-signed certificate..."`
|
|
98
|
+
);
|
|
99
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
100
|
+
"Unable to cache generated self-signed certificate in home/.wrangler/local-cert.
|
|
101
|
+
ERROR: Cannot write file"
|
|
102
|
+
`);
|
|
103
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
function mockStatSync(matcher: RegExp, stats: Partial<fs.Stats>) {
|
|
108
|
+
const originalStatSync = jest.requireActual("node:fs").statSync;
|
|
109
|
+
jest.spyOn(fs, "statSync").mockImplementation((statPath, options) => {
|
|
110
|
+
return matcher.test(statPath.toString())
|
|
111
|
+
? (stats as fs.Stats)
|
|
112
|
+
: originalStatSync(statPath, options);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function mockWriteFileSyncThrow(matcher: RegExp) {
|
|
117
|
+
const originalWriteFileSync = jest.requireActual("node:fs").writeFileSync;
|
|
118
|
+
jest
|
|
119
|
+
.spyOn(fs, "writeFileSync")
|
|
120
|
+
.mockImplementation((filePath, data, options) => {
|
|
121
|
+
if (matcher.test(filePath.toString())) {
|
|
122
|
+
throw new Error("ERROR: Cannot write file");
|
|
123
|
+
} else {
|
|
124
|
+
return originalWriteFileSync(filePath, data, options);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -63,7 +63,7 @@ describe("wrangler", () => {
|
|
|
63
63
|
await expect(
|
|
64
64
|
runWrangler("invalid-command")
|
|
65
65
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
66
|
-
`"Unknown
|
|
66
|
+
`"Unknown argument: invalid-command"`
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
@@ -89,7 +89,7 @@ describe("wrangler", () => {
|
|
|
89
89
|
-v, --version Show version number [boolean]
|
|
90
90
|
--legacy-env Use legacy environments [boolean]
|
|
91
91
|
|
|
92
|
-
Unknown
|
|
92
|
+
Unknown argument: invalid-command"
|
|
93
93
|
`);
|
|
94
94
|
});
|
|
95
95
|
});
|
|
@@ -812,14 +812,6 @@ describe("wrangler", () => {
|
|
|
812
812
|
expect(fs.existsSync("./wrangler.toml")).toBe(true);
|
|
813
813
|
});
|
|
814
814
|
|
|
815
|
-
it("should error if `--type` is used", async () => {
|
|
816
|
-
await expect(
|
|
817
|
-
runWrangler("init --type")
|
|
818
|
-
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
819
|
-
`"The --type option is no longer supported."`
|
|
820
|
-
);
|
|
821
|
-
});
|
|
822
|
-
|
|
823
815
|
it("should error if `--type javascript` is used", async () => {
|
|
824
816
|
await expect(
|
|
825
817
|
runWrangler("init --type javascript")
|
|
@@ -854,7 +846,7 @@ describe("wrangler", () => {
|
|
|
854
846
|
Try using \`wrangler dev\` to to try out a worker during development.
|
|
855
847
|
"
|
|
856
848
|
`);
|
|
857
|
-
await expect(runWrangler(
|
|
849
|
+
await expect(runWrangler(`preview GET "SomeBody"`)).rejects
|
|
858
850
|
.toThrowErrorMatchingInlineSnapshot(`
|
|
859
851
|
"DEPRECATION WARNING:
|
|
860
852
|
The \`wrangler preview\` command has been deprecated.
|
|
@@ -863,4 +855,129 @@ describe("wrangler", () => {
|
|
|
863
855
|
`);
|
|
864
856
|
});
|
|
865
857
|
});
|
|
858
|
+
|
|
859
|
+
describe("subcommand implicit help ran on imcomplete command execution", () => {
|
|
860
|
+
function endEventLoop() {
|
|
861
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
862
|
+
}
|
|
863
|
+
it("no subcommand for 'secret' should display a list of available subcommands", async () => {
|
|
864
|
+
await runWrangler("secret");
|
|
865
|
+
await endEventLoop();
|
|
866
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
867
|
+
"wrangler secret
|
|
868
|
+
|
|
869
|
+
🤫 Generate a secret that can be referenced in the worker script
|
|
870
|
+
|
|
871
|
+
Commands:
|
|
872
|
+
wrangler secret put <key> Create or update a secret variable for a script
|
|
873
|
+
wrangler secret delete <key> Delete a secret variable from a script
|
|
874
|
+
wrangler secret list List all secrets for a script
|
|
875
|
+
|
|
876
|
+
Flags:
|
|
877
|
+
-c, --config Path to .toml configuration file [string]
|
|
878
|
+
-h, --help Show help [boolean]
|
|
879
|
+
-v, --version Show version number [boolean]
|
|
880
|
+
--legacy-env Use legacy environments [boolean]"
|
|
881
|
+
`);
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("no subcommand 'kv:namespace' should display a list of available subcommands", async () => {
|
|
885
|
+
await runWrangler("kv:namespace");
|
|
886
|
+
await endEventLoop();
|
|
887
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
888
|
+
"wrangler kv:namespace
|
|
889
|
+
|
|
890
|
+
🗂️ Interact with your Workers KV Namespaces
|
|
891
|
+
|
|
892
|
+
Commands:
|
|
893
|
+
wrangler kv:namespace create <namespace> Create a new namespace
|
|
894
|
+
wrangler kv:namespace list Outputs a list of all KV namespaces associated with your account id.
|
|
895
|
+
wrangler kv:namespace delete Deletes a given namespace.
|
|
896
|
+
|
|
897
|
+
Flags:
|
|
898
|
+
-c, --config Path to .toml configuration file [string]
|
|
899
|
+
-h, --help Show help [boolean]
|
|
900
|
+
-v, --version Show version number [boolean]
|
|
901
|
+
--legacy-env Use legacy environments [boolean]"
|
|
902
|
+
`);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it("no subcommand 'kv:key' should display a list of available subcommands", async () => {
|
|
906
|
+
await runWrangler("kv:key");
|
|
907
|
+
await endEventLoop();
|
|
908
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
909
|
+
"wrangler kv:key
|
|
910
|
+
|
|
911
|
+
🔑 Individually manage Workers KV key-value pairs
|
|
912
|
+
|
|
913
|
+
Commands:
|
|
914
|
+
wrangler kv:key put <key> [value] Writes a single key/value pair to the given namespace.
|
|
915
|
+
wrangler kv:key list Outputs a list of all keys in a given namespace.
|
|
916
|
+
wrangler kv:key get <key> Reads a single value by key from the given namespace.
|
|
917
|
+
wrangler kv:key delete <key> Removes a single key value pair from the given namespace.
|
|
918
|
+
|
|
919
|
+
Flags:
|
|
920
|
+
-c, --config Path to .toml configuration file [string]
|
|
921
|
+
-h, --help Show help [boolean]
|
|
922
|
+
-v, --version Show version number [boolean]
|
|
923
|
+
--legacy-env Use legacy environments [boolean]"
|
|
924
|
+
`);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
it("no subcommand 'kv:bulk' should display a list of available subcommands", async () => {
|
|
928
|
+
await runWrangler("kv:bulk");
|
|
929
|
+
await endEventLoop();
|
|
930
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
931
|
+
"wrangler kv:bulk
|
|
932
|
+
|
|
933
|
+
💪 Interact with multiple Workers KV key-value pairs at once
|
|
934
|
+
|
|
935
|
+
Commands:
|
|
936
|
+
wrangler kv:bulk put <filename> Upload multiple key-value pairs to a namespace
|
|
937
|
+
wrangler kv:bulk delete <filename> Delete multiple key-value pairs from a namespace
|
|
938
|
+
|
|
939
|
+
Flags:
|
|
940
|
+
-c, --config Path to .toml configuration file [string]
|
|
941
|
+
-h, --help Show help [boolean]
|
|
942
|
+
-v, --version Show version number [boolean]
|
|
943
|
+
--legacy-env Use legacy environments [boolean]"
|
|
944
|
+
`);
|
|
945
|
+
});
|
|
946
|
+
it("no subcommand 'r2' should display a list of available subcommands", async () => {
|
|
947
|
+
await runWrangler("r2");
|
|
948
|
+
await endEventLoop();
|
|
949
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
950
|
+
"wrangler r2
|
|
951
|
+
|
|
952
|
+
📦 Interact with an R2 store
|
|
953
|
+
|
|
954
|
+
Commands:
|
|
955
|
+
wrangler r2 bucket Manage R2 buckets
|
|
956
|
+
|
|
957
|
+
Flags:
|
|
958
|
+
-c, --config Path to .toml configuration file [string]
|
|
959
|
+
-h, --help Show help [boolean]
|
|
960
|
+
-v, --version Show version number [boolean]
|
|
961
|
+
--legacy-env Use legacy environments [boolean]"
|
|
962
|
+
`);
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
describe("Deprecated commands", () => {
|
|
966
|
+
it("should print a deprecation message for 'generate'", async () => {
|
|
967
|
+
await runWrangler("generate").catch((err) => {
|
|
968
|
+
expect(err.message).toMatchInlineSnapshot(`
|
|
969
|
+
"DEPRECATION WARNING:
|
|
970
|
+
\`wrangler generate\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#generate for alternatives"
|
|
971
|
+
`);
|
|
972
|
+
});
|
|
973
|
+
});
|
|
974
|
+
it("should print a deprecation message for 'build'", async () => {
|
|
975
|
+
await runWrangler("build").catch((err) => {
|
|
976
|
+
expect(err.message).toMatchInlineSnapshot(`
|
|
977
|
+
"DEPRECATION WARNING:
|
|
978
|
+
\`wrangler build\` has been deprecated, please refer to https://github.com/cloudflare/wrangler2/blob/main/docs/deprecations.md#build for alternatives"
|
|
979
|
+
`);
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
});
|
|
866
983
|
});
|
|
@@ -43,15 +43,27 @@ jest.mock("../cfetch/internal");
|
|
|
43
43
|
jest.mock("../dialogs");
|
|
44
44
|
|
|
45
45
|
// By default (if not configured by mockConfirm()) calls to `confirm()` should throw.
|
|
46
|
-
(confirm as jest.Mock).mockImplementation(() => {
|
|
46
|
+
(confirm as jest.Mock).mockImplementation((text: string) => {
|
|
47
47
|
throw new Error(
|
|
48
|
-
|
|
48
|
+
`Unexpected call to \`confirm("${text}")\`.\nYou should use \`mockConfirm()\` to mock calls to \`confirm()\` with expectations. Search the codebase for \`mockConfirm\` to learn more.`
|
|
49
49
|
);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
// By default (if not configured by mockPrompt()) calls to `prompt()` should throw.
|
|
53
|
-
(prompt as jest.Mock).mockImplementation(() => {
|
|
53
|
+
(prompt as jest.Mock).mockImplementation((text: string) => {
|
|
54
54
|
throw new Error(
|
|
55
|
-
|
|
55
|
+
`Unexpected call to \`prompt(${text}, ...)\`.\nYou should use \`mockPrompt()\` to mock calls to \`prompt()\` with expectations. Search the codebase for \`mockPrompt\` to learn more.`
|
|
56
56
|
);
|
|
57
57
|
});
|
|
58
|
+
|
|
59
|
+
jest.mock("../dev/dev", () => {
|
|
60
|
+
const { useApp } = jest.requireActual("ink");
|
|
61
|
+
const { useEffect } = jest.requireActual("react");
|
|
62
|
+
return jest.fn().mockImplementation(() => {
|
|
63
|
+
const { exit } = useApp();
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
exit();
|
|
66
|
+
});
|
|
67
|
+
return null;
|
|
68
|
+
});
|
|
69
|
+
});
|