From 49081a1151aac82768ca69083fff4f4bbaab8bd8 Mon Sep 17 00:00:00 2001 From: Stanislav Lvovsky Date: Wed, 15 Jan 2020 10:57:11 +0200 Subject: [PATCH] catch question exception (#100) * catch question exception * Update QuestionTypeSelector.vue * remove bootstrap * add test * fix solution * write error to log * toggle log * version update --- backend/package.json | 13 +-- backend/src/yeomanui.ts | 5 + backend/tests/yeomanui.spec.ts | 7 ++ frontend/package.json | 4 +- frontend/src/App.vue | 100 ++++++++++-------- .../src/components/QuestionTypeSelector.vue | 2 +- .../QuestionTypes/QuestionInput.vue | 5 - frontend/tests/App.spec.js | 21 +++- frontend/tests/Utils.js | 2 - .../QuestionTypes/GeneratorSelection.spec.js | 18 ---- 10 files changed, 93 insertions(+), 84 deletions(-) diff --git a/backend/package.json b/backend/package.json index 9c0cdfe2..a4313507 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "license": "Apache 2.0", "description": "Provide rich user experience for Yeoman generators using VSCode extension or the browser", "repository": "https://github.com/SAP/yeoman-ui", - "version": "0.0.22", + "version": "0.0.23", "engines": { "vscode": "^1.38.0" }, @@ -50,11 +50,12 @@ "yeoman-environment": "^2.7.0", "titleize": "^1.0.1", "humanize-string": "^1.0.2", - "fs-extra": "^8.1.0" + "fs-extra": "^8.1.0", + "chalk": "^3.0.0" }, "devDependencies": { - "@types/fs-extra": "^8.0.1", "@types/chai": "^4.2.5", + "@types/fs-extra": "^8.0.1", "@types/inquirer": "^6.5.0", "@types/lodash": "^4.14.145", "@types/mocha": "^5.2.7", @@ -63,6 +64,7 @@ "@types/strip-ansi": "^5.2.1", "@types/ws": "^6.0.3", "@types/yeoman-environment": "^2.3.2", + "bufferutil": "^4.0.1", "chai": "^4.2.0", "copy-webpack-plugin": "^5.0.5", "mocha": "^6.2.2", @@ -74,11 +76,10 @@ "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", "typescript": "^3.7.3", + "utf-8-validate": "^5.0.2", "vsce": "^1.68.0", "vscode": "^1.1.28", "webpack": "^4.41.2", - "webpack-cli": "^3.3.10", - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "webpack-cli": "^3.3.10" } } diff --git a/backend/src/yeomanui.ts b/backend/src/yeomanui.ts index 7a5e6fda..233f74e9 100644 --- a/backend/src/yeomanui.ts +++ b/backend/src/yeomanui.ts @@ -67,6 +67,7 @@ export class YeomanUI { this.rpc.registerMethod({ func: this.runGenerator, thisArg: this }); this.rpc.registerMethod({ func: this.evaluateMethod, thisArg: this }); this.rpc.registerMethod({ func: this.toggleLog, thisArg: this }); + this.rpc.registerMethod({ func: this.logMessage, thisArg: this }); this.youiAdapter = new YouiAdapter(logger); this.youiAdapter.setYeomanUI(this); this.promptCount = 0; @@ -209,6 +210,10 @@ export class YeomanUI { return this.logger.showLog(); } + public logMessage(message: string): void { + this.logger.log(message); + } + public async showPrompt(questions: Environment.Adapter.Questions): Promise { this.currentQuestions = questions; diff --git a/backend/tests/yeomanui.spec.ts b/backend/tests/yeomanui.spec.ts index f5a1d72b..59ebb724 100644 --- a/backend/tests/yeomanui.spec.ts +++ b/backend/tests/yeomanui.spec.ts @@ -409,6 +409,13 @@ describe('yeomanui unit test', () => { expect(res).to.be.false; }); + it("logMessage", () => { + const yeomanUi: YeomanUI = new YeomanUI(rpc, logger); + const res = yeomanUi.logMessage("message"); + // tslint:disable-next-line: no-unused-expression + expect(res).to.be.undefined; + }); + describe("getEnv", () => { const yeomanUi: YeomanUI = new YeomanUI(rpc, logger); const testEnv = yeomanUi["getEnv"](); diff --git a/frontend/package.json b/frontend/package.json index 4b745bc2..5122308a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "yeoman-ui-frontend", "displayName": "Yeoman UI Frontend", - "version": "0.1.9", + "version": "0.2.3", "publisher": "SAP", "license": "Apache 2.0", "description": "Frontend for the yeoman UI framework", @@ -15,8 +15,6 @@ "precommit": "lint-staged" }, "dependencies": { - "bootstrap": "^4.3.1", - "bootstrap-vue": "^2.0.2", "core-js": "2.6.6", "jquery": "3.4.1", "vue-loading-overlay": "^3.2.0", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 0469d183..aeb4c9d5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -187,57 +187,63 @@ export default { }, async updateQuestion(question) { const newAnswers = this.currentPrompt.answers - if (question.when === FUNCTION) { - question.isWhen = await this.rpc.invoke("evaluateMethod", [ - [newAnswers], - question.name, - "when" - ]); - } - - if (question.isWhen === true) { - if (question.filter === FUNCTION) { - question.answer = await this.rpc.invoke("evaluateMethod", [ - [question.answer], - question.name, - "filter" - ]); - } - if (question._default === FUNCTION) { - question.default = await this.rpc.invoke("evaluateMethod", [ - [newAnswers], - question.name, - "default" - ]); - if (question.answer === undefined) { - question.answer = question.default; - } - } - if (question._message === FUNCTION) { - question.message = await this.rpc.invoke("evaluateMethod", [ - [newAnswers], - question.name, - "message" - ]); - } - if (question._choices === FUNCTION) { - question.choices = await this.rpc.invoke("evaluateMethod", [ + try { + if (question.when === FUNCTION) { + question.isWhen = await this.rpc.invoke("evaluateMethod", [ [newAnswers], question.name, - "choices" + "when" ]); } - if (question.validate === FUNCTION) { - const response = await this.rpc.invoke("evaluateMethod", [ - [question.answer, newAnswers], - question.name, - "validate" - ]); - question.isValid = _.isString(response) ? false : response; - question.validationMessage = _.isString(response) - ? response - : undefined; + + if (question.isWhen === true) { + if (question.filter === FUNCTION) { + question.answer = await this.rpc.invoke("evaluateMethod", [ + [question.answer], + question.name, + "filter" + ]); + } + if (question._default === FUNCTION) { + question.default = await this.rpc.invoke("evaluateMethod", [ + [newAnswers], + question.name, + "default" + ]); + if (question.answer === undefined) { + question.answer = question.default; + } + } + if (question._message === FUNCTION) { + question.message = await this.rpc.invoke("evaluateMethod", [ + [newAnswers], + question.name, + "message" + ]); + } + if (question._choices === FUNCTION) { + question.choices = await this.rpc.invoke("evaluateMethod", [ + [newAnswers], + question.name, + "choices" + ]); + } + if (question.validate === FUNCTION) { + const response = await this.rpc.invoke("evaluateMethod", [ + [question.answer, newAnswers], + question.name, + "validate" + ]); + question.isValid = _.isString(response) ? false : response; + question.validationMessage = _.isString(response) + ? response + : undefined; + } } + } catch (error) { + const message = `'${question.name}' question update of generator ${this.generatorName} has failed: ${_.get(error, "message", error)}`; + await this.rpc.invoke("logMessage", [message]); + this.rpc.invoke("toggleLog", [{}]); } }, next() { @@ -330,6 +336,7 @@ export default { } this.$set(question, "answer", answer); this.$set(question, "isValid", true); + this.$set(question, "doNotShow", false); this.$set(question, "validationMessage", true); this.$set(question, "isWhen", question.when !== FUNCTION); @@ -485,4 +492,3 @@ div.bottom-right-col .progress-buttons-row { padding-top: 4px; } - diff --git a/frontend/src/components/QuestionTypeSelector.vue b/frontend/src/components/QuestionTypeSelector.vue index e3d41218..7272e6b2 100644 --- a/frontend/src/components/QuestionTypeSelector.vue +++ b/frontend/src/components/QuestionTypeSelector.vue @@ -75,4 +75,4 @@ export default { selectGenerator: Function } }; - \ No newline at end of file + diff --git a/frontend/src/components/QuestionTypes/QuestionInput.vue b/frontend/src/components/QuestionTypes/QuestionInput.vue index cbfdb450..daa03528 100644 --- a/frontend/src/components/QuestionTypes/QuestionInput.vue +++ b/frontend/src/components/QuestionTypes/QuestionInput.vue @@ -32,11 +32,6 @@ export default { }, computed: { type() { - // mapping between - // https://www.npmjs.com/package/inquirer#question - // and - // https://bootstrap-vue.js.org/docs/components/form-input/ - // const questionType = _.get(this.currentQuestion, "type") const type = this.currentQuestion.type; return type === "input" ? "text" : type; } diff --git a/frontend/tests/App.spec.js b/frontend/tests/App.spec.js index 31311963..7a7805f8 100644 --- a/frontend/tests/App.spec.js +++ b/frontend/tests/App.spec.js @@ -1,11 +1,9 @@ import {initComponent, destroy} from './Utils' import App from '../src/App.vue'; import Vue from 'vue' -import BootstrapVue from 'bootstrap-vue' import Vuetify from 'vuetify' import { WebSocket } from 'mock-socket' -Vue.use(BootstrapVue) Vue.use(Vuetify) global.WebSocket = WebSocket @@ -148,6 +146,25 @@ describe('App.vue', () => { expect(wrapper.vm.prompts[0].questions[0].isValid).toBe(true) expect(wrapper.vm.prompts[0].questions[0].validationMessage ).toBeUndefined() }) + + test('invoke for question that throws error', async () => { + wrapper.vm.rpc = { + invoke: jest.fn().mockRejectedValueOnce("test error").mockResolvedValue() + } + wrapper.vm.prompts = [{ + questions: [{ + name: 'validateQ', validate: '__Function', answer: 'validateAnswer', isWhen: true, doNotShow: false + }], + answers: {} + }] + wrapper.vm.promptIndex = 0 + + const invokeSpy = jest.spyOn(wrapper.vm.rpc, 'invoke') + await wrapper.vm.updateQuestionsFromIndex(0) + expect(invokeSpy).toHaveBeenCalledWith('evaluateMethod', [["validateAnswer", {"validateQ": "validateAnswer"}], 'validateQ', 'validate']) + expect(invokeSpy).toHaveBeenCalledWith('logMessage', ["'validateQ' question update of generator has failed: test error"]) + expect(invokeSpy).toHaveBeenCalledWith('toggleLog', [{}]) + }) }) describe('setQuestionProps - method', () => { diff --git a/frontend/tests/Utils.js b/frontend/tests/Utils.js index 00307a21..072cd5fe 100644 --- a/frontend/tests/Utils.js +++ b/frontend/tests/Utils.js @@ -1,9 +1,7 @@ import { mount, shallowMount } from '@vue/test-utils' import Vue from 'vue' -import BootstrapVue from 'bootstrap-vue' import Vuetify from 'vuetify' Vue.use(Vuetify); -Vue.use(BootstrapVue) import { createLocalVue diff --git a/frontend/tests/components/QuestionTypes/GeneratorSelection.spec.js b/frontend/tests/components/QuestionTypes/GeneratorSelection.spec.js index 210e516f..f240d287 100644 --- a/frontend/tests/components/QuestionTypes/GeneratorSelection.spec.js +++ b/frontend/tests/components/QuestionTypes/GeneratorSelection.spec.js @@ -32,24 +32,6 @@ describe('GeneratorSelection.vue', () => { expect(wrapper.vm.getImageUrl({})).toBe("testPublicPath-generator.png") }) }) - - describe.skip('emitSelection - method', () => { - beforeEach(() => { - wrapper = initComponent(GeneratorSelection, { - currentQuestion: { - choices: [{imageUrl: 'testImageUrl', name: 'testName'}] - }, - selectGenerator: jest.fn() - }, true) - }) - - test('on click event of vCard', async () => { - wrapper.find('.v-card').trigger('click') - await wrapper.vm.$nextTick() - expect(wrapper.emitted('generatorSelected')).toBeTruthy() - expect(wrapper.vm.$options.propsData.currentQuestion.answer).toBe('testName') - }) - }) describe('select - method', () => { beforeEach(() => {