Skip to content

Commit acca0a5

Browse files
authored
feat(vscode): show quickPickProject on monorepos (#1026)
1 parent ad8887d commit acca0a5

File tree

11 files changed

+337
-71
lines changed

11 files changed

+337
-71
lines changed

extensions/vscode/src/commands/new-middleware.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
isDartFrogCLIInstalled,
1212
nearestParentDartFrogProject,
1313
normalizeRoutePath,
14+
quickPickProject,
1415
resolveDartFrogProjectPathFromActiveTextEditor,
1516
resolveDartFrogProjectPathFromWorkspaceFolders,
1617
suggestInstallingDartFrogCLI,
@@ -50,7 +51,15 @@ export const newMiddleware = async (uri: Uri | undefined): Promise<void> => {
5051
selectedPath = resolveDartFrogProjectPathFromActiveTextEditor();
5152

5253
if (!selectedPath) {
53-
selectedPath = resolveDartFrogProjectPathFromWorkspaceFolders();
54+
const dartFrogProjectsPaths =
55+
resolveDartFrogProjectPathFromWorkspaceFolders();
56+
if (dartFrogProjectsPaths && dartFrogProjectsPaths.length > 0) {
57+
const selection = await quickPickProject({}, dartFrogProjectsPaths);
58+
if (!selection) {
59+
return;
60+
}
61+
selectedPath = selection;
62+
}
5463
}
5564
if (!selectedPath) {
5665
selectedPath = await promptForTargetDirectory();

extensions/vscode/src/commands/new-route.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
isDartFrogCLIInstalled,
1212
nearestParentDartFrogProject,
1313
normalizeRoutePath,
14+
quickPickProject,
1415
resolveDartFrogProjectPathFromActiveTextEditor,
1516
resolveDartFrogProjectPathFromWorkspaceFolders,
1617
suggestInstallingDartFrogCLI,
@@ -50,7 +51,16 @@ export const newRoute = async (uri: Uri | undefined): Promise<void> => {
5051
selectedPath = resolveDartFrogProjectPathFromActiveTextEditor();
5152

5253
if (!selectedPath) {
53-
selectedPath = resolveDartFrogProjectPathFromWorkspaceFolders();
54+
const dartFrogProjectsPaths =
55+
resolveDartFrogProjectPathFromWorkspaceFolders();
56+
57+
if (dartFrogProjectsPaths && dartFrogProjectsPaths.length > 0) {
58+
const selection = await quickPickProject({}, dartFrogProjectsPaths);
59+
if (!selection) {
60+
return;
61+
}
62+
selectedPath = selection;
63+
}
5464
}
5565
if (!selectedPath) {
5666
selectedPath = await promptForTargetDirectory();

extensions/vscode/src/commands/start-daemon.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export const startDaemon = async (): Promise<void> => {
3434
return;
3535
}
3636

37-
let dartFrogProjectPath = resolveDartFrogProjectPathFromWorkspaceFolders();
37+
let dartFrogProjectPath: string | undefined;
38+
const dartFrogProjectsPaths =
39+
resolveDartFrogProjectPathFromWorkspaceFolders();
40+
if (dartFrogProjectsPaths && dartFrogProjectsPaths.length > 0) {
41+
dartFrogProjectPath = dartFrogProjectsPaths[0];
42+
}
3843
if (!dartFrogProjectPath) {
3944
dartFrogProjectPath = resolveDartFrogProjectPathFromActiveTextEditor();
4045
}

extensions/vscode/src/commands/start-dev-server.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Uri, commands, window } from "vscode";
99
import {
1010
isDartFrogCLIInstalled,
1111
nearestParentDartFrogProject,
12+
quickPickProject,
1213
resolveDartFrogProjectPathFromActiveTextEditor,
1314
resolveDartFrogProjectPathFromWorkspaceFolders,
1415
suggestInstallingDartFrogCLI,
@@ -57,13 +58,23 @@ export const startDevServer = async (): Promise<
5758
return;
5859
}
5960

60-
let workingPath = resolveDartFrogProjectPathFromWorkspaceFolders();
61-
if (!workingPath) {
62-
workingPath = resolveDartFrogProjectPathFromActiveTextEditor();
61+
let projectPath: string | undefined;
62+
const dartFrogProjectsPaths =
63+
resolveDartFrogProjectPathFromWorkspaceFolders();
64+
if (dartFrogProjectsPaths && dartFrogProjectsPaths.length > 0) {
65+
const selection = await quickPickProject({}, dartFrogProjectsPaths);
66+
if (!selection) {
67+
return;
68+
}
69+
projectPath = selection;
70+
}
71+
72+
if (!projectPath) {
73+
projectPath = resolveDartFrogProjectPathFromActiveTextEditor();
6374
}
6475

65-
const workingDirectory = workingPath
66-
? nearestParentDartFrogProject(workingPath)
76+
const workingDirectory = projectPath
77+
? nearestParentDartFrogProject(projectPath)
6778
: undefined;
6879
if (!workingDirectory) {
6980
await window.showErrorMessage(

extensions/vscode/src/test/suite/commands/new-middleware.test.ts

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ suite("new-middleware command", () => {
3232
resolveDartFrogProjectPathFromWorkspaceFolders: sinon.stub(),
3333
isDartFrogCLIInstalled: sinon.stub(),
3434
suggestInstallingDartFrogCLI: sinon.stub(),
35+
quickPickProject: sinon.stub(),
3536
};
3637
utilsStub.isDartFrogCLIInstalled.returns(true);
38+
utilsStub.quickPickProject.resolves(validUri.fsPath);
3739

3840
utilsStub.nearestParentDartFrogProject
3941
.withArgs(invalidUri.fsPath)
@@ -74,6 +76,46 @@ suite("new-middleware command", () => {
7476
sinon.assert.notCalled(utilsStub.suggestInstallingDartFrogCLI);
7577
});
7678

79+
suite("quick pick project", () => {
80+
test("is shown when Uri and active text editor are undefined and there is more than one Dart Frog project in workspace folders", async () => {
81+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
82+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
83+
"/home/dart_frog/routes",
84+
"/home/dart_frog2/routes",
85+
]);
86+
87+
await command.newMiddleware();
88+
89+
sinon.assert.calledOnceWithExactly(utilsStub.quickPickProject, {}, [
90+
"/home/dart_frog/routes",
91+
"/home/dart_frog2/routes",
92+
]);
93+
});
94+
95+
suite("is not shown", () => {
96+
beforeEach(() => {
97+
utilsStub.nearestParentDartFrogProject.returns("/home/dart_frog/");
98+
utilsStub.normalizeRoutePath.returns("/");
99+
});
100+
101+
test("when Uri is defined", async () => {
102+
await command.newMiddleware(validUri);
103+
104+
sinon.assert.notCalled(utilsStub.quickPickProject);
105+
});
106+
107+
test("when Uri is undefined but resolves a Dart Frog project from active text editor", async () => {
108+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns(
109+
"/home/dart_frog/routes/index.dart"
110+
);
111+
112+
await command.newMiddleware();
113+
114+
sinon.assert.notCalled(utilsStub.quickPickProject);
115+
});
116+
});
117+
});
118+
77119
suite("file open dialog", () => {
78120
test("is shown when Uri is undefined and fails to resolve a path from workspace folder", async () => {
79121
vscodeStub.window.showOpenDialog.resolves();
@@ -111,9 +153,9 @@ suite("new-middleware command", () => {
111153

112154
test("when Uri and active text editor are undefined but resolves a path from workspace folder", async () => {
113155
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
114-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
115-
"/home/dart_frog/routes"
116-
);
156+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
157+
"/home/dart_frog/routes",
158+
]);
117159
utilsStub.nearestParentDartFrogProject.returns("/home/dart_frog/");
118160
utilsStub.normalizeRoutePath.returns("/");
119161

@@ -204,9 +246,9 @@ suite("new-middleware command", () => {
204246

205247
test("when Uri and resolved active text editor are undefined but resolved workspace file is valid", async () => {
206248
vscodeStub.window.showInputBox.returns("animals/frog");
207-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
208-
"home/routes/animals/frog"
209-
);
249+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
250+
"home/routes/animals/frog",
251+
]);
210252
utilsStub.nearestParentDartFrogProject.returns(
211253
"home/routes/animals/frog"
212254
);
@@ -246,9 +288,9 @@ suite("new-middleware command", () => {
246288

247289
beforeEach(() => {
248290
vscodeStub.window.showInputBox.returns("animals/frog");
249-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
250-
"home/routes/animals/frog"
251-
);
291+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
292+
"home/routes/animals/frog",
293+
]);
252294
utilsStub.nearestParentDartFrogProject.returns(
253295
"home/routes/animals/frog"
254296
);
@@ -296,6 +338,19 @@ suite("new-middleware command", () => {
296338
});
297339
});
298340

341+
test("does not run `dart_frog new middleware` command when project selection is cancelled", async () => {
342+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
343+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
344+
"/home/dart_frog/routes",
345+
"/home/dart_frog2/routes",
346+
]);
347+
utilsStub.quickPickProject.resolves();
348+
349+
await command.newMiddleware();
350+
351+
sinon.assert.notCalled(childProcessStub.exec);
352+
});
353+
299354
suite("runs `dart_frog new middleware` command with route", () => {
300355
test("successfully with non-index route name", async () => {
301356
utilsStub.normalizeRoutePath.returns("food");
@@ -366,9 +421,9 @@ suite("new-middleware command", () => {
366421
});
367422

368423
test("successfully with prompt route path", async () => {
369-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
370-
"home/routes/animals/frog"
371-
);
424+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
425+
"home/routes/animals/frog",
426+
]);
372427
utilsStub.nearestParentDartFrogProject.returns(
373428
"home/routes/animals/frog"
374429
);

extensions/vscode/src/test/suite/commands/new-route.test.ts

Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ suite("new-route command", () => {
3434
resolveDartFrogProjectPathFromWorkspaceFolders: sinon.stub(),
3535
isDartFrogCLIInstalled: sinon.stub(),
3636
suggestInstallingDartFrogCLI: sinon.stub(),
37+
quickPickProject: sinon.stub(),
3738
};
3839

39-
utilsStub.nearestParentDartFrogProject
40-
.withArgs(invalidUri.fsPath)
41-
.returns(undefined);
4240
utilsStub.nearestParentDartFrogProject
4341
.withArgs(validUri.fsPath)
4442
.returns(validUri.fsPath);
@@ -149,13 +147,50 @@ suite("new-route command", () => {
149147
});
150148
});
151149

150+
suite("quick pick project", () => {
151+
test("is shown when Uri and active text editor are undefined and there is more than one Dart Frog project in workspace folders", async () => {
152+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
153+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
154+
"/home/dart_frog/routes",
155+
"/home/dart_frog2/routes",
156+
]);
157+
158+
await command.newRoute();
159+
160+
sinon.assert.calledOnceWithExactly(utilsStub.quickPickProject, {}, [
161+
"/home/dart_frog/routes",
162+
"/home/dart_frog2/routes",
163+
]);
164+
});
165+
166+
suite("is not shown", () => {
167+
beforeEach(() => {
168+
utilsStub.normalizeRoutePath.returns("/");
169+
});
170+
171+
test("when Uri is defined", async () => {
172+
await command.newRoute(validUri);
173+
174+
sinon.assert.notCalled(utilsStub.quickPickProject);
175+
});
176+
177+
test("when Uri is undefined but resolves a Dart Frog project from active text editor", async () => {
178+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns(
179+
"/home/dart_frog/routes/index.dart"
180+
);
181+
182+
await command.newRoute();
183+
184+
sinon.assert.notCalled(utilsStub.quickPickProject);
185+
});
186+
});
187+
});
188+
152189
suite("file open dialog", () => {
153190
test("is shown when Uri is undefined and fails to resolve a path from workspace", async () => {
154191
vscodeStub.window.showInputBox.returns(validRouteName);
155192
vscodeStub.window.showOpenDialog.resolves();
156-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
157-
undefined
158-
);
193+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns();
159194

160195
await command.newRoute();
161196

@@ -189,9 +224,9 @@ suite("new-route command", () => {
189224

190225
test("when Uri and active text editor are undefined but resolves a path from workspace folder", async () => {
191226
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
192-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns(
193-
validUri.fsPath
194-
);
227+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
228+
validUri.fsPath,
229+
]);
195230
utilsStub.normalizeRoutePath.returns("/");
196231

197232
await command.newRoute();
@@ -273,19 +308,62 @@ suite("new-route command", () => {
273308
});
274309
});
275310

276-
test("runs `dart_frog new route` command with prompted route successfully", async () => {
277-
utilsStub.normalizeRoutePath.returns("/");
311+
test("does not run `dart_frog new route` command when project selection is cancelled", async () => {
312+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
313+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
314+
"/home/dart_frog/routes",
315+
"/home/dart_frog2/routes",
316+
]);
317+
utilsStub.quickPickProject.resolves();
318+
319+
await command.newRoute();
320+
321+
sinon.assert.notCalled(childProcessStub.exec);
322+
});
323+
324+
suite("runs `dart_frog new route` command", () => {
278325
const routePath = "pizza";
279-
vscodeStub.window.showInputBox.returns(routePath);
280326

281-
await command.newRoute(validUri);
282-
const progressFunction = vscodeStub.window.withProgress.getCall(0).args[1];
283-
await progressFunction();
327+
beforeEach(() => {
328+
utilsStub.normalizeRoutePath.returns("/");
329+
vscodeStub.window.showInputBox.returns(routePath);
330+
});
284331

285-
sinon.assert.calledWith(
286-
childProcessStub.exec,
287-
`dart_frog new route '${routePath}'`
288-
);
332+
test("with prompted route successfully", async () => {
333+
await command.newRoute(validUri);
334+
335+
const progressFunction =
336+
vscodeStub.window.withProgress.getCall(0).args[1];
337+
await progressFunction();
338+
339+
sinon.assert.calledWith(
340+
childProcessStub.exec,
341+
`dart_frog new route '${routePath}'`
342+
);
343+
});
344+
345+
test("with cwd as selected project", async () => {
346+
utilsStub.resolveDartFrogProjectPathFromActiveTextEditor.returns();
347+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
348+
"/home/dart_frog/routes",
349+
validUri.fsPath,
350+
]);
351+
utilsStub.quickPickProject.resolves(validUri.fsPath);
352+
353+
await command.newRoute();
354+
355+
const progressFunction =
356+
vscodeStub.window.withProgress.getCall(0).args[1];
357+
await progressFunction();
358+
359+
sinon.assert.calledWith(
360+
childProcessStub.exec,
361+
`dart_frog new route '${routePath}'`,
362+
{
363+
cwd: validUri.fsPath,
364+
}
365+
);
366+
});
289367
});
290368

291369
test("shows error message when `dart_frog new route` fails", async () => {

extensions/vscode/src/test/suite/commands/start-daemon.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ suite("start-daemon command", () => {
117117
utilsStub.isDartFrogCLIInstalled.returns(true);
118118
dartFrogDaemon.DartFrogDaemon.instance.isReady = false;
119119
dartFrogDaemon.DartFrogDaemon.instance.invoke = sinon.stub();
120-
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns("path");
120+
utilsStub.resolveDartFrogProjectPathFromWorkspaceFolders.returns([
121+
"path",
122+
"another-path",
123+
]);
121124
utilsStub.nearestParentDartFrogProject.returns("path");
122125

123126
await command.startDaemon();

0 commit comments

Comments
 (0)