diff --git a/package-lock.json b/package-lock.json index 0a6ba538..2f74827e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@types/chai": "^4.3.10", + "@types/fs-extra": "^11.0.4", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.4", "@types/node": "14.x", @@ -382,6 +383,16 @@ "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==", "dev": true }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "node_modules/@types/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", @@ -404,6 +415,15 @@ "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -5558,6 +5578,16 @@ "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==", "dev": true }, + "@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "requires": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "@types/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", @@ -5580,6 +5610,15 @@ "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, + "@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", diff --git a/package.json b/package.json index 9789a0df..cedbdbaa 100644 --- a/package.json +++ b/package.json @@ -248,6 +248,7 @@ }, "devDependencies": { "@types/chai": "^4.3.10", + "@types/fs-extra": "^11.0.4", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.4", "@types/node": "14.x", diff --git a/src/ui-test/resources/tasks-examples/01_launch_camel_debug_profile.json b/src/ui-test/resources/tasks-examples/01_launch_camel_debug_profile.json new file mode 100644 index 00000000..cd67ccbc --- /dev/null +++ b/src/ui-test/resources/tasks-examples/01_launch_camel_debug_profile.json @@ -0,0 +1,27 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Launch Camel test with Maven with camel.debug profile", + "type": "shell", + "command": "mvn", // mvn binary of Maven must be available on command-line + "args": [ + "test", + "-Dtest=*", // If more than one test is present, a specific one must be specified as a single test can be Camel debugged per launch. + "-Pcamel.debug" // This depends on your project. The goal here is to have camel-debug on the classpath. + ], + "options": { + "env": { + "CAMEL_DEBUGGER_SUSPEND": "true" // Set to true by default. A debugger must be attached for message to be processed. + } + }, + "problemMatcher": "$camel.debug.problemMatcher", + "presentation": { + "reveal": "always" + }, + "isBackground": true // Must be set as background as the Maven commands doesn't return until the Camel application stops. + } + ] +} diff --git a/src/ui-test/resources/tasks-examples/02_run_jbang_w_camel_debug.json b/src/ui-test/resources/tasks-examples/02_run_jbang_w_camel_debug.json new file mode 100644 index 00000000..c463d389 --- /dev/null +++ b/src/ui-test/resources/tasks-examples/02_run_jbang_w_camel_debug.json @@ -0,0 +1,27 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run Camel application with JBang with camel-debug", + "type": "shell", + "command": "jbang", // jbang binary must be available on command-line + "args": [ + "-Dorg.apache.camel.debugger.suspend=true", // requires Camel 3.18+ to take effect + "-Dcamel.jbang.version=4.0.0", // to adapt to your Camel version. 3.16+ is required + "camel@apache/camel", + "run", + "${relativeFile}", //to adapt to your workspace, using relativeFile means that the task must be launched when the file to start in debug in the active editor + "--logging-level=info", + "--reload", + "--dep=org.apache.camel:camel-debug" + ], + "problemMatcher": "$camel.debug.problemMatcher", + "presentation": { + "reveal": "always" + }, + "isBackground": true // Must be set as background as the jbang command doesn't return until the Camel application stops. + } + ] +} diff --git a/src/ui-test/resources/tasks-examples/03_start_mvn_camel_debug_goal.json b/src/ui-test/resources/tasks-examples/03_start_mvn_camel_debug_goal.json new file mode 100644 index 00000000..35b3b7a1 --- /dev/null +++ b/src/ui-test/resources/tasks-examples/03_start_mvn_camel_debug_goal.json @@ -0,0 +1,25 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Camel application with camel:debug Maven goal", + "type": "shell", + "command": "mvn", // mvn binary of Maven must be available on command-line + "args": [ + "camel:debug" + ], + "options": { + "env": { + "CAMEL_DEBUGGER_SUSPEND": "true" // Set to true by default. A debugger must be attached for message to be processed. + } + }, + "problemMatcher": "$camel.debug.problemMatcher", + "presentation": { + "reveal": "always" + }, + "isBackground": true // Must be set as background as the Maven commands doesn't return until the Camel application stops. + } + ] +} diff --git a/src/ui-test/resources/tasks-examples/04_start_mvn_quarkus_dev_debug_profile.json b/src/ui-test/resources/tasks-examples/04_start_mvn_quarkus_dev_debug_profile.json new file mode 100644 index 00000000..b2eea104 --- /dev/null +++ b/src/ui-test/resources/tasks-examples/04_start_mvn_quarkus_dev_debug_profile.json @@ -0,0 +1,22 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Camel application with Maven Quarkus Dev with camel.debug profile", + "type": "shell", + "command": "mvn", // mvn binary of Maven must be available on command-line + "args": [ + "compile", + "quarkus:dev", + "-Pcamel.debug" // This depends on your project. The goal here is to have camel-debug on the classpath. + ], + "problemMatcher": "$camel.debug.problemMatcher", + "presentation": { + "reveal": "always" + }, + "isBackground": true // Must be set as background as the Maven commands doesn't return until the Camel application stops. + } + ] +} diff --git a/src/ui-test/resources/tasks-examples/05_start_mvn_camel_debug_profile.json b/src/ui-test/resources/tasks-examples/05_start_mvn_camel_debug_profile.json new file mode 100644 index 00000000..6ed0819a --- /dev/null +++ b/src/ui-test/resources/tasks-examples/05_start_mvn_camel_debug_profile.json @@ -0,0 +1,21 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Camel application with camel.debug profile", + "type": "shell", + "command": "mvn", // mvn binary of Maven must be available on command-line + "args": [ + "camel:run", + "-Pcamel.debug" // This depends on your project. The goal here is to have camel-debug on the classpath. + ], + "problemMatcher": "$camel.debug.problemMatcher", + "presentation": { + "reveal": "always" + }, + "isBackground": true // Must be set as background as the Maven commands doesn't return until the Camel application stops. + } + ] +} diff --git a/src/ui-test/resources/tasks.json b/src/ui-test/resources/tasks.json new file mode 100644 index 00000000..7bf744dc --- /dev/null +++ b/src/ui-test/resources/tasks.json @@ -0,0 +1,8 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + + ] +} diff --git a/src/ui-test/tests/tasks.json.autocompletion.test.ts b/src/ui-test/tests/tasks.json.autocompletion.test.ts new file mode 100644 index 00000000..5ec5d4dc --- /dev/null +++ b/src/ui-test/tests/tasks.json.autocompletion.test.ts @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License", destination); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from "chai"; +import { activateEditor, closeEditor, getFileContent, openFileInEditor, selectFromCA } from "../utils"; +import { ActivityBar, TextEditor, VSBrowser, WebDriver } from "vscode-uitests-tooling"; +import * as path from "path"; +import { RESOURCES_DIR, RESOURCES_TASK_EXAMPLES_DIR, TASKS_TEST_FILE } from "../variables"; + +describe('Completion inside tasks.json', function () { + this.timeout(120000); + + let driver: WebDriver; + let textEditor: TextEditor | null; + + before(async function () { + driver = VSBrowser.instance.driver; + await VSBrowser.instance.openResources(path.resolve('src', 'ui-test', 'resources')); + await (await new ActivityBar().getViewControl('Explorer')).openView(); + }); + + const PARAMS = [ + // command, filename + ['Launch Camel test with Maven with camel.debug profile', '01_launch_camel_debug_profile.json'], + ['Run Camel application with JBang with camel-debug', '02_run_jbang_w_camel_debug.json'], + ['Start Camel application with camel:debug Maven goal', '03_start_mvn_camel_debug_goal.json'], + ['Start Camel application with Maven Quarkus Dev with camel.debug profile', '04_start_mvn_quarkus_dev_debug_profile.json'], + ['Start Camel application with Maven with camel.debug profile', '05_start_mvn_camel_debug_profile.json'] + ]; + + PARAMS.forEach(function (params) { + const command = params.at(0) as string; + const file = params.at(1) as string; + + it(`${command}`, async function () { + await openFileInEditor(driver, RESOURCES_DIR, TASKS_TEST_FILE); + textEditor = await activateEditor(driver, TASKS_TEST_FILE); + // workaround for https://github.com/redhat-developer/vscode-extension-tester/issues/931 + await textEditor?.setTextAtLine(6, " "); + await textEditor?.moveCursor(6, 9); + await selectFromCA(command); + const text = await textEditor?.getText(); + expect(text).equals(getFileContent(file, RESOURCES_TASK_EXAMPLES_DIR)); + await closeEditor(TASKS_TEST_FILE, false); + }); + }); +}); diff --git a/src/ui-test/utils.ts b/src/ui-test/utils.ts index 0e46db49..7536fb6a 100644 --- a/src/ui-test/utils.ts +++ b/src/ui-test/utils.ts @@ -3,14 +3,19 @@ import { BottomBarPanel, BreakpointSectionItem, CodeLens, + ContentAssist, + ContentAssistItem, ContextMenu, ContextMenuItem, DebugToolbar, DebugView, + EditorView, InputBox, + ModalDialog, SideBarView, TerminalView, TextEditor, + VSBrowser, VariableSectionItem, ViewItem, WebDriver, @@ -20,6 +25,8 @@ import { repeat, until } from 'vscode-uitests-tooling'; +import * as path from 'path'; +import * as fs from 'fs-extra'; export const DEFAULT_HEADER = 'YamlHeader'; export const DEFAULT_PROPERTY = 'yaml-dsl'; @@ -249,3 +256,127 @@ export async function findCodelens(title: string): Promise { message: `could not find codelens: ${title}` }); } + +/** +* Switch to an editor tab with the given title. +* +* @param title Title of editor to activate +*/ +export async function activateEditor(driver: WebDriver, title: string): Promise { + // workaround for https://issues.redhat.com/browse/FUSETOOLS2-2099 + let editor: TextEditor | null = null; + await driver.wait(async function () { + try { + editor = await new EditorView().openEditor(title) as TextEditor; + return true; + } catch (err) { + await driver.actions().click().perform(); + return false; + } + }, 10000, undefined, 500); + return editor; +} + +/** + * Wait until content assist contains specific item. + * + * @param expectedContentAssistItem Expected item. + * @param timeout Timeout for waiting. + * @returns Item from Content Assist. + */ +export async function waitUntilContentAssistContains(expectedContentAssistItem: string, timeout = 10000): Promise { + const editor = new TextEditor(); + let contentAssist: ContentAssist | null = null; + + await editor.getDriver().wait(async function () { + contentAssist = await editor.toggleContentAssist(true) as ContentAssist; + const hasItem = await contentAssist.hasItem(expectedContentAssistItem); + if (!hasItem) { + await editor.toggleContentAssist(false); + } + return hasItem; + }, timeout); + + return contentAssist; +} + +/** + * Workaround for issue with ContentAssistItem getText() method. + * For more details please see https://issues.redhat.com/browse/FUSETOOLS2-284 + * + * @param item ContenAssistItem + */ +export async function getTextExt(item: ContentAssistItem): Promise { + const name: string = await item.getText(); + return name.split('\n')[0]; +} + +/** + * Close editor with handling of Save/Don't Save Modal dialog. + * + * @param title Title of opened active editor. + * @param save true/false + */ +export async function closeEditor(title: string, save?: boolean) { + const dirty = await new TextEditor().isDirty(); + await new EditorView().closeEditor(title); + if (dirty) { + const dialog = new ModalDialog(); + if (save) { + await dialog.pushButton('Save'); + } else { + await dialog.pushButton('Don\'t Save'); + } + } +} + +/** + * Get content of specific file. + * + * @param filename Name of file. + * @param folder Folder with file. + * @returns File content as string. + */ +export function getFileContent(filename: string, folder: string): string { + return fs.readFileSync(path.resolve(folder, filename), { encoding: 'utf8', flag: 'r' }); +} + +/** +* Select specific item from Content Assist proposals. +* +* @param expectedItem Expected item in Content Assist. +*/ +export async function selectFromCA(expectedItem: string, timeout = 15000): Promise { + let contentAssist: ContentAssist | null = null; + contentAssist = await waitUntilContentAssistContains(expectedItem, timeout); + if (contentAssist !== null) { + const item = await contentAssist.getItem(expectedItem); + await item.click(); + } +} + +/** Opens file in editor. + * + * @param driver WebDriver. + * @param folder Folder with file. + * @param file Filename. + * @returns Instance of Text Editor. + */ +export async function openFileInEditor(driver: WebDriver, folder: string, file: string): Promise { + await VSBrowser.instance.openResources(path.join(folder, file)); + await waitUntilEditorIsOpened(driver, file); + return (await activateEditor(driver, file)); +} + +/** + * Wait until editor is opened. + * + * @param driver WebDriver. + * @param title Title of editor - filename. + * @param timeout Timeout for dynamic wait. + */ +export async function waitUntilEditorIsOpened(driver: WebDriver, title: string, timeout = 10000): Promise { + await driver.wait(async function () { + return (await new EditorView().getOpenEditorTitles()).find(t => t === title); + }, timeout); +} diff --git a/src/ui-test/variables.ts b/src/ui-test/variables.ts index 19c8c151..1e549d76 100644 --- a/src/ui-test/variables.ts +++ b/src/ui-test/variables.ts @@ -4,6 +4,9 @@ export const TEST_RESOURCES_DIR = path.resolve('.', 'test-resources'); export const EXTENSION_DIR = path.join(TEST_RESOURCES_DIR, 'test-extensions'); export const WORKBENCH_DIR = path.join(TEST_RESOURCES_DIR, 'ui-workbench'); export const RESOURCES_DIR = path.resolve('.', 'src', 'ui-test', 'resources'); +export const RESOURCES_TASK_EXAMPLES_DIR: string = path.join(RESOURCES_DIR, 'tasks-examples'); + +export const TASKS_TEST_FILE: string = "tasks.json"; export const DEFAULT_HEADER = 'YamlHeader'; export const DEFAULT_PROPERTY = 'yaml-dsl'; diff --git a/tsconfig.json b/tsconfig.json index 72473843..65c5a463 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { "module": "commonjs", - "target": "ES2020", + "target": "ES2022", "outDir": "out", "lib": [ - "ES2020" + "ES2022" ], "sourceMap": true, "rootDir": "src",