Skip to content

Commit 6867559

Browse files
authored
Show quickpicks if there are multiple runtime matches for targetFramework (#4165)
* Reverse list * change to quickpicks * add test * remove * Add another test * remove * update comment * update again * see if tests work * delete * add
1 parent fe58f7f commit 6867559

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

src/commands/createNewProject/dotnetSteps/DotnetRuntimeStep.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@ export class DotnetRuntimeStep extends AzureWizardPromptStep<IProjectWizardConte
1616
public static async createStep(context: IProjectWizardContext): Promise<DotnetRuntimeStep> {
1717
if (context.targetFramework) {
1818
context.targetFramework = typeof context.targetFramework === 'string' ? [context.targetFramework] : context.targetFramework;
19-
const runtimes = await getRuntimes(context);
19+
const runtimes = (await getRuntimes(context));
2020
// if a targetFramework was provided from createNewProject
21-
const workerRuntime = runtimes.find(runtime => context.targetFramework?.includes(runtime.targetFramework));
21+
const filteredRuntimes = runtimes.filter(runtime => context.targetFramework?.includes(runtime.targetFramework));
22+
let workerRuntime: cliFeedUtils.IWorkerRuntime | undefined = undefined;
23+
if (filteredRuntimes.length > 1) {
24+
const placeHolder: string = localize('selectWorkerRuntime', 'Select a .NET runtime');
25+
workerRuntime = (await context.ui.showQuickPick(new DotnetRuntimeStep().getPicks(context, filteredRuntimes), { placeHolder })).data;
26+
} else if (filteredRuntimes.length === 1) {
27+
workerRuntime = filteredRuntimes[0];
28+
}
29+
2230
if (!workerRuntime) {
2331
throw new Error(localize('unknownFramework', 'Unrecognized target frameworks: "{0}". Available frameworks: {1}.',
2432
context.targetFramework.map(tf => `"${tf}"`).join(', '),
@@ -49,8 +57,11 @@ export class DotnetRuntimeStep extends AzureWizardPromptStep<IProjectWizardConte
4957
return !context.workerRuntime;
5058
}
5159

52-
private async getPicks(context: IProjectWizardContext): Promise<IAzureQuickPickItem<cliFeedUtils.IWorkerRuntime | undefined>[]> {
53-
const runtimes = await getRuntimes(context);
60+
private async getPicks(context: IProjectWizardContext, runtimes?: cliFeedUtils.IWorkerRuntime[]): Promise<IAzureQuickPickItem<cliFeedUtils.IWorkerRuntime | undefined>[]> {
61+
if (!runtimes) {
62+
runtimes = await getRuntimes(context);
63+
}
64+
5465
const picks: IAzureQuickPickItem<cliFeedUtils.IWorkerRuntime | undefined>[] = [];
5566
for (const runtime of runtimes) {
5667
picks.push({

test/api.test.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { runWithInputs } from '@microsoft/vscode-azext-dev';
77
import { type apiUtils } from "@microsoft/vscode-azext-utils";
88
import * as path from 'path';
99
import { extensions, type Extension } from "vscode";
10-
import { ProjectLanguage, extensionId, nonNullValue, registerOnActionStartHandler } from '../extension.bundle';
10+
import { FuncVersion, ProjectLanguage, extensionId, nonNullValue, registerOnActionStartHandler } from '../extension.bundle';
1111
// eslint-disable-next-line no-restricted-imports
1212
import { type AzureFunctionsExtensionApi } from '../src/vscode-azurefunctions.api';
1313
import { getTestWorkspaceFolder, testFolderPath } from './global.test';
14-
import { getJavaScriptValidateOptions, validateProject, type IValidateProjectOptions } from './project/validateProject';
14+
import { getCSharpValidateOptions, getJavaScriptValidateOptions, validateProject, type IValidateProjectOptions } from './project/validateProject';
1515

1616
suite(`AzureFunctionsExtensionApi`, () => {
1717
let api: AzureFunctionsExtensionApi;
@@ -73,4 +73,49 @@ suite(`AzureFunctionsExtensionApi`, () => {
7373
);
7474
await validateProject(folderPath, validateOptions);
7575
});
76+
77+
test('createFunction dotnet with targetFramework', async () => {
78+
const functionName: string = 'endpoint1';
79+
const language: string = ProjectLanguage.CSharp;
80+
const workspaceFolder = getTestWorkspaceFolder();
81+
const projectSubpath = 'api';
82+
const folderPath: string = path.join(workspaceFolder, projectSubpath);
83+
84+
await runWithInputs('api.createFunction', [language, /6/i, 'Company.Function', 'Anonymous'], registerOnActionStartHandler, async () => {
85+
await api.createFunction({
86+
folderPath,
87+
functionName,
88+
templateId: 'HttpTrigger',
89+
languageFilter: /^C\#$/i,
90+
functionSettings: { authLevel: 'anonymous' },
91+
targetFramework: ['net8.0', 'net7.0', 'net6.0']
92+
});
93+
});
94+
95+
const validateOptions: IValidateProjectOptions = getCSharpValidateOptions('net6.0', FuncVersion.v4, 1, projectSubpath, workspaceFolder);
96+
// Exclude .git because the test workspace folders are already inside a git repo so we don't do git init.
97+
validateOptions.excludedPaths?.push('.git');
98+
await validateProject(folderPath, validateOptions);
99+
});
100+
101+
// Intentionally pass a version (8) that hasn't been specified in targetFramework (6 & 7) to verify it isn't a possible pick. In the correct case (when 8 isn't a pick) we throw an error. api.createFunction swallows the error and returns undefined.
102+
// In the incorrect case (when 8 is a pick) the test fails since the 2 provided test inputs have already been used, but there are more prompts.
103+
test('createFunction with language not in targetFramework', async () => {
104+
const functionName: string = 'endpoint1';
105+
const language: string = ProjectLanguage.CSharp;
106+
const workspaceFolder = getTestWorkspaceFolder();
107+
const projectSubpath = 'api';
108+
const folderPath: string = path.join(workspaceFolder, projectSubpath);
109+
110+
await runWithInputs('api.createFunction', [language, /8/i], registerOnActionStartHandler, async () => {
111+
await api.createFunction({
112+
folderPath,
113+
functionName,
114+
templateId: 'HttpTrigger',
115+
languageFilter: /^C\#$/i,
116+
functionSettings: { authLevel: 'anonymous' },
117+
targetFramework: ['net7.0', 'net6.0']
118+
})
119+
});
120+
});
76121
});

test/project/validateProject.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,25 @@ export function getTypeScriptValidateOptions(options?: { version?: FuncVersion,
8181
return result;
8282
}
8383

84-
export function getCSharpValidateOptions(targetFramework: string, version: FuncVersion = defaultTestFuncVersion, numCsproj: number = 1): IValidateProjectOptions {
84+
export function getCSharpValidateOptions(targetFramework: string, version: FuncVersion = defaultTestFuncVersion, numCsproj: number = 1, projectSubpath?: string, workspaceFolder?: string): IValidateProjectOptions {
8585
return {
8686
language: ProjectLanguage.CSharp,
8787
version,
8888
expectedSettings: {
8989
'azureFunctions.projectLanguage': ProjectLanguage.CSharp,
9090
'azureFunctions.projectRuntime': version,
9191
'azureFunctions.preDeployTask': 'publish (functions)',
92-
'azureFunctions.deploySubpath': `bin/Release/${targetFramework}/publish`,
92+
'azureFunctions.deploySubpath': path.join(projectSubpath ?? '', `bin/Release/${targetFramework}/publish`),
9393
'debug.internalConsoleOptions': 'neverOpen',
9494
},
9595
expectedPaths: [
96-
{ globPattern: '*.csproj', numMatches: numCsproj }
96+
{ globPattern: path.join(projectSubpath ?? '', '*.csproj'), numMatches: numCsproj }
9797
],
9898
expectedExtensionRecs: [
9999
'ms-dotnettools.csharp'
100100
],
101101
excludedPaths: [
102-
'.funcignore'
102+
path.join(projectSubpath ?? '', '.funcignore')
103103
],
104104
expectedDebugConfigs: [
105105
'Attach to .NET Functions'
@@ -110,7 +110,8 @@ export function getCSharpValidateOptions(targetFramework: string, version: FuncV
110110
'clean release (functions)',
111111
'publish (functions)',
112112
'host start'
113-
]
113+
],
114+
workspaceFolder
114115
};
115116
}
116117

0 commit comments

Comments
 (0)