Skip to content

Commit 23800f8

Browse files
authored
feat(vite-plugin-cloudflare): add tunnel shortcut hint (#13922)
1 parent 2e72c83 commit 23800f8

4 files changed

Lines changed: 138 additions & 207 deletions

File tree

.changeset/sour-falcons-share.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@cloudflare/vite-plugin": patch
3+
---
4+
5+
Add a tunnel shortcut hint when CLI shortcuts are printed
6+
7+
The Cloudflare Vite plugin now includes a `t + enter` tunnel hint alongside the other CLI shortcuts it prints.

packages/vite-plugin-cloudflare/src/__tests__/shortcuts.spec.ts

Lines changed: 78 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import { removeDirSync } from "@cloudflare/workers-utils";
66
import { afterAll, beforeAll, beforeEach, describe, test, vi } from "vitest";
77
import { PluginContext } from "../context";
88
import { resolvePluginConfig } from "../plugin-config";
9-
import {
10-
addBindingsShortcut,
11-
addExplorerShortcut,
12-
addTunnelShortcut,
13-
} from "../plugins/shortcuts";
9+
import { addShortcuts } from "../plugins/shortcuts";
1410
import * as tunnelPlugin from "../plugins/tunnel";
1511
import { satisfiesMinimumViteVersion } from "../utils";
1612
import type * as vite from "vite";
@@ -124,62 +120,84 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
124120
return () => removeDirSync(tempDir);
125121
});
126122

127-
test("display binding shortcut hint", ({ expect }) => {
123+
function createMockContext(options?: {
124+
auxiliaryWorkers?: Array<{ configPath: string }>;
125+
}) {
128126
const mockContext = new PluginContext({
129127
hasShownWorkerConfigWarnings: false,
130128
restartingDevServerCount: 0,
131129
tunnelHostnames: new Set(),
132130
});
131+
133132
mockContext.setResolvedPluginConfig(
134133
resolvePluginConfig(
135-
{ configPath: primaryConfigPath },
134+
{
135+
configPath: primaryConfigPath,
136+
auxiliaryWorkers: options?.auxiliaryWorkers,
137+
},
136138
{},
137139
{ command: "serve", mode: "development" }
138140
)
139141
);
140-
addBindingsShortcut(mockServer, mockContext);
142+
143+
return mockContext;
144+
}
145+
146+
test("prints shortcut hints in registration order", ({ expect }) => {
147+
vi.spyOn(tunnelPlugin, "isTunnelOpen").mockReturnValue(false);
148+
addShortcuts(mockServer, createMockContext());
141149

142150
serverLogs.info = [];
143151
mockServer.bindCLIShortcuts();
144152

145-
expect(normalize(serverLogs.info)).not.toMatch(
146-
"press b + enter to list configured Cloudflare bindings"
147-
);
153+
expect(normalize(serverLogs.info)).toBe("");
148154

149155
mockServer.bindCLIShortcuts({ print: true });
150156

151-
expect(normalize(serverLogs.info)).toMatch(
152-
"press b + enter to list configured Cloudflare bindings"
157+
expect(normalize(serverLogs.info)).toBe(
158+
[
159+
"➜ press b + enter to list configured Cloudflare bindings",
160+
"➜ press e + enter to open local explorer",
161+
"➜ press t + enter to start tunnel",
162+
].join("\n")
153163
);
154164
});
155165

156-
test("prints bindings with a single Worker", ({ expect }) => {
166+
test("registers custom shortcuts in order", ({ expect }) => {
157167
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
158-
const mockContext = new PluginContext({
159-
hasShownWorkerConfigWarnings: false,
160-
restartingDevServerCount: 0,
161-
tunnelHostnames: new Set(),
162-
});
163168

164-
mockContext.setResolvedPluginConfig(
165-
resolvePluginConfig(
166-
{ configPath: primaryConfigPath },
167-
{},
168-
{ command: "serve", mode: "development" }
169-
)
170-
);
169+
addShortcuts(mockServer, createMockContext());
171170

172-
addBindingsShortcut(mockServer, mockContext);
173171
expect(mockServer.bindCLIShortcuts).not.toBe(mockBindCLIShortcuts);
174-
expect(mockBindCLIShortcuts).toHaveBeenCalledExactlyOnceWith({
172+
expect(mockBindCLIShortcuts).toHaveBeenCalledWith({
175173
customShortcuts: [
176174
{
177175
key: "b",
178176
description: "list configured Cloudflare bindings",
179177
action: expect.any(Function),
180178
},
179+
{
180+
key: "e",
181+
description: "open local explorer",
182+
action: expect.any(Function),
183+
},
184+
{
185+
key: "t",
186+
description: "start or close tunnel",
187+
action: expect.any(Function),
188+
},
189+
{
190+
key: "a",
191+
description: "extend tunnel by 1 hour",
192+
action: expect.any(Function),
193+
},
181194
],
182195
});
196+
});
197+
198+
test("prints bindings with a single Worker", ({ expect }) => {
199+
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
200+
addShortcuts(mockServer, createMockContext());
183201

184202
const { customShortcuts } = mockBindCLIShortcuts.mock.calls[0]?.[0] ?? {};
185203
const printBindingShortcut = customShortcuts?.find((s) => s.key === "b");
@@ -204,35 +222,13 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
204222

205223
test("prints bindings with multi Workers", ({ expect }) => {
206224
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
207-
const mockContext = new PluginContext({
208-
hasShownWorkerConfigWarnings: false,
209-
restartingDevServerCount: 0,
210-
tunnelHostnames: new Set(),
211-
});
212-
213-
mockContext.setResolvedPluginConfig(
214-
resolvePluginConfig(
215-
{
216-
configPath: primaryConfigPath,
217-
auxiliaryWorkers: [{ configPath: auxiliaryConfigPath }],
218-
},
219-
{},
220-
{ command: "serve", mode: "development" }
221-
)
225+
addShortcuts(
226+
mockServer,
227+
createMockContext({
228+
auxiliaryWorkers: [{ configPath: auxiliaryConfigPath }],
229+
})
222230
);
223231

224-
addBindingsShortcut(mockServer, mockContext);
225-
expect(mockServer.bindCLIShortcuts).not.toBe(mockBindCLIShortcuts);
226-
expect(mockBindCLIShortcuts).toHaveBeenCalledExactlyOnceWith({
227-
customShortcuts: [
228-
{
229-
key: "b",
230-
description: "list configured Cloudflare bindings",
231-
action: expect.any(Function),
232-
},
233-
],
234-
});
235-
236232
const { customShortcuts } = mockBindCLIShortcuts.mock.calls[0]?.[0] ?? {};
237233
const printBindingShortcut = customShortcuts?.find((s) => s.key === "b");
238234

@@ -260,18 +256,7 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
260256

261257
test("registers explorer shortcut with correct URL", async ({ expect }) => {
262258
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
263-
264-
addExplorerShortcut(mockServer);
265-
266-
expect(mockBindCLIShortcuts).toHaveBeenCalledWith({
267-
customShortcuts: [
268-
{
269-
key: "e",
270-
description: "open local explorer",
271-
action: expect.any(Function),
272-
},
273-
],
274-
});
259+
addShortcuts(mockServer, createMockContext());
275260

276261
const { customShortcuts } = mockBindCLIShortcuts.mock.calls[0]?.[0] ?? {};
277262
const explorerShortcut = customShortcuts?.find((s) => s.key === "e");
@@ -284,35 +269,14 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
284269
});
285270

286271
test("registers tunnel shortcut and extends expiry", async ({ expect }) => {
287-
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
288-
const mockContext = new PluginContext({
289-
hasShownWorkerConfigWarnings: false,
290-
restartingDevServerCount: 0,
291-
tunnelHostnames: new Set(),
292-
});
293272
const toggleTunnelSpy = vi
294273
.spyOn(tunnelPlugin, "toggleTunnel")
295274
.mockResolvedValue(undefined);
296275
const extendExpirySpy = vi
297276
.spyOn(tunnelPlugin, "extendTunnelExpiry")
298277
.mockImplementation(() => {});
299-
300-
addTunnelShortcut(mockServer, mockContext);
301-
302-
expect(mockBindCLIShortcuts).toHaveBeenCalledWith({
303-
customShortcuts: [
304-
{
305-
key: "t",
306-
description: "start or close tunnel",
307-
action: expect.any(Function),
308-
},
309-
{
310-
key: "a",
311-
description: "extend tunnel by 1 hour",
312-
action: expect.any(Function),
313-
},
314-
],
315-
});
278+
const mockBindCLIShortcuts = vi.spyOn(mockServer, "bindCLIShortcuts");
279+
addShortcuts(mockServer, createMockContext());
316280

317281
const { customShortcuts } = mockBindCLIShortcuts.mock.calls[0]?.[0] ?? {};
318282
const toggleShortcut = customShortcuts?.find((s) => s.key === "t");
@@ -325,30 +289,31 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
325289
expect(extendExpirySpy).toHaveBeenCalledTimes(1);
326290
});
327291

328-
test("registers tunnel shortcuts even without tunnel config", ({
329-
expect,
330-
}) => {
331-
const mockContext = new PluginContext({
332-
hasShownWorkerConfigWarnings: false,
333-
restartingDevServerCount: 0,
334-
tunnelHostnames: new Set(),
335-
});
292+
test("display tunnel shortcut hint", ({ expect }) => {
293+
vi.spyOn(tunnelPlugin, "isTunnelOpen")
294+
.mockReturnValueOnce(false)
295+
.mockReturnValueOnce(true);
336296

337-
addTunnelShortcut(mockServer, mockContext);
297+
addShortcuts(mockServer, createMockContext());
338298

339-
expect(mockServer.bindCLIShortcuts).toHaveBeenCalledWith({
340-
customShortcuts: [
341-
{
342-
key: "t",
343-
description: "start or close tunnel",
344-
action: expect.any(Function),
345-
},
346-
{
347-
key: "a",
348-
description: "extend tunnel by 1 hour",
349-
action: expect.any(Function),
350-
},
351-
],
352-
});
299+
serverLogs.info = [];
300+
mockServer.bindCLIShortcuts();
301+
302+
expect(normalize(serverLogs.info)).not.toMatch(
303+
"press t + enter to start tunnel"
304+
);
305+
306+
mockServer.bindCLIShortcuts({ print: true });
307+
308+
expect(normalize(serverLogs.info)).toMatch(
309+
"press t + enter to start tunnel"
310+
);
311+
312+
serverLogs.info = [];
313+
mockServer.bindCLIShortcuts({ print: true });
314+
315+
expect(normalize(serverLogs.info)).toMatch(
316+
"press t + enter to close tunnel"
317+
);
353318
});
354319
});

0 commit comments

Comments
 (0)