wrangler 2.4.4 → 2.5.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/bin/wrangler.js +20 -8
- package/miniflare-dist/index.mjs +8 -3
- package/package.json +1 -1
- package/src/__tests__/delete.test.ts +81 -48
- package/src/__tests__/dev.test.tsx +8 -8
- package/src/__tests__/helpers/mock-oauth-flow.ts +5 -1
- package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/queues.test.ts +155 -67
- package/src/__tests__/tail.test.ts +207 -108
- package/src/__tests__/user.test.ts +43 -69
- package/src/d1/backups.tsx +7 -2
- package/src/d1/delete.tsx +4 -4
- package/src/d1/index.ts +2 -0
- package/src/d1/migrations/apply.tsx +6 -5
- package/src/d1/migrations/helpers.ts +4 -2
- package/src/d1/migrations/list.tsx +2 -2
- package/src/d1/migrations/options.ts +18 -0
- package/src/dev/dev.tsx +46 -22
- package/src/dev/local.tsx +18 -14
- package/src/dev/start-server.ts +22 -21
- package/src/dev.tsx +10 -11
- package/src/miniflare-cli/assets.ts +7 -2
- package/src/miniflare-cli/index.ts +1 -1
- package/src/proxy.ts +5 -0
- package/wrangler-dist/cli.js +219 -174
package/bin/wrangler.js
CHANGED
|
@@ -62,15 +62,21 @@ Consider using a Node.js version manager such as https://volta.sh/ or https://gi
|
|
|
62
62
|
...process.argv.slice(2),
|
|
63
63
|
],
|
|
64
64
|
{
|
|
65
|
-
stdio: "inherit",
|
|
65
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
66
66
|
env: {
|
|
67
67
|
...process.env,
|
|
68
68
|
NODE_EXTRA_CA_CERTS: pathToCACerts,
|
|
69
69
|
},
|
|
70
70
|
}
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
)
|
|
72
|
+
.on("exit", (code) =>
|
|
73
|
+
process.exit(code === undefined || code === null ? 0 : code)
|
|
74
|
+
)
|
|
75
|
+
.on("message", (message) => {
|
|
76
|
+
if (process.send) {
|
|
77
|
+
process.send(message);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
/**
|
|
@@ -95,11 +101,17 @@ function runDelegatedWrangler() {
|
|
|
95
101
|
process.execPath,
|
|
96
102
|
[resolvedBinaryPath, ...process.argv.slice(2)],
|
|
97
103
|
{
|
|
98
|
-
stdio: "inherit",
|
|
104
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
99
105
|
}
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
)
|
|
107
|
+
.on("exit", (code) =>
|
|
108
|
+
process.exit(code === undefined || code === null ? 0 : code)
|
|
109
|
+
)
|
|
110
|
+
.on("message", (message) => {
|
|
111
|
+
if (process.send) {
|
|
112
|
+
process.send(message);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
/**
|
package/miniflare-dist/index.mjs
CHANGED
|
@@ -5974,11 +5974,11 @@ function parseRedirects(input) {
|
|
|
5974
5974
|
|
|
5975
5975
|
// src/miniflare-cli/assets.ts
|
|
5976
5976
|
var import_mime = __toESM(require_mime());
|
|
5977
|
-
import { fetch as miniflareFetch2 } from "@miniflare/core";
|
|
5978
5977
|
import {
|
|
5979
5978
|
Response as MiniflareResponse2,
|
|
5980
5979
|
Request as MiniflareRequest2
|
|
5981
5980
|
} from "@miniflare/core";
|
|
5981
|
+
import { upgradingFetch as miniflareFetch2 } from "@miniflare/web-sockets";
|
|
5982
5982
|
import { watch } from "chokidar";
|
|
5983
5983
|
|
|
5984
5984
|
// src/pages/hash.tsx
|
|
@@ -6000,7 +6000,12 @@ async function generateASSETSBinding(options) {
|
|
|
6000
6000
|
try {
|
|
6001
6001
|
const url = new URL(miniflareRequest.url);
|
|
6002
6002
|
url.host = `localhost:${options.proxyPort}`;
|
|
6003
|
-
|
|
6003
|
+
const proxyRequest = new MiniflareRequest2(url, miniflareRequest);
|
|
6004
|
+
if (proxyRequest.headers.get("Upgrade") === "websocket") {
|
|
6005
|
+
proxyRequest.headers.delete("Sec-WebSocket-Accept");
|
|
6006
|
+
proxyRequest.headers.delete("Sec-WebSocket-Key");
|
|
6007
|
+
}
|
|
6008
|
+
return await miniflareFetch2(proxyRequest);
|
|
6004
6009
|
} catch (thrown) {
|
|
6005
6010
|
options.log.error(new Error(`Could not proxy request: ${thrown}`));
|
|
6006
6011
|
return new MiniflareResponse2(
|
|
@@ -6288,7 +6293,7 @@ async function main() {
|
|
|
6288
6293
|
}
|
|
6289
6294
|
process.send && process.send(
|
|
6290
6295
|
JSON.stringify({
|
|
6291
|
-
mfPort,
|
|
6296
|
+
port: mfPort,
|
|
6292
6297
|
ready: true,
|
|
6293
6298
|
durableObjectsPort: durableObjectsMfPort
|
|
6294
6299
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { rest } from "msw";
|
|
1
2
|
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
|
|
2
|
-
import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
|
|
3
3
|
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
4
4
|
import { mockConfirm } from "./helpers/mock-dialogs";
|
|
5
|
+
import { msw } from "./helpers/msw";
|
|
5
6
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
6
7
|
import { runWrangler } from "./helpers/run-wrangler";
|
|
7
8
|
import writeWranglerToml from "./helpers/write-wrangler-toml";
|
|
@@ -12,10 +13,6 @@ describe("delete", () => {
|
|
|
12
13
|
mockApiToken();
|
|
13
14
|
runInTempDir();
|
|
14
15
|
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
unsetAllMocks();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
16
|
const std = mockConsoleMethods();
|
|
20
17
|
|
|
21
18
|
it("should delete an entire service by name", async () => {
|
|
@@ -107,14 +104,19 @@ describe("delete", () => {
|
|
|
107
104
|
});
|
|
108
105
|
mockListKVNamespacesRequest(...kvNamespaces);
|
|
109
106
|
// it should only try to delete the site namespace associated with this worker
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
msw.use(
|
|
108
|
+
rest.delete(
|
|
109
|
+
"*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
|
|
110
|
+
(req, res, ctx) => {
|
|
111
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
112
|
+
return res.once(
|
|
113
|
+
ctx.status(200),
|
|
114
|
+
ctx.json({ success: true, errors: [], messages: [], result: null })
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
)
|
|
117
118
|
);
|
|
119
|
+
|
|
118
120
|
mockDeleteWorkerRequest({ name: "my-script" });
|
|
119
121
|
await runWrangler("delete --name my-script");
|
|
120
122
|
expect(std).toMatchInlineSnapshot(`
|
|
@@ -155,22 +157,40 @@ describe("delete", () => {
|
|
|
155
157
|
mockListKVNamespacesRequest(...kvNamespaces);
|
|
156
158
|
// it should only try to delete the site namespace associated with this worker
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
msw.use(
|
|
161
|
+
rest.delete(
|
|
162
|
+
"*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
|
|
163
|
+
(req, res, ctx) => {
|
|
164
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
165
|
+
return res.once(
|
|
166
|
+
ctx.status(200),
|
|
167
|
+
ctx.json({
|
|
168
|
+
success: true,
|
|
169
|
+
errors: [],
|
|
170
|
+
messages: [],
|
|
171
|
+
result: {},
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
)
|
|
165
176
|
);
|
|
166
177
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
msw.use(
|
|
179
|
+
rest.delete(
|
|
180
|
+
"*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-preview-ns",
|
|
181
|
+
(req, res, ctx) => {
|
|
182
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
183
|
+
return res.once(
|
|
184
|
+
ctx.status(200),
|
|
185
|
+
ctx.json({
|
|
186
|
+
success: true,
|
|
187
|
+
errors: [],
|
|
188
|
+
messages: [],
|
|
189
|
+
result: {},
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
)
|
|
174
194
|
);
|
|
175
195
|
|
|
176
196
|
mockDeleteWorkerRequest({ name: "my-script" });
|
|
@@ -197,34 +217,47 @@ function mockDeleteWorkerRequest(
|
|
|
197
217
|
} = {}
|
|
198
218
|
) {
|
|
199
219
|
const { env, legacyEnv, name } = options;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
: `${name || "test-name"}`
|
|
211
|
-
);
|
|
220
|
+
msw.use(
|
|
221
|
+
rest.delete(
|
|
222
|
+
"*/accounts/:accountId/workers/services/:scriptName",
|
|
223
|
+
(req, res, ctx) => {
|
|
224
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
225
|
+
expect(req.params.scriptName).toEqual(
|
|
226
|
+
legacyEnv && env
|
|
227
|
+
? `${name ?? "test-name"}-${env}`
|
|
228
|
+
: `${name ?? "test-name"}`
|
|
229
|
+
);
|
|
212
230
|
|
|
213
|
-
|
|
231
|
+
expect(req.url.searchParams.get("force")).toEqual("true");
|
|
214
232
|
|
|
215
|
-
|
|
216
|
-
|
|
233
|
+
return res.once(
|
|
234
|
+
ctx.status(200),
|
|
235
|
+
ctx.json({
|
|
236
|
+
success: true,
|
|
237
|
+
errors: [],
|
|
238
|
+
messages: [],
|
|
239
|
+
result: null,
|
|
240
|
+
})
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
)
|
|
217
244
|
);
|
|
218
245
|
}
|
|
219
246
|
|
|
220
247
|
/** Create a mock handler for the request to get a list of all KV namespaces. */
|
|
221
248
|
function mockListKVNamespacesRequest(...namespaces: KVNamespaceInfo[]) {
|
|
222
|
-
|
|
223
|
-
"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
msw.use(
|
|
250
|
+
rest.get("*/accounts/:accountId/storage/kv/namespaces", (req, res, ctx) => {
|
|
251
|
+
expect(req.params.accountId).toEqual("some-account-id");
|
|
252
|
+
return res.once(
|
|
253
|
+
ctx.status(200),
|
|
254
|
+
ctx.json({
|
|
255
|
+
success: true,
|
|
256
|
+
errors: [],
|
|
257
|
+
messages: [],
|
|
258
|
+
result: namespaces,
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
})
|
|
229
262
|
);
|
|
230
263
|
}
|
|
@@ -792,7 +792,7 @@ describe("wrangler dev", () => {
|
|
|
792
792
|
});
|
|
793
793
|
fs.writeFileSync("index.js", `export default {};`);
|
|
794
794
|
await runWrangler("dev");
|
|
795
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
795
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("0.0.0.0");
|
|
796
796
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
797
797
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
798
798
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -807,7 +807,7 @@ describe("wrangler dev", () => {
|
|
|
807
807
|
});
|
|
808
808
|
fs.writeFileSync("index.js", `export default {};`);
|
|
809
809
|
await runWrangler("dev");
|
|
810
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
810
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("1.2.3.4");
|
|
811
811
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
812
812
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
813
813
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -822,7 +822,7 @@ describe("wrangler dev", () => {
|
|
|
822
822
|
});
|
|
823
823
|
fs.writeFileSync("index.js", `export default {};`);
|
|
824
824
|
await runWrangler("dev --ip=5.6.7.8");
|
|
825
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
825
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("5.6.7.8");
|
|
826
826
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
827
827
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
828
828
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -933,7 +933,7 @@ describe("wrangler dev", () => {
|
|
|
933
933
|
});
|
|
934
934
|
fs.writeFileSync("index.js", `export default {};`);
|
|
935
935
|
await runWrangler("dev");
|
|
936
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
936
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(8787);
|
|
937
937
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
938
938
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
939
939
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -951,7 +951,7 @@ describe("wrangler dev", () => {
|
|
|
951
951
|
(getPort as jest.Mock).mockResolvedValue(98765);
|
|
952
952
|
|
|
953
953
|
await runWrangler("dev");
|
|
954
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
954
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(8888);
|
|
955
955
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
956
956
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
957
957
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -985,7 +985,7 @@ describe("wrangler dev", () => {
|
|
|
985
985
|
(getPort as jest.Mock).mockResolvedValue(98765);
|
|
986
986
|
|
|
987
987
|
await runWrangler("dev --port=9999");
|
|
988
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
988
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(9999);
|
|
989
989
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
990
990
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
991
991
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -1000,7 +1000,7 @@ describe("wrangler dev", () => {
|
|
|
1000
1000
|
(getPort as jest.Mock).mockResolvedValue(98765);
|
|
1001
1001
|
|
|
1002
1002
|
await runWrangler("dev");
|
|
1003
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
1003
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(98765);
|
|
1004
1004
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
1005
1005
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
1006
1006
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -1030,7 +1030,7 @@ describe("wrangler dev", () => {
|
|
|
1030
1030
|
});
|
|
1031
1031
|
fs.writeFileSync("index.js", `export default {};`);
|
|
1032
1032
|
await runWrangler("dev");
|
|
1033
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
1033
|
+
expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("0.0.0.0");
|
|
1034
1034
|
expect(std.out).toMatchInlineSnapshot(`
|
|
1035
1035
|
"Your worker has access to the following bindings:
|
|
1036
1036
|
- Durable Objects:
|
|
@@ -42,10 +42,13 @@ export const mockOAuthFlow = () => {
|
|
|
42
42
|
* at the OAuth URL, it will automatically trigger the callback URL on the mock HttpServer that
|
|
43
43
|
* we have created as part of the call to `mockOAuthFlow()`.
|
|
44
44
|
*/
|
|
45
|
-
const mockOAuthServerCallback = (
|
|
45
|
+
const mockOAuthServerCallback = (
|
|
46
|
+
respondWith?: "timeout" | "success" | "failure" | GrantResponseOptions
|
|
47
|
+
) => {
|
|
46
48
|
(
|
|
47
49
|
openInBrowser as jest.MockedFunction<typeof openInBrowser>
|
|
48
50
|
).mockImplementation(async (url: string) => {
|
|
51
|
+
if (respondWith) mockGrantAuthorization({ respondWith });
|
|
49
52
|
// We don't support the grant response timing out.
|
|
50
53
|
if (oauthGrantResponse === "timeout") {
|
|
51
54
|
throw "unimplemented";
|
|
@@ -68,6 +71,7 @@ export const mockOAuthFlow = () => {
|
|
|
68
71
|
});
|
|
69
72
|
};
|
|
70
73
|
|
|
74
|
+
//TODO: this can just handled in `mockOAuthServerCallback`
|
|
71
75
|
const mockGrantAuthorization = ({
|
|
72
76
|
respondWith,
|
|
73
77
|
}: {
|
|
@@ -13,24 +13,19 @@ export const mswSuccessOauthHandlers = [
|
|
|
13
13
|
);
|
|
14
14
|
}),
|
|
15
15
|
// revoke access token
|
|
16
|
-
rest.post(
|
|
17
|
-
|
|
18
|
-
(_, response, context) =>
|
|
19
|
-
response.once(context.status(200), context.text(""))
|
|
16
|
+
rest.post("*/oauth2/revoke", (_, response, context) =>
|
|
17
|
+
response.once(context.status(200), context.text(""))
|
|
20
18
|
),
|
|
21
19
|
// exchange (auth code | refresh token) for access token
|
|
22
|
-
rest.post(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
),
|
|
20
|
+
rest.post("*/oauth2/token", (_, response, context) => {
|
|
21
|
+
return response.once(
|
|
22
|
+
context.status(200),
|
|
23
|
+
context.json({
|
|
24
|
+
access_token: "test-access-token",
|
|
25
|
+
expires_in: 100000,
|
|
26
|
+
refresh_token: "test-refresh-token",
|
|
27
|
+
scope: "account:read",
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
}),
|
|
36
31
|
];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { rest } from "msw";
|
|
4
|
+
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
5
|
+
import { USER_AUTH_CONFIG_FILE, writeAuthConfigFile } from "../user";
|
|
6
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
7
|
+
import { msw } from "./helpers/msw";
|
|
8
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
9
|
+
import { runWrangler } from "./helpers/run-wrangler";
|
|
10
|
+
|
|
11
|
+
describe("logout", () => {
|
|
12
|
+
runInTempDir();
|
|
13
|
+
const std = mockConsoleMethods();
|
|
14
|
+
|
|
15
|
+
it("should exit with a message stating the user is not logged in", async () => {
|
|
16
|
+
await runWrangler("logout");
|
|
17
|
+
expect(std.out).toMatchInlineSnapshot(`"Not logged in, exiting..."`);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should logout user that has been properly logged in", async () => {
|
|
21
|
+
writeAuthConfigFile({
|
|
22
|
+
oauth_token: "some-oauth-tok",
|
|
23
|
+
refresh_token: "some-refresh-tok",
|
|
24
|
+
});
|
|
25
|
+
// Make sure that logout removed the config file containing the auth tokens.
|
|
26
|
+
const config = path.join(
|
|
27
|
+
getGlobalWranglerConfigPath(),
|
|
28
|
+
USER_AUTH_CONFIG_FILE
|
|
29
|
+
);
|
|
30
|
+
let counter = 0;
|
|
31
|
+
msw.use(
|
|
32
|
+
rest.post("*/oauth2/revoke", (_, response, context) => {
|
|
33
|
+
// Make sure that we made the request to logout.
|
|
34
|
+
counter += 1;
|
|
35
|
+
response.once(context.status(200), context.text(""));
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(fs.existsSync(config)).toBeTruthy();
|
|
40
|
+
|
|
41
|
+
await runWrangler("logout");
|
|
42
|
+
|
|
43
|
+
expect(std.out).toMatchInlineSnapshot(`"Successfully logged out."`);
|
|
44
|
+
expect(fs.existsSync(config)).toBeFalsy();
|
|
45
|
+
expect(counter).toBe(1);
|
|
46
|
+
});
|
|
47
|
+
});
|