From 1a585e64e9ce9b29879e369d3390a775f10bdaee Mon Sep 17 00:00:00 2001 From: Ira Gordin Date: Wed, 7 Oct 2020 15:24:04 +0300 Subject: [PATCH] When 'Open in new workspace' and 'Add to workspace' should be shown. (#379) --- backend/package.json | 2 +- backend/src/vscode-youi-events.ts | 10 +++-- backend/src/yeomanui.ts | 47 ++++++++++++++++++------ backend/tests/vscode-youi-events.spec.ts | 20 +++++----- backend/tests/yeomanui.spec.ts | 24 ++++++++++-- 5 files changed, 72 insertions(+), 31 deletions(-) diff --git a/backend/package.json b/backend/package.json index b57497247..9421d044e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "yeoman-ui", - "version": "1.1.28", + "version": "1.1.29", "displayName": "Application Wizard", "publisher": "SAPOS", "author": { diff --git a/backend/src/vscode-youi-events.ts b/backend/src/vscode-youi-events.ts index 890bd12b9..e459dd20f 100644 --- a/backend/src/vscode-youi-events.ts +++ b/backend/src/vscode-youi-events.ts @@ -114,10 +114,14 @@ export class VSCodeYouiEvents implements YouiEvents { const addToWorkspace = "Add to Workspace"; const openInNewWorkspace: any = "Open in New Workspace"; const items: string[] = []; - - const targetFolderUri: vscode.Uri = vscode.Uri.file(targetFolderPath); + let targetFolderUri: vscode.Uri = null; - if (!_.includes(this.genFilter.types, GeneratorType.module)) { + // The correct targetFolderPath is unknown ---> no buttons should be shown + if (!_.isNil(targetFolderPath)) { + // Target folder is visible in workpace ---> addToWorkspace only + // Target folder is not visible in workpace ---> addToWorkspace and openInNewWorkspace + + targetFolderUri = vscode.Uri.file(targetFolderPath); const workspacePath = _.get(vscode, "workspace.workspaceFolders[0].uri.fsPath"); // 1. target workspace folder should not already contain target generator folder const foundInWorkspace = _.find(vscode.workspace.workspaceFolders, (wsFolder: vscode.WorkspaceFolder) => { diff --git a/backend/src/yeomanui.ts b/backend/src/yeomanui.ts index e38b56673..c9a6ce51d 100644 --- a/backend/src/yeomanui.ts +++ b/backend/src/yeomanui.ts @@ -19,6 +19,7 @@ import {IPrompt} from "@sap-devx/yeoman-ui-types"; import { SWA } from "./swa-tracker/swa-tracker-wrapper"; import TerminalAdapter = require("yeoman-environment/lib/adapter"); import { Output } from "./output"; +import { resolve } from "path"; export interface IQuestionsPrompt extends IPrompt{ @@ -54,6 +55,7 @@ export class YeomanUI { private readonly replayUtils: ReplayUtils; private readonly customQuestionEventHandlers: Map>; private errorThrown = false; + private outputPath: string; constructor(rpc: IRpc, youiEvents: YouiEvents, output: Output, logger: IChildLogger, uiOptions: any, outputPath: string = YeomanUI.PROJECTS) { this.rpc = rpc; @@ -62,7 +64,8 @@ export class YeomanUI { this.replayUtils = new ReplayUtils(); this.youiEvents = youiEvents; this.logger = logger; - this.output = output; + this.output = output; + this.outputPath = outputPath; this.rpc.setResponseTimeout(3600000); this.rpc.registerMethod({ func: this.receiveIsWebviewReady, thisArg: this }); this.rpc.registerMethod({ func: this.runGenerator, thisArg: this }); @@ -209,7 +212,9 @@ export class YeomanUI { // (for example: 2.0.5) of "yeoman-generator" do not support it this.gen.run(error => {; if (!this.errorThrown && !error) { - this.getChildDirectories(this.gen.destinationRoot()).then(dirsAfter => { + // Without resolve this code worked only for absolute paths without / at the end. + // Generator can put a relative path, path including . and .. and / at the end. + this.getChildDirectories(resolve(this.getCwd(), this.gen.destinationRoot())).then(dirsAfter => { this.onGeneratorSuccess(generatorName, dirsBefore, dirsAfter); }); } @@ -351,18 +356,32 @@ export class YeomanUI { return (firstQuestionName ? _.startCase(firstQuestionName) : `Step ${this.promptCount}`); } - private onGeneratorSuccess(generatorName: string, reourcesBeforeGen?: any, resourcesAfterGen?: any) { - let targetFolderPath: string = _.get(resourcesAfterGen, "targetFolderPath"); - if (_.get(reourcesBeforeGen, "targetFolderPath") === targetFolderPath) { - const newDirs: string[] = _.difference(_.get(resourcesAfterGen, "childDirs"), _.get(reourcesBeforeGen, "childDirs")); + private onGeneratorSuccess(generatorName: string, resourcesBeforeGen?: any, resourcesAfterGen?: any) { + let targetFolderPath: string = null; + // All the paths here absolute normilized paths. + const targetFolderPathBeforeGen: string = _.get(resourcesBeforeGen, "targetFolderPath"); + const targetFolderPathAfterGen: string = _.get(resourcesAfterGen, "targetFolderPath"); + if (targetFolderPathBeforeGen === targetFolderPathAfterGen) { + const newDirs: string[] = _.difference(_.get(resourcesAfterGen, "childDirs"), _.get(resourcesBeforeGen, "childDirs")); if (_.size(newDirs) === 1) { - targetFolderPath = newDirs[0]; - } - } + // One folder added by generator and targetFolderPath/destinationRoot was not changed by generator. + // ---> Fiori project generator flow. + targetFolderPath = newDirs[0]; + } //else { //_.size(newDirs) = 0 (0 folders) or _.size(newDirs) > 1 (5 folders) + // We don't know what is the correct targetFolderPath ---> no buttons should be shown. + // No folder added by generator ---> Fiori module generator flow. + // Many folders added by generator ---> + // } + } else { //(targetFolderPathBeforeGen !== targetFolderPathAfterGen) + // Generator changed targetFolderPath/destinationRoot. + // ---> FoodQ generator flow. + targetFolderPath = targetFolderPathAfterGen; + } const message = this.uiOptions.messages.artifact_with_name_generated(generatorName); - this.logger.debug("done running yeomanui! " + message + ` You can find it at ${targetFolderPath}`); - SWA.updateGeneratorEnded(this.generatorName, true, this.logger); + const generatedTemplatePath = targetFolderPath ? targetFolderPath : targetFolderPathBeforeGen; + this.logger.debug("done running yeomanui! " + message + ` You can find it at ${generatedTemplatePath}`); + SWA.updateGeneratorEnded(generatorName, true, this.logger); this.youiEvents.doGeneratorDone(true, message, targetFolderPath); } @@ -370,7 +389,7 @@ export class YeomanUI { this.errorThrown = true; const messagePrefix = `${generatorName} generator failed`; const errorMessage: string = await this.logError(error, messagePrefix); - SWA.updateGeneratorEnded(this.generatorName, false, this.logger, errorMessage); + SWA.updateGeneratorEnded(generatorName, false, this.logger, errorMessage); this.youiEvents.doGeneratorDone(false, errorMessage); } @@ -427,6 +446,10 @@ export class YeomanUI { getPath: async (path: string) => path, validate: async (path: string) => { try { + // Without resolve this code worked only for absolute paths without / at the end. + // The user can put a relative path, path including . and .. and / at the end. + // In this case many project generation failed or opened invalid folders instead of project at the end (after clicking on the button 'Open project in workspace'). + path = resolve(this.outputPath, path); await fsextra.access(path, fsextra.constants.W_OK); this.setCwd(path); return true; diff --git a/backend/tests/vscode-youi-events.spec.ts b/backend/tests/vscode-youi-events.spec.ts index 7166cba5b..9e73c1c4c 100644 --- a/backend/tests/vscode-youi-events.spec.ts +++ b/backend/tests/vscode-youi-events.spec.ts @@ -206,7 +206,7 @@ describe('vscode-youi-events unit test', () => { return events.doGeneratorDone(true, "success message", "testDestinationRoot"); }); - it("on success, no buttons are displayed", () => { + it("on success with the project already opened in the workspace, no buttons are displayed", () => { eventsMock.expects("doClose"); _.set(vscode, "workspace.workspaceFolders", [{uri: {fsPath: "testDestinationRoot"}}]); windowMock.expects("showInformationMessage"). @@ -214,20 +214,18 @@ describe('vscode-youi-events unit test', () => { return events.doGeneratorDone(true, "success message", "testDestinationRoot"); }); - it("on failure", () => { + it("on success with null targetFolderPath, no buttons are displayed", () => { eventsMock.expects("doClose"); - windowMock.expects("showErrorMessage").withExactArgs("error message"); - return events.doGeneratorDone(false, "error message"); + _.set(vscode, "workspace.workspaceFolders", [{uri: {fsPath: "rootFolderPath"}}]); + windowMock.expects("showInformationMessage"). + withExactArgs(messages.default.artifact_generated).resolves(); + return events.doGeneratorDone(true, "success message", null); }); - it("generator filter type is module", () => { - const genFilter = GeneratorFilter.create({type: ["module"]}); - loggerWrapperMock.expects("getClassLogger"); - const testEvents = new VSCodeYouiEvents(undefined, undefined, genFilter, messages.default, undefined); - eventsMock = sandbox.mock(testEvents); + it("on failure", () => { eventsMock.expects("doClose"); - windowMock.expects("showInformationMessage").withExactArgs(messages.default.artifact_generated).resolves(); - return testEvents.doGeneratorDone(true, "success message", "testDestinationRoot"); + windowMock.expects("showErrorMessage").withExactArgs("error message"); + return events.doGeneratorDone(false, "error message"); }); }); }); diff --git a/backend/tests/yeomanui.spec.ts b/backend/tests/yeomanui.spec.ts index 8e9639d86..ed85f2230 100644 --- a/backend/tests/yeomanui.spec.ts +++ b/backend/tests/yeomanui.spec.ts @@ -669,7 +669,7 @@ describe('yeomanui unit test', () => { it("onGeneratorSuccess - one dir was created", () => { const beforeGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1"]}; const afterGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1", "dirpath2"]}; - swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenerator", true); + swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenName", true, testLogger); yeomanUi["onGeneratorSuccess"]("testGenName", beforeGen, afterGen); expect(doGeneratorDoneSpy.calledWith(true, _.get(yeomanUi, "uiOptions.messages.artifact_with_name_generated")("testGenName"), "dirpath2")).to.be.true; }); @@ -677,13 +677,29 @@ describe('yeomanui unit test', () => { it("onGeneratorSuccess - two dirs were created", () => { const beforeGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1"]}; const afterGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1", "dirpath2", "dirpath3"]}; - swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenerator", true); + swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenName", true, testLogger); yeomanUi["onGeneratorSuccess"]("testGenName", beforeGen, afterGen); - expect(doGeneratorDoneSpy.calledWith(true, _.get(yeomanUi, "uiOptions.messages.artifact_with_name_generated")("testGenName"), "testDestinationRoot")).to.be.true; + expect(doGeneratorDoneSpy.calledWith(true, _.get(yeomanUi, "uiOptions.messages.artifact_with_name_generated")("testGenName"), null)).to.be.true; + }); + + it("onGeneratorSuccess - zero dirs were created", () => { + const beforeGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1"]}; + const afterGen = {targetFolderPath: "testDestinationRoot", childDirs: ["dirparh1"]}; + swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenName", true, testLogger); + yeomanUi["onGeneratorSuccess"]("testGenName", beforeGen, afterGen); + expect(doGeneratorDoneSpy.calledWith(true, _.get(yeomanUi, "uiOptions.messages.artifact_with_name_generated")("testGenName"), null)).to.be.true; + }); + + it("onGeneratorSuccess - targetFolderPath was changed by generator", () => { + const beforeGen = {targetFolderPath: "testDestinationRoot"}; + const afterGen = {targetFolderPath: "testDestinationRoot/generatedProject"}; + swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenName", true, testLogger); + yeomanUi["onGeneratorSuccess"]("testGenName", beforeGen, afterGen); + expect(doGeneratorDoneSpy.calledWith(true, _.get(yeomanUi, "uiOptions.messages.artifact_with_name_generated")("testGenName"), "testDestinationRoot/generatedProject")).to.be.true; }); it("onGeneratorFailure", async () => { - swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenerator", false); + swaTrackerWrapperMock.expects("updateGeneratorEnded").withArgs("testGenName", false, testLogger); await yeomanUi["onGeneratorFailure"]("testGenName", "testError"); expect(doGeneratorDoneSpy.calledWith(false, `{"message":"testGenName generator failed - testError"}`)).to.be.true; });