diff --git a/packages/vscode-extension/package.nls.json b/packages/vscode-extension/package.nls.json index a1297eddbc..5ae2f93d82 100644 --- a/packages/vscode-extension/package.nls.json +++ b/packages/vscode-extension/package.nls.json @@ -250,6 +250,9 @@ "teamstoolkit.handlers.provisionDescription": "[%s] is successfully created at [local address](%s). Continue to provision and then you can preview the app.", "teamstoolkit.handlers.provisionDescription.fallback": "[%s] is successfully created at %s. Continue to provision and then you can preview the app.", "teamstoolkit.handlers.provisionTitle": "Provision", + "teamstoolkit.handlers.manualStepRequired": "[%s] is created at [local address](%s). Follow the instructions in README file to preview your app.", + "teamstoolkit.handlers.manualStepRequired.fallback": "[%s] is created at %s. Follow the instructions in README file to preview your app.", + "teamstoolkit.handlers.manualStepRequiredTitle": "Open README", "teamstoolkit.handlers.referLinkForMoreDetails": "Please refer to this link for more details: ", "teamstoolkit.handlers.reportIssue": "Report Issue", "teamstoolkit.handlers.similarIssues": "Similar Issues", diff --git a/packages/vscode-extension/src/telemetry/extTelemetryEvents.ts b/packages/vscode-extension/src/telemetry/extTelemetryEvents.ts index 06c2f3371e..4927dfddb1 100644 --- a/packages/vscode-extension/src/telemetry/extTelemetryEvents.ts +++ b/packages/vscode-extension/src/telemetry/extTelemetryEvents.ts @@ -194,6 +194,9 @@ export enum TelemetryEvent { ShowProvisionNotification = "show-provision-notification", ClickProvision = "click-provision", + ShowManualStepRequiredNotification = "show-manual-step-required-notification", + ClickReadManualStep = "click-read-manual-step", + ShowLocalDebugNotification = "show-local-debug-notification", ShowLocalPreviewNotification = "show-local-preview-notification", ClickLocalDebug = "click-local-debug", diff --git a/packages/vscode-extension/src/utils/autoOpenHelper.ts b/packages/vscode-extension/src/utils/autoOpenHelper.ts index cedb8e04f2..0984fa0fa5 100644 --- a/packages/vscode-extension/src/utils/autoOpenHelper.ts +++ b/packages/vscode-extension/src/utils/autoOpenHelper.ts @@ -30,6 +30,7 @@ import { getAppName } from "./appDefinitionUtils"; import { getLocalDebugMessageTemplate } from "./commonUtils"; import { localize } from "./localizeUtils"; import { VS_CODE_UI } from "../qm/vsc_ui"; +import { openReadMeHandler } from "../handlers/readmeHandlers"; export async function showLocalDebugMessage() { const shouldShowLocalDebugMessage = (await globalStateGet( @@ -44,13 +45,40 @@ export async function showLocalDebugMessage() { } const hasLocalEnv = await fs.pathExists(path.join(workspaceUri!.fsPath, "teamsapp.local.yml")); + const hasKeyGenJsFile = await fs.pathExists(path.join(workspaceUri!.fsPath, "/src/keyGen.js")); + const hasKeyGenTsFile = await fs.pathExists(path.join(workspaceUri!.fsPath, "/src/keyGen.ts")); const appName = (await getAppName()) ?? localize("teamstoolkit.handlers.fallbackAppName"); const isWindows = process.platform === "win32"; const folderLink = encodeURI(workspaceUri!.toString()); const openFolderCommand = `command:fx-extension.openFolder?%5B%22${folderLink}%22%5D`; - if (hasLocalEnv) { + if (hasKeyGenJsFile || hasKeyGenTsFile) { + const openReadMe = { + title: localize("teamstoolkit.handlers.manualStepRequiredTitle"), + run: async (): Promise => { + await openReadMeHandler([TelemetryTriggerFrom.Notification]); + }, + }; + ExtTelemetry.sendTelemetryEvent(TelemetryEvent.ShowManualStepRequiredNotification); + const message = isWindows + ? util.format( + localize("teamstoolkit.handlers.manualStepRequired"), + appName, + openFolderCommand + ) + : util.format( + localize("teamstoolkit.handlers.manualStepRequired.fallback"), + appName, + workspaceUri?.fsPath + ); + void vscode.window.showInformationMessage(message, openReadMe).then((selection) => { + if (selection?.title === localize("teamstoolkit.handlers.manualStepRequiredTitle")) { + ExtTelemetry.sendTelemetryEvent(TelemetryEvent.ClickReadManualStep); + void selection.run(); + } + }); + } else if (hasLocalEnv) { const localDebug = { title: localize("teamstoolkit.handlers.localDebugTitle"), run: async (): Promise => { diff --git a/packages/vscode-extension/test/utils/autoOpenHelper.test.ts b/packages/vscode-extension/test/utils/autoOpenHelper.test.ts index 5afb4fcf78..b819e5b648 100644 --- a/packages/vscode-extension/test/utils/autoOpenHelper.test.ts +++ b/packages/vscode-extension/test/utils/autoOpenHelper.test.ts @@ -9,6 +9,7 @@ import * as appDefinitionUtils from "../../src/utils/appDefinitionUtils"; import { ok } from "@microsoft/teamsfx-api"; import { ExtTelemetry } from "../../src/telemetry/extTelemetry"; import { showLocalDebugMessage } from "../../src/utils/autoOpenHelper"; +import * as readmeHandlers from "../../src/handlers/readmeHandlers"; describe("autoOpenHelper", () => { const sandbox = sinon.createSandbox(); @@ -21,7 +22,7 @@ describe("autoOpenHelper", () => { sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("win32"); - sandbox.stub(fs, "pathExists").resolves(true); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(true); const runLocalDebug = sandbox.stub(runIconHandlers, "selectAndDebug").resolves(ok(null)); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { @@ -55,7 +56,7 @@ describe("autoOpenHelper", () => { sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("linux"); - sandbox.stub(fs, "pathExists").resolves(true); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(true); const runLocalDebug = sandbox.stub(runIconHandlers, "selectAndDebug").resolves(ok(null)); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { @@ -89,7 +90,7 @@ describe("autoOpenHelper", () => { sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("win32"); - sandbox.stub(fs, "pathExists").resolves(true); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(true); const runLocalDebug = sandbox.stub(runIconHandlers, "selectAndDebug").resolves(ok(null)); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { @@ -120,7 +121,7 @@ describe("autoOpenHelper", () => { sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("win32"); - sandbox.stub(fs, "pathExists").resolves(false); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(false); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { if (key === "ShowLocalDebugMessage") { @@ -155,7 +156,7 @@ describe("autoOpenHelper", () => { sandbox.stub(appDefinitionUtils, "getAppName").resolves(""); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("linux"); - sandbox.stub(fs, "pathExists").resolves(false); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(false); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { if (key === "ShowLocalDebugMessage") { @@ -189,7 +190,7 @@ describe("autoOpenHelper", () => { sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); sandbox.stub(vscode.workspace, "openTextDocument"); sandbox.stub(process, "platform").value("win32"); - sandbox.stub(fs, "pathExists").resolves(false); + sandbox.stub(fs, "pathExists").onFirstCall().resolves(false); sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { if (key === "ShowLocalDebugMessage") { @@ -215,4 +216,216 @@ describe("autoOpenHelper", () => { chai.assert.isTrue(showMessageStub.called); chai.assert.isFalse(executeCommandStub.called); }); + + it("showLocalDebugMessage() - generate an API key manually (TS - windows)", async () => { + sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); + sandbox.stub(vscode.workspace, "openTextDocument"); + sandbox.stub(process, "platform").value("win32"); + sandbox + .stub(fs, "pathExists") + .onFirstCall() + .resolves(true) + .onSecondCall() + .resolves(true) + .onThirdCall() + .resolves(false); + const openReadMeHandlerStub = sandbox + .stub(readmeHandlers, "openReadMeHandler") + .resolves(ok(null)); + + sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { + if (key === "ShowLocalDebugMessage") { + return true; + } else { + return false; + } + }); + sandbox.stub(globalState, "globalStateUpdate"); + sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); + sandbox.stub(globalVariables, "workspaceUri").value(vscode.Uri.file("test")); + const showMessageStub = sandbox + .stub(vscode.window, "showInformationMessage") + .callsFake( + (title: string, options: vscode.MessageOptions, ...items: vscode.MessageItem[]) => { + return Promise.resolve({ + title: "Open README", + run: (options as any).run, + } as vscode.MessageItem); + } + ); + + await showLocalDebugMessage(); + + chai.assert.isTrue(showMessageStub.called); + chai.assert.isTrue(openReadMeHandlerStub.called); + }); + + it("showLocalDebugMessage() - generate an API key manually (TS - windows) not clicked", async () => { + sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); + sandbox.stub(vscode.workspace, "openTextDocument"); + sandbox.stub(process, "platform").value("win32"); + sandbox + .stub(fs, "pathExists") + .onFirstCall() + .resolves(true) + .onSecondCall() + .resolves(true) + .onThirdCall() + .resolves(false); + const openReadMeHandlerStub = sandbox + .stub(readmeHandlers, "openReadMeHandler") + .resolves(ok(null)); + + sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { + if (key === "ShowLocalDebugMessage") { + return true; + } else { + return false; + } + }); + sandbox.stub(globalState, "globalStateUpdate"); + sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); + sandbox.stub(globalVariables, "workspaceUri").value(vscode.Uri.file("test")); + const showMessageStub = sandbox + .stub(vscode.window, "showInformationMessage") + .callsFake( + (title: string, options: vscode.MessageOptions, ...items: vscode.MessageItem[]) => { + return Promise.resolve({ + title: "Not Open README", + run: (options as any).run, + } as vscode.MessageItem); + } + ); + + await showLocalDebugMessage(); + + chai.assert.isTrue(showMessageStub.called); + chai.assert.isFalse(openReadMeHandlerStub.called); + }); + + it("showLocalDebugMessage() - generate an API key manually (TS - windows - non selection)", async () => { + sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); + sandbox.stub(vscode.workspace, "openTextDocument"); + sandbox.stub(process, "platform").value("win32"); + sandbox + .stub(fs, "pathExists") + .onFirstCall() + .resolves(true) + .onSecondCall() + .resolves(true) + .onThirdCall() + .resolves(false); + const openReadMeHandlerStub = sandbox + .stub(readmeHandlers, "openReadMeHandler") + .resolves(ok(null)); + + sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { + if (key === "ShowLocalDebugMessage") { + return true; + } else { + return false; + } + }); + sandbox.stub(globalState, "globalStateUpdate"); + sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); + sandbox.stub(globalVariables, "workspaceUri").value(vscode.Uri.file("test")); + const showMessageStub = sandbox + .stub(vscode.window, "showInformationMessage") + .callsFake( + (title: string, options: vscode.MessageOptions, ...items: vscode.MessageItem[]) => { + return Promise.resolve(undefined); + } + ); + + await showLocalDebugMessage(); + + chai.assert.isTrue(showMessageStub.called); + chai.assert.isFalse(openReadMeHandlerStub.called); + }); + + it("showLocalDebugMessage() - generate an API key manually (JS - windows)", async () => { + sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); + sandbox.stub(vscode.workspace, "openTextDocument"); + sandbox.stub(process, "platform").value("win32"); + sandbox + .stub(fs, "pathExists") + .onFirstCall() + .resolves(true) + .onSecondCall() + .resolves(false) + .onThirdCall() + .resolves(true); + const openReadMeHandlerStub = sandbox + .stub(readmeHandlers, "openReadMeHandler") + .resolves(ok(null)); + + sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { + if (key === "ShowLocalDebugMessage") { + return true; + } else { + return false; + } + }); + sandbox.stub(globalState, "globalStateUpdate"); + sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); + sandbox.stub(globalVariables, "workspaceUri").value(vscode.Uri.file("test")); + const showMessageStub = sandbox + .stub(vscode.window, "showInformationMessage") + .callsFake( + (title: string, options: vscode.MessageOptions, ...items: vscode.MessageItem[]) => { + return Promise.resolve({ + title: "Open README", + run: (options as any).run, + } as vscode.MessageItem); + } + ); + + await showLocalDebugMessage(); + + chai.assert.isTrue(showMessageStub.called); + chai.assert.isTrue(openReadMeHandlerStub.called); + }); + + it("showLocalDebugMessage() - generate an API key manually (JS - non windows)", async () => { + sandbox.stub(vscode.workspace, "workspaceFolders").value([{ uri: vscode.Uri.file("test") }]); + sandbox.stub(vscode.workspace, "openTextDocument"); + sandbox.stub(process, "platform").value("linux"); + sandbox + .stub(fs, "pathExists") + .onFirstCall() + .resolves(true) + .onSecondCall() + .resolves(false) + .onThirdCall() + .resolves(true); + const openReadMeHandlerStub = sandbox + .stub(readmeHandlers, "openReadMeHandler") + .resolves(ok(null)); + + sandbox.stub(globalState, "globalStateGet").callsFake(async (key: string) => { + if (key === "ShowLocalDebugMessage") { + return true; + } else { + return false; + } + }); + sandbox.stub(globalState, "globalStateUpdate"); + sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); + sandbox.stub(globalVariables, "workspaceUri").value(vscode.Uri.file("test")); + const showMessageStub = sandbox + .stub(vscode.window, "showInformationMessage") + .callsFake( + (title: string, options: vscode.MessageOptions, ...items: vscode.MessageItem[]) => { + return Promise.resolve({ + title: "Open README", + run: (options as any).run, + } as vscode.MessageItem); + } + ); + + await showLocalDebugMessage(); + + chai.assert.isTrue(showMessageStub.called); + chai.assert.isTrue(openReadMeHandlerStub.called); + }); });