diff --git a/extester.config.json b/extester.config.json new file mode 100644 index 00000000..d5a07d5b --- /dev/null +++ b/extester.config.json @@ -0,0 +1,17 @@ +{ + "$schema": "./node_modules/vscode-extension-tester/resources/extester.schema.json", + "setup": { + "extensionsDir": "./test-resources", + "packageOptions": { + "useYarn": true, + "dependencies": false + } + }, + "run": { + "testFiles": ["out/**/*.test.js"], + "resources": ["./test Fixture with speci@l chars"], + "extensionsDir": "./test-resources", + "settings": "./it-tests/vscode-settings.json", + "customPageObjects": "./out/pageObjects/locators.js" + } +} diff --git a/it-tests/BasicFlow.test.ts b/it-tests/BasicFlow.test.ts index 877149f9..69ccca47 100644 --- a/it-tests/BasicFlow.test.ts +++ b/it-tests/BasicFlow.test.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { By, EditorView, until, VSBrowser, WebDriver, WebView, logging, InputBox } from 'vscode-extension-tester'; +import { By, EditorView, until, VSBrowser, WebDriver, WebView, logging } from 'vscode-extension-tester'; import { assert } from 'chai'; import * as path from 'path'; import { @@ -23,6 +23,7 @@ import { openResourcesAndWaitForActivation, workaroundToRedrawContextualMenu, } from './Util'; +import { KaotoCanvas, KaotoEditor, kaotoLocators, DataMapperEditor } from './pageObjects'; import { waitUntil } from 'async-wait-until'; import * as fs from 'fs-extra'; @@ -132,7 +133,7 @@ describe('Kaoto basic development flow', function () { // TODO: Add a target xsd // TODO: Map an element - await (await driver.findElement(By.css('a[data-testid="design-tab"]'))).click(); + await KaotoEditor.clickDesignTab(driver); const files = fs.readdirSync(workspaceFolder); const xslFiles = files.filter((file) => file.endsWith('.xsl')); @@ -175,90 +176,27 @@ describe('Kaoto basic development flow', function () { }); async function addXsdForSource(driver: WebDriver, kaotoWebview: WebView) { - await driver.wait( - until.elementLocated(By.css('button[data-testid="attach-schema-sourceBody-Body-button"]')), - 5000, - 'Cannot find the button to attach the schema', - ); - await (await driver.findElement(By.css('button[data-testid="attach-schema-sourceBody-Body-button"]'))).click(); - - await waitForXsdAttachModal(driver); - const schemaButton = await driver.findElement(By.xpath("//button[@data-testid='attach-schema-modal-btn-file']")); - await schemaButton.click(); - - await kaotoWebview.switchBack(); - const xsdInputbox = await InputBox.create(10000); - await xsdInputbox.setText('shiporder.xsd'); - try { - const checkboxes = await xsdInputbox.getCheckboxes(); - if (checkboxes.length > 0) { - await checkboxes[0].select(); - } - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - if (!/NoSuchElement/i.test(message) && !/checkbox/i.test(message)) { - throw error; - } - // Fallback for Kaoto <2.10.0 (no checkboxes) - } - await xsdInputbox.confirm(); - await kaotoWebview.switchToFrame(); - - await waitForXsdAttachModal(driver); - const attachButton = await driver.findElement(By.xpath("//button[@data-testid='attach-schema-modal-btn-attach']")); - await attachButton.click(); - - await driver.wait( - until.elementLocated( - // Kaoto 2.6- using -field-, Kaoto 2.7+ using -fx- - By.xpath('//div[starts-with(@data-testid, "node-source-field-shiporder-")] | //div[starts-with(@data-testid, "node-source-fx-shiporder-")]'), - ), - 5000, - 'Root of the imported xsd is not displayed in the UI', - ); -} - -async function waitForXsdAttachModal(driver: WebDriver, timeout: number = 3_000): Promise { - await driver.wait(until.elementLocated(By.xpath("//div[@data-testid='attach-schema-modal']")), timeout, 'Cannot find XSD schema modal dialog'); + await DataMapperEditor.attachXsdSchema(driver, kaotoWebview, 'sourceBody-Body', 'shiporder.xsd'); + await DataMapperEditor.waitForSourceField(driver, 5_000); } async function openDataMapperEditor(driver: WebDriver) { - const kaotoNode = await driver.findElement( - By.css( - `g[data-testid^="custom-node__kaoto-datamapper"],g[data-testid="custom-node__route.from.steps.0.kaoto-datamapper"],g[data-testid="${DATA_TEST_ID_DATAMAPPERSTEP_2_5}"]`, - ), - ); - await kaotoNode.click(); - await driver.wait( - until.elementLocated(By.css('button[title="Click to launch the Kaoto DataMapper editor"]')), - 5000, - 'Cannot find the button to open the datamapper', - ); - await (await driver.findElement(By.css('button[title="Click to launch the Kaoto DataMapper editor"]'))).click(); + const nodeSelector = `g[data-testid^="custom-node__kaoto-datamapper"],g[data-testid="custom-node__route.from.steps.0.kaoto-datamapper"],g[data-testid="${DATA_TEST_ID_DATAMAPPERSTEP_2_5}"]`; + await DataMapperEditor.openFromNode(driver, nodeSelector); } async function deleteDataMapperStep(driver: WebDriver, workspaceFolder: string, kaotoWebview: WebView) { await checkStepWithTestIdOrNodeLabelPresent(driver, 'custom-node__kaoto-datamapper', 'kaoto-datamapper'); - const kaotoNodeConfigured = await driver.findElement( - By.xpath(`//*[name()='g' and starts-with(@data-testid,'custom-node__kaoto-datamapper') or @data-nodelabel='kaoto-datamapper']`), - ); + const kaotoNodeConfigured = await KaotoCanvas.findNodeByTestIdOrLabel(driver, 'custom-node__kaoto-datamapper', 'kaoto-datamapper'); await kaotoNodeConfigured.click(); await workaroundToRedrawContextualMenu(kaotoWebview); - await driver.wait(until.elementLocated(By.css("button[id='reset-view']")), 5000, 'Cannot find the reset view button'); - await (await driver.findElement(By.css("button[id='reset-view']"))).click(); // reset view nodes position to see the delete button - - await driver.wait(until.elementLocated(By.xpath("//div[@data-testid='step-toolbar']")), 5000, 'Cannot find the step toolbar'); - await (await driver.findElement(By.css('button[data-testid="kaoto-datamapper|step-toolbar-button-delete"]'))).click(); + await KaotoCanvas.resetView(driver, 5_000); + await KaotoCanvas.deleteStep(driver, 'kaoto-datamapper', 5_000); + await KaotoCanvas.confirmDeleteStepAndFile(driver, 5_000); - await driver.wait( - until.elementLocated(By.xpath("//div[@data-ouia-component-id='ActionConfirmationModal']")), - 5000, - 'Cannot find the modal to delete the data mapper step', - ); - await (await driver.findElement(By.css('button[data-testid="action-confirmation-modal-btn-del-step-and-file"]'))).click(); await driver.wait( async () => { const filesAfterDeletion = fs.readdirSync(workspaceFolder); @@ -271,63 +209,50 @@ async function deleteDataMapperStep(driver: WebDriver, workspaceFolder: string, } async function createNewRoute(driver: WebDriver) { - await (await driver.findElement(By.xpath("//button[@data-testid='dsl-list-btn']"))).click(); + await KaotoCanvas.clickDslListButton(driver); } async function addAMQPStep(driver: WebDriver) { + const canvasNode = await KaotoCanvas.findTimerOrFromNode(driver); + await KaotoCanvas.openContextMenu(driver, canvasNode); + await KaotoCanvas.clickReplaceInContextMenu(driver); + await driver.wait( - until.elementLocated(By.css('g[data-testid^="custom-node__timer"],g[data-testid="custom-node__route.from"]')), + until.elementLocated(By.xpath(kaotoLocators.KaotoCatalog.tileHeaderByTestId('amqp'))), 5000, - 'Cannot find the node for the timer', + 'Cannot find the tile header for the AMQP step', ); - - const canvasNode = await driver.findElement(By.css('g[data-testid^="custom-node__timer"],g[data-testid="custom-node__route.from"]')); - await driver.actions().contextClick(canvasNode).perform(); - - await driver.wait(until.elementLocated(By.className('pf-topology-context-menu__c-dropdown__menu')), 5000, 'Cannot find the context menu'); - await (await driver.findElement(By.xpath("//\*[@data-testid='context-menu-item-replace']"))).click(); - - await driver.wait(until.elementLocated(By.xpath("//div[@data-testid='tile-header-amqp']")), 5000, 'Cannot find the tile header for the AMQP step'); - await (await driver.findElement(By.xpath("//div[@data-testid='tile-header-amqp']"))).click(); + await (await driver.findElement(By.xpath(kaotoLocators.KaotoCatalog.tileHeaderByTestId('amqp')))).click(); } async function addDatamapperStep(driver: WebDriver, kaotoWebview: WebView) { - await driver.wait( - until.elementLocated(By.css('g[data-testid^="custom-node__log"],g[data-testid="custom-node__route.from.steps.0.log"]')), - 5000, - 'Cannot find the node for the log', - ); - - const canvasNode = await driver.findElement(By.css('g[data-testid^="custom-node__log"],g[data-testid="custom-node__route.from.steps.0.log"]')); - await driver.actions().contextClick(canvasNode).perform(); + const canvasNode = await KaotoCanvas.findLogNode(driver); + await KaotoCanvas.openContextMenu(driver, canvasNode); await workaroundToRedrawContextualMenu(kaotoWebview); - await driver.wait(until.elementLocated(By.className('pf-topology-context-menu__c-dropdown__menu')), 5000, 'Cannot find the context menu'); - await (await driver.findElement(By.xpath("//*[@data-testid='context-menu-item-replace']"))).click(); + await KaotoCanvas.clickReplaceInContextMenu(driver); - await driver.wait(until.elementLocated(By.xpath("//input[@placeholder='Filter by name, description or tag']")), 5000, 'Cannot find the filter input'); - const filterInput = await driver.findElement(By.xpath("//input[@placeholder='Filter by name, description or tag']")); + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.KaotoCatalog.filterInput)), 5000, 'Cannot find the filter input'); + const filterInput = await driver.findElement(By.xpath(kaotoLocators.KaotoCatalog.filterInput)); await filterInput.sendKeys('datamapper'); - await driver.wait(until.elementLocated(By.xpath("//div[@data-testid='tile-kaoto-datamapper']")), 5000, 'Cannot find the tile for the DataMapper step'); - - await (await driver.findElement(By.xpath("//div[@data-testid='tile-kaoto-datamapper']"))).click(); + await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.KaotoCatalog.tileHeaderByTestId('kaoto-datamapper'))), + 5000, + 'Cannot find the tile for the DataMapper step', + ); + await (await driver.findElement(By.xpath(kaotoLocators.KaotoCatalog.tileHeaderByTestId('kaoto-datamapper')))).click(); } /** - * * @param driver * @param testId used for Kaoto 2.3 * @param nodeLabel used for Kaoto 2.4 */ async function checkStepWithTestIdOrNodeLabelPresent(driver: WebDriver, testId: string, nodeLabel: string) { - await driver.wait(until.elementLocated(By.xpath(`//*[name()='g' and starts-with(@data-testid,'${testId}') or @data-nodelabel='${nodeLabel}']`)), 5_000); + await KaotoCanvas.findNodeByTestIdOrLabel(driver, testId, nodeLabel); } async function checkIntegrationNameInTopBarLoaded(driver: WebDriver, name: string) { - await driver.wait( - until.elementLocated(By.xpath(`//span[@data-testid='flows-list-route-id' and contains(., '${name}')]`)), - 5_000, - `Unable to locate integration name '${name} in top bar!'`, - ); + await KaotoEditor.waitForIntegrationName(driver, name); } diff --git a/it-tests/MavenDependencyUpdate.test.ts b/it-tests/MavenDependencyUpdate.test.ts index bc424635..194521d7 100644 --- a/it-tests/MavenDependencyUpdate.test.ts +++ b/it-tests/MavenDependencyUpdate.test.ts @@ -27,7 +27,6 @@ import { import { By, EditorView, - until, VSBrowser, WebDriver, Workbench, @@ -38,6 +37,7 @@ import { CheckboxSetting, before, } from 'vscode-extension-tester'; +import { KaotoCanvas } from './pageObjects'; import { assert, expect } from 'chai'; import * as fs from 'fs'; @@ -250,26 +250,13 @@ describe('Maven dependency update pom.xml', function () { * Add the SQL component step to the topology. */ async function addSqlComponentStep() { - // hover over edge to show add step button - const setBodyToLogEdge = await driver.findElement(By.css('g[data-id^="my-camel-quarkus-route|route.from.steps.0.setBody >>> route.from.steps.1.log"]')); - await driver.actions().move({ origin: setBodyToLogEdge, duration: 2_000 }).perform(); - - // click Add Step button - const addStepButton = await setBodyToLogEdge.findElement(By.className('custom-edge__add-step add-step-icon')); + // hover over edge to show add step button and click Add Step button + const edgeSelector = 'g[data-id^="my-camel-quarkus-route|route.from.steps.0.setBody >>> route.from.steps.1.log"]'; + const addStepButton = await KaotoCanvas.getAddStepButton(driver, edgeSelector); await addStepButton.click(); - // add text into catalog filter - await driver.wait(until.elementLocated(By.className('pf-v6-c-text-input-group__text-input')), 5_000); - const textInput = await driver.findElement(By.className('pf-v6-c-text-input-group__text-input')); - await textInput.click(); - await textInput.clear(); - await textInput.sendKeys('sql'); - - await driver.wait(until.elementLocated(By.css('div[data-testid="tile-sql"]')), 5_000); - - // select SQL component from catalog - const sqlComponent = await driver.findElement(By.css('div[data-testid="tile-sql"]')); - await sqlComponent.click(); + // filter catalog and select SQL component + await KaotoCanvas.filterCatalogAndSelectTile(driver, 'sql', 'sql'); } /** @@ -282,14 +269,13 @@ describe('Maven dependency update pom.xml', function () { ).kaotoWebview; // right click on SQL component node - const sqlComponent = await driver.findElement(By.css('g[data-nodelabel="sql"]')); - await driver.actions().contextClick(sqlComponent).perform(); + const sqlNode = await driver.findElement(By.css('g[data-nodelabel="sql"]')); + await KaotoCanvas.openContextMenu(driver, sqlNode); await workaroundToRedrawContextualMenu(kaotoWebview); // click Delete button - const deleteButton = await driver.findElement(By.css('li[data-testid="context-menu-item-delete"]')); - await deleteButton.click(); + await KaotoCanvas.clickDeleteInContextMenu(driver); // save editor await kaotoWebview.switchBack(); diff --git a/it-tests/PropertyPanelLoading.test.ts b/it-tests/PropertyPanelLoading.test.ts index 6560882e..80c918b3 100644 --- a/it-tests/PropertyPanelLoading.test.ts +++ b/it-tests/PropertyPanelLoading.test.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { By, EditorView, until, VSBrowser, WebDriver, WebView } from 'vscode-extension-tester'; +import { EditorView, VSBrowser, WebDriver, WebView } from 'vscode-extension-tester'; import * as path from 'path'; -import { openAndSwitchToKaotoFrame } from './Util'; +import { clickWhenClickable, dismissHoverOverlay, openAndSwitchToKaotoFrame } from './Util'; +import { KaotoCanvas, KaotoEditor } from './pageObjects'; describe('Property panel loading test', function () { this.timeout(60_000); @@ -31,44 +32,25 @@ describe('Property panel loading test', function () { }); after(async function () { - await kaotoWebview.switchBack(); - - const editorView = new EditorView(); - await editorView.closeAllEditors(); + if (kaotoWebview) { + try { + await kaotoWebview.switchBack(); + } catch { + // already on the VS Code frame — switchBack is a no-op in that case + } + } + await new EditorView().closeAllEditors(); }); it('Open "choice.camel.yaml" file and check property panel is loading and closing', async function () { kaotoWebview = (await openAndSwitchToKaotoFrame(workspaceFolder, 'choice.camel.yaml', driver, true)).kaotoWebview; - const stepWhenXpath = By.xpath( - `//*[name()='g' and starts-with(@data-testid,'custom-node__when')]|//*[name()='foreignObject' and @data-nodelabel='when']/div/div`, - ); - await driver.wait(until.elementLocated(stepWhenXpath), 5_000); - await (await driver.findElement(stepWhenXpath)).click(); - await driver.wait(until.elementLocated(By.className('pf-v6-c-card')), 5_000); - - const closeBtn = await driver.findElement(By.xpath("//button[@data-testid='close-side-bar']")); - await driver.wait( - async () => { - return (await closeBtn.isDisplayed()) && (await closeBtn.isEnabled()); - }, - 5_000, - 'Close button is not displayed!', - ); - // It seems that the close button might not react if clicked too fast after being displayed - await driver.sleep(1_00); + const logNode = await KaotoCanvas.findNodeByInnerTestId(driver, 'route.from.steps.0.choice.when.0.steps.0.log'); + await dismissHoverOverlay(driver); + await clickWhenClickable(driver, logNode); - await closeBtn.click(); - - await driver.sleep(1_000); - - try { - await driver.wait(until.elementLocated(By.className('pf-v6-c-card')), 5_000); - throw new Error('Property panel was not closed!'); - } catch (error) { - if (error instanceof Error && error.name !== 'TimeoutError') { - throw new Error(error.message); - } - } + await KaotoEditor.waitForPropertyPanel(driver); + await KaotoEditor.closePropertyPanel(driver); + await KaotoEditor.waitForPropertyPanelClosed(driver); }); }); diff --git a/it-tests/SwitchBetweenTabs.test.ts b/it-tests/SwitchBetweenTabs.test.ts index 1c277292..ee6f8a7f 100644 --- a/it-tests/SwitchBetweenTabs.test.ts +++ b/it-tests/SwitchBetweenTabs.test.ts @@ -17,35 +17,13 @@ import { By, EditorView, until, VSBrowser, WebDriver, WebView } from 'vscode-ext import { openAndSwitchToKaotoFrame } from './Util'; import { join } from 'path'; import { expect } from 'chai'; +import { EditorTabs, kaotoLocators } from './pageObjects'; describe('Switching between editor tabs', function () { this.timeout(90_000); const WORKSPACE_FOLDER = join(__dirname, '../test Fixture with speci@l chars'); - const locators = { - EditorTabs: { - tabsList: 'pf-v6-c-tabs__list', - tabsItem: 'pf-v6-c-tabs__item', - activeTabAttribute: 'aria-selected', - tabLabelAttribute: 'aria-label', - tabText: 'pf-v6-c-tabs__item-text', - designTabLabel: 'Design canvas', - beansTabLabel: 'Beans editor', - dataMapperTabLabel: 'DataMapper', - }, - DesignCanvas: { - canvas: 'topology', - }, - BeansEditor: { - listView: 'metadata-editor-modal-list-view', - detailsView: 'metadata-editor-modal-details-view', - }, - DataMapper: { - howTo: 'pf-v6-l-bullseye datamapper-howto', - }, - }; - let driver: WebDriver; let kaotoWebview: WebView; @@ -60,57 +38,29 @@ describe('Switching between editor tabs', function () { it('Open "my.camel.yaml" file and check "Design" tab is active by default', async function () { kaotoWebview = (await openAndSwitchToKaotoFrame(WORKSPACE_FOLDER, 'my.camel.yaml', driver, true)).kaotoWebview; - await driver.wait(until.elementLocated(By.className(locators.EditorTabs.tabsList)), 5_000, 'Editor tabs are not displayed properly!'); - expect(await getActiveTabName()).to.equal('Design'); + await driver.wait(until.elementLocated(By.className(kaotoLocators.EditorTabs.tabsList)), 5_000, 'Editor tabs are not displayed properly!'); + const tabs = new EditorTabs(); + expect(await tabs.getActiveTabName()).to.equal('Design'); }); it('Switch to "Beans" tab and check it is active', async function () { - await switchToTab(locators.EditorTabs.beansTabLabel); - await waitForBeansEditorIsLoaded(); - expect(await getActiveTabName()).to.equal('Beans'); + const tabs = new EditorTabs(); + await tabs.switchToTab('Beans editor'); + await EditorTabs.waitForBeansTab(driver); + expect(await tabs.getActiveTabName()).to.equal('Beans'); }); it('Switch to "DataMapper" tab and check it is active', async function () { - await switchToTab(locators.EditorTabs.dataMapperTabLabel); - await waitForDataMapperIsLoaded(); - expect(await getActiveTabName()).to.equal('DataMapper'); + const tabs = new EditorTabs(); + await tabs.switchToTab('DataMapper'); + await EditorTabs.waitForDataMapperTab(driver); + expect(await tabs.getActiveTabName()).to.equal('DataMapper'); }); it('Switch back to "Design" tab and check it is active', async function () { - await switchToTab(locators.EditorTabs.designTabLabel); - await waitForDesignCanvasIsLoaded(); - expect(await getActiveTabName()).to.equal('Design'); + const tabs = new EditorTabs(); + await tabs.switchToTab('Design canvas'); + await EditorTabs.waitForDesignTab(driver); + expect(await tabs.getActiveTabName()).to.equal('Design'); }); - - async function getActiveTabName(): Promise { - const tabName = await driver - .findElement(By.className(locators.EditorTabs.tabsList)) - .findElement(By.xpath(`//button[@${locators.EditorTabs.activeTabAttribute}='true']`)) - .findElement(By.className(locators.EditorTabs.tabText)); - return await tabName.getText(); - } - - async function switchToTab(name: string): Promise { - await driver - .findElement(By.className(locators.EditorTabs.tabsList)) - .findElement(By.xpath(`//button[@${locators.EditorTabs.tabLabelAttribute}='${name}']`)) - .click(); - } - - async function waitForDesignCanvasIsLoaded(): Promise { - await driver.wait( - until.elementLocated(By.xpath(`//div[@data-test-id='${locators.DesignCanvas.canvas}']`)), - 5_000, - 'Design canvas of editor was not loaded properly!', - ); - } - - async function waitForBeansEditorIsLoaded(): Promise { - await driver.wait(until.elementLocated(By.className(locators.BeansEditor.listView)), 5_000, 'Beans "list" view was not loaded properly!'); - await driver.wait(until.elementLocated(By.className(locators.BeansEditor.detailsView)), 5_000, 'Beans "details" view was not loaded properly!'); - } - - async function waitForDataMapperIsLoaded() { - await driver.wait(until.elementLocated(By.className(locators.DataMapper.howTo)), 5_000, 'DataMapper "howTo" content was not loaded properly!'); - } }); diff --git a/it-tests/Util.ts b/it-tests/Util.ts index bcad7e9d..1c73d771 100644 --- a/it-tests/Util.ts +++ b/it-tests/Util.ts @@ -21,6 +21,7 @@ import { ActivityBar, BottomBarPanel, By, + createWaitHelper, CustomEditor, EditorView, ExtensionsViewItem, @@ -32,15 +33,16 @@ import { TerminalView, TextEditor, TreeItem, - until, ViewControl, ViewItemAction, ViewPanelAction, ViewSection, VSBrowser, WebDriver, + WebElement, WebView, } from 'vscode-extension-tester'; +import { KaotoEditor } from './pageObjects/KaotoEditor'; export const CATALOG_VERSION_ID = 'kaoto.camelJbang.version'; @@ -144,7 +146,7 @@ export async function switchToKaotoFrame(driver: WebDriver, checkNotDirty: boole try { kaotoEditor = new CustomEditor(); kaotoWebview = kaotoEditor.getWebView(); - await kaotoWebview.switchToFrame(); + await kaotoWebview.switchToFrame(10_000); return true; } catch (exception) { console.log('failed to switch to frame ' + exception); @@ -159,12 +161,11 @@ export async function switchToKaotoFrame(driver: WebDriver, checkNotDirty: boole } export async function checkEmptyCanvasLoaded(driver: WebDriver, timeout: number = 10_000) { - await driver.wait(until.elementLocated(By.xpath("//div[@data-testid='visualization-empty-state']")), timeout, 'Empty Kaoto Canvas was not loaded properly'); + await KaotoEditor.waitForEmptyCanvas(driver, timeout); } export async function checkTopologyLoaded(driver: WebDriver, timeout: number = 10_000) { - await driver.wait(until.elementLocated(By.xpath("//div[@data-test-id='topology']")), timeout, 'Kaoto topology was not loaded properly'); - await driver.sleep(1_000); // stabilize tests which are sometimes failing on macOS CI + await KaotoEditor.waitForTopology(driver, timeout); } // Enforce same default storage setup as ExTester - see https://github.com/redhat-developer/vscode-extension-tester/wiki/Test-Setup#useful-env-variables @@ -203,6 +204,30 @@ export async function closeEditor(title: string, save?: boolean) { } } +export async function dismissHoverOverlay(driver: WebDriver) { + const waitHelper = createWaitHelper(driver); + const hoverContents = await driver.findElements(By.css('.hover-contents')); + for (const hoverContent of hoverContents) { + if (await hoverContent.isDisplayed()) { + await driver + .actions() + .move({ x: 5, y: 5, origin: driver.findElement(By.css('body')) }) + .perform(); + await waitHelper.forNotVisible(hoverContent, { timeout: 2_000, message: 'Hover overlay is still visible' }); + break; + } + } +} + +export async function clickWhenClickable(driver: WebDriver, element: WebElement, timeout = 5_000) { + const waitHelper = createWaitHelper(driver); + await waitHelper.forVisible(element, { timeout }); + await waitHelper.forEnabled(element, { timeout }); + await waitHelper.forStable(element, { timeout }); + await waitHelper.forClickable(element, { timeout }); + await element.click(); +} + export async function openResourcesAndWaitForActivation( path: string, waitForActivation: boolean = true, diff --git a/it-tests/pageObjects/CatalogModal.ts b/it-tests/pageObjects/CatalogModal.ts new file mode 100644 index 00000000..41b2b02a --- /dev/null +++ b/it-tests/pageObjects/CatalogModal.ts @@ -0,0 +1,156 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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 { AbstractElement, By, Key, until, WebDriver, WebElement } from 'vscode-extension-tester'; +import { kaotoLocators } from './locators'; + +/** + * Page object for the Kaoto Catalog modal dialog. + * + * Opened by clicking the catalog button in the topology control bar. + * Provides helpers for: + * - Opening and closing the modal + * - Switching between List and Gallery layouts + * - Reading catalog item providers + * - Filtering by provider + * + * Must be used inside the webview frame. + */ +export class CatalogModal extends AbstractElement { + constructor() { + super(CatalogModal.locators.CatalogModal.constructor); + } + + // ─── Open / close ────────────────────────────────────────────────────────── + + /** + * Click the topology-control-bar catalog button and wait for the modal. + * Returns the catalog modal `WebElement`. + */ + static async open(driver: WebDriver, timeout = 10_000): Promise { + const catalogBtn = await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.CatalogModal.catalogButton)), + timeout, + 'Catalog button was not located', + ); + await driver.wait(until.elementIsVisible(catalogBtn), timeout, 'Catalog button was not visible'); + await driver.wait(until.elementIsEnabled(catalogBtn), timeout, 'Catalog button was not enabled'); + await catalogBtn.click(); + + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.CatalogModal.window)), timeout); + return driver.findElement(By.xpath(kaotoLocators.CatalogModal.window)); + } + + /** + * Close the catalog modal. + */ + static async close(catalogWindow: WebElement): Promise { + await catalogWindow.findElement(By.xpath(kaotoLocators.CatalogModal.closeButton)).click(); + } + + // ─── Layout switching ───────────────────────────────────────────────────── + + /** + * Switch the catalog to List view and wait for the list to appear. + * Returns the modal element. + */ + static async switchToListView(driver: WebDriver, catalogWindow: WebElement, timeout = 10_000): Promise { + await catalogWindow.findElement(By.xpath(kaotoLocators.CatalogModal.listButton)).click(); + await driver.wait(until.elementLocated(By.className(kaotoLocators.CatalogModal.list)), timeout); + return catalogWindow; + } + + /** + * Switch the catalog back to Gallery view and wait for the gallery to appear. + */ + static async switchToGalleryView(driver: WebDriver, catalogWindow: WebElement, timeout = 5_000): Promise { + try { + await catalogWindow.findElement(By.xpath(kaotoLocators.CatalogModal.galleryButton)).click(); + await driver.wait(until.elementLocated(By.className(kaotoLocators.CatalogModal.gallery)), timeout); + } catch { + // Workaround: hover text for the list button can overlap the gallery button + await driver.actions().sendKeys(Key.ENTER).perform(); + await catalogWindow.findElement(By.xpath(kaotoLocators.CatalogModal.galleryButton)).click(); + await driver.wait(until.elementLocated(By.className(kaotoLocators.CatalogModal.gallery)), timeout); + } + } + + // ─── Provider filter ────────────────────────────────────────────────────── + + /** + * Open or close the provider filter dropdown. + * + * @param open `true` to open and wait for the menu; `false` to close it. + */ + static async toggleProviderDropdown(driver: WebDriver, catalogWindow: WebElement, open: boolean, timeout = 5_000): Promise { + const dropdown = await catalogWindow.findElement(By.xpath(kaotoLocators.CatalogModal.providerFilterDropdown)); + await dropdown.click(); + await driver.sleep(1_000); // allow DOM to reflect changes + + if (open) { + await driver.wait(until.elementLocated(By.id(kaotoLocators.CatalogModal.providerSelectMenu)), timeout); + } else { + try { + await driver.wait(until.elementLocated(By.id(kaotoLocators.CatalogModal.providerSelectMenu)), timeout); + } catch (error) { + if (error instanceof Error && error.name !== 'TimeoutError') { + throw error; + } + } + } + } + + /** + * Return the list of provider names shown in the provider filter menu. + */ + static async getProviderNames(driver: WebDriver, catalogWindow: WebElement): Promise { + await CatalogModal.toggleProviderDropdown(driver, catalogWindow, true); + + const menu = await catalogWindow.findElement(By.id(kaotoLocators.CatalogModal.providerSelectMenu)); + const items = await menu.findElements(By.className(kaotoLocators.CatalogModal.providerMenuListItem)); + const labels = await Promise.all(items.map((item) => item.getText())); + + await CatalogModal.toggleProviderDropdown(driver, catalogWindow, false); + return labels; + } + + /** + * Toggle the checkbox for a given provider in the provider filter menu. + * + * @param provider Provider label shown in the menu, e.g. "Community", "Red Hat" + */ + static async toggleProvider(driver: WebDriver, catalogWindow: WebElement, provider: string): Promise { + await CatalogModal.toggleProviderDropdown(driver, catalogWindow, true); + + const menu = await catalogWindow.findElement(By.id(kaotoLocators.CatalogModal.providerSelectMenu)); + const item = await menu.findElement(By.xpath(kaotoLocators.CatalogModal.providerItem(provider))); + await item.click(); + + await CatalogModal.toggleProviderDropdown(driver, catalogWindow, false); + } + + // ─── List item inspection ───────────────────────────────────────────────── + + /** + * Return the provider label string of the first visible catalog item in + * List view. + */ + static async getFirstItemProvider(catalogWindow: WebElement): Promise { + const listItem = await catalogWindow + .findElement(By.className(kaotoLocators.CatalogModal.list)) + .findElement(By.className(kaotoLocators.CatalogModal.listItemRow)); + return listItem.findElement(By.className(kaotoLocators.CatalogModal.listItemProvider)).getText(); + } +} diff --git a/it-tests/pageObjects/DataMapperEditor.ts b/it-tests/pageObjects/DataMapperEditor.ts new file mode 100644 index 00000000..047c76fe --- /dev/null +++ b/it-tests/pageObjects/DataMapperEditor.ts @@ -0,0 +1,141 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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 { AbstractElement, By, until, WebDriver, WebView } from 'vscode-extension-tester'; +import { InputBox } from 'vscode-extension-tester'; +import { kaotoLocators } from './locators'; + +/** + * Page object for the Kaoto DataMapper editor. + * + * Provides helpers for: + * - Opening the DataMapper from a canvas node + * - Attaching an XSD schema for the source body + * - Waiting for the DataMapper "how-to" content (empty state) + * + * Must be used inside the webview frame (except where noted). + */ +export class DataMapperEditor extends AbstractElement { + constructor() { + super(DataMapperEditor.locators.DataMapperEditor.constructor); + } + + // ─── Open DataMapper ─────────────────────────────────────────────────────── + + /** + * Click a DataMapper canvas node and then click the + * "Click to launch the Kaoto DataMapper editor" button. + * + * @param nodeSelector CSS selector that uniquely identifies the DataMapper node + */ + static async openFromNode(driver: WebDriver, nodeSelector: string, timeout = 5_000): Promise { + const node = await driver.findElement(By.css(nodeSelector)); + await node.click(); + await driver.wait( + until.elementLocated(By.css(kaotoLocators.DataMapperEditor.openEditorButton)), + timeout, + 'Cannot find the button to open the DataMapper', + ); + await (await driver.findElement(By.css(kaotoLocators.DataMapperEditor.openEditorButton))).click(); + } + + // ─── Schema attachment ──────────────────────────────────────────────────── + + /** + * Click the "Attach schema" button for a given target (e.g. + * `"sourceBody-Body"`) and go through the file-picker flow to select + * an XSD file by name. + * + * The method temporarily leaves the webview frame to interact with the + * VS Code InputBox. + * + * @param driver + * @param kaotoWebview The active WebView — needed to switch frames + * @param target The target suffix used in the button's data-testid + * @param xsdFileName File name to type into the InputBox filter + */ + static async attachXsdSchema(driver: WebDriver, kaotoWebview: WebView, target: string, xsdFileName: string): Promise { + // Click the attach button inside the webview + await driver.wait( + until.elementLocated(By.css(kaotoLocators.DataMapperEditor.attachSchemaButton(target))), + 5_000, + 'Cannot find the button to attach the schema', + ); + await (await driver.findElement(By.css(kaotoLocators.DataMapperEditor.attachSchemaButton(target)))).click(); + + // Wait for the attach-schema modal + await DataMapperEditor.waitForAttachSchemaModal(driver); + + // Click the "File" button to open the VS Code file picker + const schemaButton = await driver.findElement(By.xpath(kaotoLocators.DataMapperEditor.attachSchemaFileButton)); + await schemaButton.click(); + + // Leave webview to interact with VS Code InputBox + await kaotoWebview.switchBack(); + const xsdInputBox = await InputBox.create(10_000); + await xsdInputBox.setText(xsdFileName); + + try { + const checkboxes = await xsdInputBox.getCheckboxes(); + if (checkboxes.length > 0) { + await checkboxes[0].select(); + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (!/NoSuchElement/i.test(message) && !/checkbox/i.test(message)) { + throw error; + } + // Fallback for Kaoto <2.10.0 (no checkboxes in InputBox) + } + await xsdInputBox.confirm(); + + // Switch back into the webview + await kaotoWebview.switchToFrame(); + + // Confirm attachment in the modal + await DataMapperEditor.waitForAttachSchemaModal(driver); + const attachButton = await driver.findElement(By.xpath(kaotoLocators.DataMapperEditor.attachSchemaAttachButton)); + await attachButton.click(); + } + + /** + * Wait for the attach-schema modal dialog to appear. + */ + static async waitForAttachSchemaModal(driver: WebDriver, timeout = 3_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.DataMapperEditor.attachSchemaModal)), timeout, 'Cannot find XSD schema modal dialog'); + } + + /** + * Wait for the XSD source field to appear after a successful schema attach. + */ + static async waitForSourceField(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.DataMapperEditor.sourceFieldByTestId)), + timeout, + 'Root of the imported XSD is not displayed in the UI', + ); + } + + /** + * Wait for the DataMapper "how-to" empty-state content to be visible. + */ + static async waitForHowToContent(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait( + until.elementLocated(By.className(kaotoLocators.DataMapperEditor.howTo)), + timeout, + 'DataMapper "howTo" content was not loaded properly!', + ); + } +} diff --git a/it-tests/pageObjects/EditorTabs.ts b/it-tests/pageObjects/EditorTabs.ts new file mode 100644 index 00000000..7306aa02 --- /dev/null +++ b/it-tests/pageObjects/EditorTabs.ts @@ -0,0 +1,84 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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 { AbstractElement, By, until, WebDriver } from 'vscode-extension-tester'; +import { kaotoLocators } from './locators'; + +/** + * Page object for the Kaoto editor tab bar. + * + * Represents the row of tabs at the top of the Kaoto editor webview: + * "Design", "Beans", "DataMapper". + * + * Must be used inside the webview frame. + */ +export class EditorTabs extends AbstractElement { + constructor() { + super(EditorTabs.locators.EditorTabs.constructor); + } + + // ─── Active tab ──────────────────────────────────────────────────────────── + + /** + * Return the text label of the currently active tab (e.g. "Design"). + */ + async getActiveTabName(): Promise { + const tabsList = await this.getDriver().findElement(By.className(kaotoLocators.EditorTabs.tabsList)); + const activeBtn = await tabsList.findElement(By.xpath(kaotoLocators.EditorTabs.activeTabXpath)); + const textEl = await activeBtn.findElement(By.className(kaotoLocators.EditorTabs.tabsItemText)); + return textEl.getText(); + } + + // ─── Navigation ──────────────────────────────────────────────────────────── + + /** + * Click the tab identified by its `aria-label` attribute. + * + * @param label Tab aria-label, e.g. "Design canvas", "Beans editor", "DataMapper" + */ + async switchToTab(label: string): Promise { + const tabsList = await this.getDriver().findElement(By.className(kaotoLocators.EditorTabs.tabsList)); + await tabsList.findElement(By.xpath(kaotoLocators.EditorTabs.tabByLabel(label))).click(); + } + + // ─── Assertions ──────────────────────────────────────────────────────────── + + /** + * Wait until the topology (Design canvas) is visible — indicates the Design + * tab has fully loaded. + */ + static async waitForDesignTab(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.KaotoEditor.topology)), timeout, 'Design canvas was not loaded properly!'); + } + + /** + * Wait until both Beans editor panels are visible. + */ + static async waitForBeansTab(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.className(kaotoLocators.BeansEditor.listView)), timeout, 'Beans "list" view was not loaded properly!'); + await driver.wait(until.elementLocated(By.className(kaotoLocators.BeansEditor.detailsView)), timeout, 'Beans "details" view was not loaded properly!'); + } + + /** + * Wait until the DataMapper "how-to" content is visible. + */ + static async waitForDataMapperTab(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait( + until.elementLocated(By.className(kaotoLocators.DataMapperEditor.howTo)), + timeout, + 'DataMapper "howTo" content was not loaded properly!', + ); + } +} diff --git a/it-tests/pageObjects/KaotoCanvas.ts b/it-tests/pageObjects/KaotoCanvas.ts new file mode 100644 index 00000000..f37219f5 --- /dev/null +++ b/it-tests/pageObjects/KaotoCanvas.ts @@ -0,0 +1,208 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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 { AbstractElement, By, until, WebDriver, WebElement } from 'vscode-extension-tester'; +import { kaotoLocators } from './locators'; + +/** + * Page object for Kaoto canvas node interactions. + * + * Provides helpers for finding nodes by test-id / node-label, triggering + * context menus, selecting/deleting steps, and interacting with edges. + * + * Must be used inside the webview frame. + */ +export class KaotoCanvas extends AbstractElement { + constructor() { + super(By.xpath(`//div[@data-test-id='topology']`)); + } + + // ─── DSL list ────────────────────────────────────────────────────────────── + + /** + * Click the "DSL list" button to create a new route from the empty canvas. + */ + static async clickDslListButton(driver: WebDriver): Promise { + await (await driver.findElement(By.xpath(kaotoLocators.KaotoCatalog.dslListButton))).click(); + } + + // ─── Node finders ────────────────────────────────────────────────────────── + + /** + * Find a canvas node in Kaoto 2.4+ by the exact data-testid on its inner
. + * In the current DOM the clickable element is a + * whose child
carries the full step path as data-testid + * (e.g. "route.from.steps.0.choice.when.0.steps.0.log"). + */ + static async findNodeByInnerTestId(driver: WebDriver, innerTestId: string, timeout = 5_000): Promise { + const xpath = kaotoLocators.KaotoNode.byInnerTestId(innerTestId); + await driver.wait(until.elementLocated(By.xpath(xpath)), timeout, `Node with inner test-id '${innerTestId}' was not found in topology`); + return driver.findElement(By.xpath(xpath)); + } + + /** + * Find a canvas node using a data-testid prefix OR data-nodelabel fallback. + * This covers the API change between Kaoto 2.3 (testId) and 2.4+ (nodeLabel). + */ + static async findNodeByTestIdOrLabel(driver: WebDriver, testIdPrefix: string, nodeLabel: string, timeout = 5_000): Promise { + await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.KaotoNode.byTestIdOrLabel(testIdPrefix, nodeLabel))), + timeout, + `Node '${nodeLabel}' was not found in topology`, + ); + return driver.findElement(By.xpath(kaotoLocators.KaotoNode.byTestIdOrLabel(testIdPrefix, nodeLabel))); + } + + /** + * Find a canvas node by CSS — data-testid prefix OR data-nodelabel. + */ + static async findNodeByCss(driver: WebDriver, testIdPrefix: string, nodeLabel: string, timeout = 5_000): Promise { + const selector = kaotoLocators.KaotoNode.byTestIdPrefixOrNodeLabel(testIdPrefix, nodeLabel); + await driver.wait(until.elementLocated(By.css(selector)), timeout, `Node '${nodeLabel}' was not found in topology`); + return driver.findElement(By.css(selector)); + } + + /** + * Wait until a node with the given data-nodelabel is present on the canvas. + */ + static async waitForNodeByLabel(driver: WebDriver, nodeLabel: string, timeout = 10_000): Promise { + await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.KaotoNode.byTestIdOrLabel(`custom-node__${nodeLabel}`, nodeLabel))), + timeout, + `'${nodeLabel}' was not found in current topology.`, + ); + } + + /** + * Find the timer/from node (covers Kaoto 2.3 `custom-node__timer` and 2.4+ `custom-node__route.from`). + */ + static async findTimerOrFromNode(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.css(kaotoLocators.KaotoNode.timerOrFromNode)), timeout, 'Cannot find the timer/from node'); + return driver.findElement(By.css(kaotoLocators.KaotoNode.timerOrFromNode)); + } + + /** + * Find the log node (covers Kaoto 2.3 `custom-node__log` and 2.4+ step-path variant). + */ + static async findLogNode(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.css(kaotoLocators.KaotoNode.logNode)), timeout, 'Cannot find the log node'); + return driver.findElement(By.css(kaotoLocators.KaotoNode.logNode)); + } + + // ─── Node label text (for settings tests) ───────────────────────────────── + + /** + * Get the label text element of a node by its data-testid. + * Used to verify Node Label setting is applied. + */ + static async findNodeLabelText(driver: WebDriver, nodeTestId: string, timeout = 10_000): Promise { + return driver.wait( + until.elementLocated(By.xpath(kaotoLocators.KaotoNode.labelText(nodeTestId))), + timeout, + `Label text for node '${nodeTestId}' was not found`, + ); + } + + // ─── Context menu ────────────────────────────────────────────────────────── + + /** + * Right-click a canvas node and wait for the context menu to appear. + */ + static async openContextMenu(driver: WebDriver, node: WebElement, timeout = 5_000): Promise { + await driver.actions().contextClick(node).perform(); + await driver.wait(until.elementLocated(By.className(kaotoLocators.KaotoContextMenu.menu)), timeout, 'Cannot find the Kaoto context menu'); + return driver.findElement(By.className(kaotoLocators.KaotoContextMenu.menu)); + } + + /** + * Click "Replace" in the node context menu. + */ + static async clickReplaceInContextMenu(driver: WebDriver): Promise { + await (await driver.findElement(By.xpath(kaotoLocators.KaotoContextMenu.replaceItem))).click(); + } + + /** + * Click "Delete" in the node context menu. + */ + static async clickDeleteInContextMenu(driver: WebDriver): Promise { + await (await driver.findElement(By.css(kaotoLocators.KaotoContextMenu.deleteItem))).click(); + } + + // ─── Step toolbar ────────────────────────────────────────────────────────── + + /** + * Wait for the step toolbar to appear and click the reset-view button to + * bring all nodes into view before interacting with toolbar actions. + */ + static async resetView(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.css(kaotoLocators.StepToolbar.resetViewButton)), timeout, 'Cannot find the reset view button'); + await (await driver.findElement(By.css(kaotoLocators.StepToolbar.resetViewButton))).click(); + } + + /** + * Wait for the step toolbar and click the delete button for a given step. + * + * @param stepId The step identifier prefix used in the button's data-testid + * (e.g. "kaoto-datamapper") + */ + static async deleteStep(driver: WebDriver, stepId: string, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.StepToolbar.toolbar)), timeout, 'Cannot find the step toolbar'); + await (await driver.findElement(By.css(kaotoLocators.StepToolbar.deleteButton(stepId)))).click(); + } + + /** + * Wait for the action-confirmation modal, then click the + * "Delete step and file" button. + */ + static async confirmDeleteStepAndFile(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.StepToolbar.actionConfirmModal)), timeout, 'Cannot find the modal to confirm deletion'); + await (await driver.findElement(By.css(kaotoLocators.StepToolbar.confirmDeleteStepAndFile))).click(); + } + + // ─── Edge / add-step ────────────────────────────────────────────────────── + + /** + * Hover over an edge and return the "Add Step" button element on that edge. + * + * @param edgeSelector CSS selector for the edge `` element + */ + static async getAddStepButton(driver: WebDriver, edgeSelector: string, _timeout = 5_000): Promise { + const edge = await driver.findElement(By.css(edgeSelector)); + await driver.actions().move({ origin: edge, duration: 2_000 }).perform(); + return edge.findElement(By.className(kaotoLocators.KaotoEdge.addStepIcon)); + } + + // ─── Catalog filter ──────────────────────────────────────────────────────── + + /** + * Type a filter term into the catalog filter input and wait for a matching + * tile to appear. + * + * @param driver + * @param term Text to type into the filter (e.g. "sql", "amqp") + * @param tileTestId The `data-testid` value of the expected tile (without + * the "tile-" prefix), e.g. "sql" + */ + static async filterCatalogAndSelectTile(driver: WebDriver, term: string, tileTestId: string, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.className(kaotoLocators.KaotoCatalog.filterTextInput)), timeout); + const textInput = await driver.findElement(By.className(kaotoLocators.KaotoCatalog.filterTextInput)); + await textInput.click(); + await textInput.clear(); + await textInput.sendKeys(term); + + await driver.wait(until.elementLocated(By.css(kaotoLocators.KaotoCatalog.tileByTestId(tileTestId))), timeout, `Cannot find tile for '${tileTestId}'`); + await (await driver.findElement(By.css(kaotoLocators.KaotoCatalog.tileByTestId(tileTestId)))).click(); + } +} diff --git a/it-tests/pageObjects/KaotoEditor.ts b/it-tests/pageObjects/KaotoEditor.ts new file mode 100644 index 00000000..609e26c1 --- /dev/null +++ b/it-tests/pageObjects/KaotoEditor.ts @@ -0,0 +1,109 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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 { AbstractElement, By, until, WebDriver } from 'vscode-extension-tester'; +import { kaotoLocators } from './locators'; + +/** + * Page object for the Kaoto webview editor. + * + * Provides high-level helpers for checking editor state (empty canvas, + * topology loaded, property panel) and interacting with the side bar. + * + * All DOM interactions happen inside the webview frame — callers must have + * already switched into the frame before constructing/using this object. + */ +export class KaotoEditor extends AbstractElement { + constructor() { + super(KaotoEditor.locators.KaotoEditor.constructor); + } + + // ─── State checks ────────────────────────────────────────────────────────── + + /** + * Wait until the "empty canvas" placeholder is visible (no routes loaded). + */ + static async waitForEmptyCanvas(driver: WebDriver, timeout = 10_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.KaotoEditor.emptyCanvas)), timeout, 'Empty Kaoto Canvas was not loaded properly'); + } + + /** + * Wait until the topology (route graph) is visible. + */ + static async waitForTopology(driver: WebDriver, timeout = 10_000): Promise { + await driver.wait(until.elementLocated(By.xpath(kaotoLocators.KaotoEditor.topology)), timeout, 'Kaoto topology was not loaded properly'); + await driver.sleep(1_000); // stabilize — topology re-renders briefly on macOS CI + } + + // ─── Property panel ──────────────────────────────────────────────────────── + + /** + * Wait for a node's property panel card to appear. + */ + static async waitForPropertyPanel(driver: WebDriver, timeout = 5_000): Promise { + await driver.wait(until.elementLocated(By.className(kaotoLocators.KaotoEditor.propertyPanelCard)), timeout, 'Property panel was not opened'); + } + + /** + * Click the "close side bar" button to dismiss the property panel. + * Returns once the property panel card has disappeared. + */ + static async closePropertyPanel(driver: WebDriver, timeout = 5_000): Promise { + const closeBtn = await driver.findElement(By.xpath(kaotoLocators.KaotoEditor.closeSideBar)); + await driver.wait(async () => (await closeBtn.isDisplayed()) && (await closeBtn.isEnabled()), timeout, 'Close button is not displayed!'); + // Small buffer — the button may not react if clicked immediately after appearing + await driver.sleep(100); + await closeBtn.click(); + } + + /** + * Assert the property panel card is **not** present. + * Throws if the card is still visible after `timeout` ms. + */ + static async waitForPropertyPanelClosed(driver: WebDriver, timeout = 5_000): Promise { + await driver.sleep(1_000); + try { + await driver.wait(until.elementLocated(By.className(kaotoLocators.KaotoEditor.propertyPanelCard)), timeout); + throw new Error('Property panel was not closed!'); + } catch (error) { + if (error instanceof Error && error.name !== 'TimeoutError') { + throw error; + } + } + } + + // ─── Navigation ──────────────────────────────────────────────────────────── + + /** + * Click the "Design" tab link inside the DataMapper editor to return to the + * design canvas. + */ + static async clickDesignTab(driver: WebDriver): Promise { + await (await driver.findElement(By.css(kaotoLocators.KaotoDesignTab.link))).click(); + } + + // ─── Integration name ────────────────────────────────────────────────────── + + /** + * Wait until the integration name appears in the top bar. + */ + static async waitForIntegrationName(driver: WebDriver, name: string, timeout = 5_000): Promise { + await driver.wait( + until.elementLocated(By.xpath(kaotoLocators.KaotoEditor.flowsListRouteId(name))), + timeout, + `Unable to locate integration name '${name}' in top bar!`, + ); + } +} diff --git a/it-tests/pageObjects/index.ts b/it-tests/pageObjects/index.ts new file mode 100644 index 00000000..9c3333c5 --- /dev/null +++ b/it-tests/pageObjects/index.ts @@ -0,0 +1,28 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ + +/** + * Barrel export for all Kaoto custom page objects. + * + * Import from here in test files: + * import { KaotoEditor, KaotoCanvas, EditorTabs, CatalogModal, DataMapperEditor } from '../pageObjects'; + */ +export { KaotoEditor } from './KaotoEditor'; +export { EditorTabs } from './EditorTabs'; +export { KaotoCanvas } from './KaotoCanvas'; +export { CatalogModal } from './CatalogModal'; +export { DataMapperEditor } from './DataMapperEditor'; +export { kaotoLocators, locators } from './locators'; diff --git a/it-tests/pageObjects/locators.ts b/it-tests/pageObjects/locators.ts new file mode 100644 index 00000000..da0313cc --- /dev/null +++ b/it-tests/pageObjects/locators.ts @@ -0,0 +1,260 @@ +/** + * Copyright 2025 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ + +/** + * Kaoto IT-test custom locator contribution. + * + * Compiled to `out/pageObjects/locators.js` and registered with ExTester via + * `extester.config.json` → `run.customPageObjects`. + * + * The exported `locators` object follows the `LocatorDiff` shape expected by + * `vscode-extension-tester`: any unknown top-level key is accepted because + * `AbstractElement.locators` uses an open intersection type. + * + * Usage in page-object classes: + * import { AbstractElement } from 'vscode-extension-tester'; + * // locators are available at runtime via AbstractElement.locators + * + * Usage in tests directly: + * import { kaotoLocators } from '../pageObjects/locators'; + * By.xpath(kaotoLocators.KaotoEditor.emptyCanvas); + */ + +import { By } from 'vscode-extension-tester'; +import type { LocatorDiff } from 'vscode-extension-tester'; + +// ─── Typed locator map ──────────────────────────────────────────────────────── + +/** + * All Kaoto-specific CSS/XPath selectors in one place. + * + * These are plain string values or `By` locators. The `LocatorDiff` shape + * requires the leaf values to be `By` objects; this object is also exported + * as `kaotoLocators` (typed with plain strings) for direct use in tests that + * prefer string-form XPath. + */ +export const kaotoLocators = { + /** Main Kaoto webview canvas & editor state */ + KaotoEditor: { + emptyCanvas: `//div[@data-testid='visualization-empty-state']`, + topology: `//div[@data-test-id='topology']`, + designTabLink: `a[data-testid="design-tab"]`, + closeSideBar: `//button[@data-testid='close-side-bar']`, + propertyPanelCard: `pf-v6-c-card`, + flowsListRouteId: (name: string) => `//span[@data-testid='flows-list-route-id' and contains(., '${name}')]`, + }, + + /** Editor tab bar (Design / Beans / DataMapper) */ + EditorTabs: { + tabsList: `pf-v6-c-tabs__list`, + tabsItemText: `pf-v6-c-tabs__item-text`, + activeTabXpath: `//button[@aria-selected='true']`, + tabByLabel: (label: string) => `//button[@aria-label='${label}']`, + }, + + /** Canvas topology nodes */ + KaotoNode: { + /** + * Generic node by data-testid prefix OR data-nodelabel. + * Covers Kaoto 2.3 () and + * Kaoto 2.4+ (). + */ + byTestIdOrLabel: (testIdPrefix: string, nodeLabel: string) => + `//*[(name()='g' or name()='foreignObject') and (starts-with(@data-testid,'${testIdPrefix}') or @data-nodelabel='${nodeLabel}')]`, + /** + * Kaoto 2.4+ nodes carry the step test-id on an inner
, while the + * clickable element is the parent . + * This locator finds that by the exact test-id of its child div. + */ + byInnerTestId: (testId: string) => `//*[name()='foreignObject' and ./div[@data-testid='${testId}']]`, + /** Node by exact data-testid (CSS) — g or foreignObject */ + byTestId: (testId: string) => `g[data-testid="${testId}"],foreignObject[data-testid="${testId}"]`, + /** Node where data-testid starts-with prefix (CSS) — g or foreignObject */ + byTestIdPrefix: (prefix: string) => `g[data-testid^="${prefix}"],foreignObject[data-testid^="${prefix}"]`, + /** Node by data-nodelabel (CSS) — g or foreignObject */ + byNodeLabel: (label: string) => `g[data-nodelabel="${label}"],foreignObject[data-nodelabel="${label}"]`, + /** Node label text span (for NodeLabel settings test) */ + labelText: (testId: string) => + `//*[(name()='g' or name()='foreignObject') and @data-testid='${testId}']//*[contains(@class,'custom-node__label__text')]`, + /** Compound selector: testId prefix OR data-nodelabel — CSS form */ + byTestIdPrefixOrNodeLabel: (prefix: string, label: string) => + `g[data-testid^="${prefix}"],g[data-nodelabel="${label}"],foreignObject[data-testid^="${prefix}"],foreignObject[data-nodelabel="${label}"]`, + /** + * Timer / from node — covers Kaoto 2.3 () + * and Kaoto 2.4+ (). + */ + timerOrFromNode: `g[data-testid^="custom-node__timer"],g[data-testid="custom-node__route.from"],foreignObject[data-nodelabel="route.from"]`, + /** + * Log node — covers Kaoto 2.3 () and + * Kaoto 2.4+ (). + */ + logNode: `g[data-testid^="custom-node__log"],g[data-testid="custom-node__route.from.steps.0.log"],foreignObject[data-nodelabel="log"]`, + }, + + /** Design-tab link inside the DataMapper view */ + KaotoDesignTab: { + link: `a[data-testid="design-tab"]`, + }, + + /** Kaoto topology context menu */ + KaotoContextMenu: { + menu: `pf-topology-context-menu__c-dropdown__menu`, + replaceItem: `//\*[@data-testid='context-menu-item-replace']`, + deleteItem: `li[data-testid="context-menu-item-delete"]`, + }, + + /** Step toolbar (shown when a node is selected) */ + StepToolbar: { + toolbar: `//div[@data-testid='step-toolbar']`, + deleteButton: (stepId: string) => `button[data-testid="${stepId}|step-toolbar-button-delete"]`, + resetViewButton: `button[id='reset-view']`, + actionConfirmModal: `//div[@data-ouia-component-id='ActionConfirmationModal']`, + confirmDeleteStepAndFile: `button[data-testid="action-confirmation-modal-btn-del-step-and-file"]`, + }, + + /** Edge / add-step interactions */ + KaotoEdge: { + addStepIcon: `custom-edge__add-step add-step-icon`, + }, + + /** Component/step catalog inside the editor */ + KaotoCatalog: { + filterInput: `//input[@placeholder='Filter by name, description or tag']`, + filterTextInput: `pf-v6-c-text-input-group__text-input`, + tileByTestId: (id: string) => `div[data-testid="tile-${id}"]`, + tileHeaderByTestId: (id: string) => `//div[@data-testid='tile-header-${id}']`, + dslListButton: `//button[@data-testid='dsl-list-btn']`, + }, + + /** Catalog modal (opened via toolbar catalog button) */ + CatalogModal: { + catalogButton: `//button[@id='topology-control-bar-catalog-button']`, + window: `//div[@data-ouia-component-id='CatalogModal']`, + closeButton: `//button[@data-ouia-component-id='CatalogModal-ModalBoxCloseButton']`, + listButton: `//button[@id='toggle-layout-button-List']`, + galleryButton: `//button[@id='toggle-layout-button-Gallery']`, + list: `pf-v6-c-data-list`, + gallery: `pf-v6-l-gallery pf-m-gutter`, + listItemRow: `pf-v6-c-data-list__item-row`, + listItemProvider: `catalog-data-list-item__provider`, + providerFilterDropdown: `//button[@data-testid='provider-filter-toggle']`, + providerSelectMenu: `providers-select-menu`, + providerMenuListItem: `pf-v6-c-menu__list-item`, + providerItem: (label: string) => `//li[@data-testid='providers-select-item-${label}']`, + }, + + /** Beans editor (side panel) */ + BeansEditor: { + listView: `metadata-editor-modal-list-view`, + detailsView: `metadata-editor-modal-details-view`, + }, + + /** DataMapper editor */ + DataMapperEditor: { + howTo: `pf-v6-l-bullseye datamapper-howto`, + openEditorButton: `button[title="Click to launch the Kaoto DataMapper editor"]`, + attachSchemaButton: (target: string) => `button[data-testid="attach-schema-${target}-button"]`, + attachSchemaModal: `//div[@data-testid='attach-schema-modal']`, + attachSchemaFileButton: `//button[@data-testid='attach-schema-modal-btn-file']`, + attachSchemaAttachButton: `//button[@data-testid='attach-schema-modal-btn-attach']`, + sourceFieldByTestId: `//div[starts-with(@data-testid, "node-source-field-shiporder-")] | //div[starts-with(@data-testid, "node-source-fx-shiporder-")]`, + }, +} as const; + +// ─── LocatorDiff contribution ───────────────────────────────────────────────── + +/** + * ExTester custom page-objects locator contribution. + * + * All selectors that require a `By` object at runtime are registered here. + * This file is compiled to `out/pageObjects/locators.js` and loaded by + * ExTester's `customPageObjects.locatorsPath` option before tests run. + * + * Page object classes extend `AbstractElement` and call + * `MyClass.locators.Kaoto*.*` to access these selectors. + */ +export const locators: LocatorDiff['locators'] = { + KaotoEditor: { + constructor: By.xpath(`//div[@data-testid='visualization-empty-state']`), + emptyCanvas: By.xpath(`//div[@data-testid='visualization-empty-state']`), + topology: By.xpath(`//div[@data-test-id='topology']`), + designTabLink: By.css(`a[data-testid="design-tab"]`), + closeSideBar: By.xpath(`//button[@data-testid='close-side-bar']`), + propertyPanelCard: By.className(`pf-v6-c-card`), + }, + + EditorTabs: { + constructor: By.className(`pf-v6-c-tabs__list`), + tabsList: By.className(`pf-v6-c-tabs__list`), + activeTab: By.xpath(`//button[@aria-selected='true']`), + tabText: By.className(`pf-v6-c-tabs__item-text`), + }, + + KaotoContextMenu: { + constructor: By.className(`pf-topology-context-menu__c-dropdown__menu`), + menu: By.className(`pf-topology-context-menu__c-dropdown__menu`), + replaceItem: By.xpath(`//\*[@data-testid='context-menu-item-replace']`), + deleteItem: By.css(`li[data-testid="context-menu-item-delete"]`), + }, + + StepToolbar: { + constructor: By.xpath(`//div[@data-testid='step-toolbar']`), + toolbar: By.xpath(`//div[@data-testid='step-toolbar']`), + resetViewButton: By.css(`button[id='reset-view']`), + actionConfirmModal: By.xpath(`//div[@data-ouia-component-id='ActionConfirmationModal']`), + }, + + KaotoCatalog: { + constructor: By.xpath(`//input[@placeholder='Filter by name, description or tag']`), + filterInput: By.xpath(`//input[@placeholder='Filter by name, description or tag']`), + filterTextInput: By.className(`pf-v6-c-text-input-group__text-input`), + dslListButton: By.xpath(`//button[@data-testid='dsl-list-btn']`), + }, + + CatalogModal: { + constructor: By.xpath(`//button[@id='topology-control-bar-catalog-button']`), + catalogButton: By.xpath(`//button[@id='topology-control-bar-catalog-button']`), + window: By.xpath(`//div[@data-ouia-component-id='CatalogModal']`), + closeButton: By.xpath(`//button[@data-ouia-component-id='CatalogModal-ModalBoxCloseButton']`), + listButton: By.xpath(`//button[@id='toggle-layout-button-List']`), + galleryButton: By.xpath(`//button[@id='toggle-layout-button-Gallery']`), + list: By.className(`pf-v6-c-data-list`), + gallery: By.className(`pf-v6-l-gallery pf-m-gutter`), + listItemRow: By.className(`pf-v6-c-data-list__item-row`), + listItemProvider: By.className(`catalog-data-list-item__provider`), + providerFilterDropdown: By.xpath(`//button[@data-testid='provider-filter-toggle']`), + providerSelectMenu: By.id(`providers-select-menu`), + providerMenuListItem: By.className(`pf-v6-c-menu__list-item`), + }, + + BeansEditor: { + constructor: By.className(`metadata-editor-modal-list-view`), + listView: By.className(`metadata-editor-modal-list-view`), + detailsView: By.className(`metadata-editor-modal-details-view`), + }, + + DataMapperEditor: { + constructor: By.className(`pf-v6-l-bullseye datamapper-howto`), + howTo: By.className(`pf-v6-l-bullseye datamapper-howto`), + openEditorButton: By.css(`button[title="Click to launch the Kaoto DataMapper editor"]`), + attachSchemaModal: By.xpath(`//div[@data-testid='attach-schema-modal']`), + attachSchemaFileButton: By.xpath(`//button[@data-testid='attach-schema-modal-btn-file']`), + attachSchemaAttachButton: By.xpath(`//button[@data-testid='attach-schema-modal-btn-attach']`), + sourceField: By.xpath( + `//div[starts-with(@data-testid, "node-source-field-shiporder-")] | //div[starts-with(@data-testid, "node-source-fx-shiporder-")]`, + ), + }, +}; diff --git a/it-tests/settings/CatalogURLSettings.test.ts b/it-tests/settings/CatalogURLSettings.test.ts index 548ebce3..e62cbba1 100644 --- a/it-tests/settings/CatalogURLSettings.test.ts +++ b/it-tests/settings/CatalogURLSettings.test.ts @@ -17,18 +17,14 @@ import { ActivityBar, after, before, - By, EditorView, InputBox, - Key, ModalDialog, NotificationType, StatusBar, TextSetting, - until, VSBrowser, WebDriver, - WebElement, WebView, Workbench, } from 'vscode-extension-tester'; @@ -36,6 +32,7 @@ import { checkTopologyLoaded, closeEditor, openAndSwitchToKaotoFrame, resetUserS import { join } from 'path'; import { rmSync } from 'fs'; import { expect } from 'chai'; +import { CatalogModal } from '../pageObjects'; describe('User Settings', function () { this.timeout(240_000); @@ -47,28 +44,6 @@ describe('User Settings', function () { let driver: WebDriver; let kaotoWebview: WebView; - const locators = { - KaotoView: { - catalogButton: By.xpath(`//button[@id='topology-control-bar-catalog-button']`), - catalog: { - window: By.xpath(`//div[@data-ouia-component-id='CatalogModal']`), - list: By.className('pf-v6-c-data-list'), - gallery: By.className('pf-v6-l-gallery pf-m-gutter'), - listItem: By.className('pf-v6-c-data-list__item-row'), - listItemProvider: By.className('catalog-data-list-item__provider'), - listButton: By.xpath(`//button[@id='toggle-layout-button-List']`), - galleryButton: By.xpath(`//button[@id='toggle-layout-button-Gallery']`), - closeButton: By.xpath(`//button[@data-ouia-component-id='CatalogModal-ModalBoxCloseButton']`), - }, - catalogProviderSelector: { - dropdown: By.xpath(`//button[@data-testid='provider-filter-toggle']`), - menu: By.id('providers-select-menu'), - menuItem: By.className('pf-v6-c-menu__list-item'), - selector: (label: string) => By.xpath(`//li[@data-testid='providers-select-item-${label}']`), - }, - }, - }; - before(function () { driver = VSBrowser.instance.driver; }); @@ -227,18 +202,19 @@ describe('User Settings', function () { kaotoWebview = (await switchToKaotoFrame(driver, false)).kaotoWebview; await checkTopologyLoaded(driver, 15_000); - const catalogWindow = await openCatalogInListView(); + const catalogWindow = await CatalogModal.open(driver, 15_000).then((w) => CatalogModal.switchToListView(driver, w, 10_000)); - const providers = await getCatalogProvidersList(catalogWindow); + const providers = await CatalogModal.getProviderNames(driver, catalogWindow); expect(providers).to.not.contain('Red Hat'); expect(providers).to.contain('Community'); // check first component does not contain 'Red Hat' flag - const provider = await getFirstCatalogItemProvider(catalogWindow); + const provider = await CatalogModal.getFirstItemProvider(catalogWindow); expect(provider).to.not.be.equal('Red Hat'); // switch catalog window back to gallery view - await switchBackCatalogToGalleryViewAndClose(catalogWindow); + await CatalogModal.switchToGalleryView(driver, catalogWindow); + await CatalogModal.close(catalogWindow); }); it(`Select 'redhat' catalog and check new components are available`, async function () { @@ -269,102 +245,24 @@ describe('User Settings', function () { kaotoWebview = (await switchToKaotoFrame(driver, false)).kaotoWebview; await checkTopologyLoaded(driver, 15_000); - const catalogWindow = await openCatalogInListView(); + const catalogWindow = await CatalogModal.open(driver, 15_000).then((w) => CatalogModal.switchToListView(driver, w, 10_000)); - const providers = await getCatalogProvidersList(catalogWindow); + const providers = await CatalogModal.getProviderNames(driver, catalogWindow); expect(providers).to.contain('Red Hat'); expect(providers).to.contain('Community'); - await clickCatalogProviderCheckboxItem(catalogWindow, 'Community'); // uncheck 'Community' provider + await CatalogModal.toggleProvider(driver, catalogWindow, 'Community'); // uncheck 'Community' provider // check first component contains 'Red Hat' flag - const provider = await getFirstCatalogItemProvider(catalogWindow); + const provider = await CatalogModal.getFirstItemProvider(catalogWindow); expect(provider).to.be.equal('Red Hat'); // switch catalog window back to gallery view - await switchBackCatalogToGalleryViewAndClose(catalogWindow); + await CatalogModal.switchToGalleryView(driver, catalogWindow); + await CatalogModal.close(catalogWindow); }); }); - async function clickCatalogProviderDropdown(open: boolean, catalogWindow: WebElement) { - const dropdown = await catalogWindow.findElement(locators.KaotoView.catalogProviderSelector.dropdown); - await dropdown.click(); - await driver.sleep(1_000); // time to reflect changes in DOM - - if (open) { - await driver.wait(until.elementLocated(locators.KaotoView.catalogProviderSelector.menu), 5_000); - } else { - try { - await driver.wait(until.elementLocated(locators.KaotoView.catalogProviderSelector.menu), 5_000); - } catch (error) { - if (error instanceof Error && error.name !== 'TimeoutError') { - throw new Error(error.message); - } - } - } - } - - async function clickCatalogProviderCheckboxItem(catalogWindow: WebElement, provider: string) { - await clickCatalogProviderDropdown(true, catalogWindow); // open menu - - const menu = await catalogWindow.findElement(locators.KaotoView.catalogProviderSelector.menu); - const item = await menu.findElement(locators.KaotoView.catalogProviderSelector.selector(provider)); - await item.click(); - - await clickCatalogProviderDropdown(false, catalogWindow); // close menu - } - - async function getCatalogProvidersList(catalogWindow: WebElement): Promise { - await clickCatalogProviderDropdown(true, catalogWindow); // open menu - - const menu = await catalogWindow.findElement(locators.KaotoView.catalogProviderSelector.menu); - const items = await menu.findElements(locators.KaotoView.catalogProviderSelector.menuItem); - const labels = await Promise.all(items.map(async (item) => await item.getText())); - - await clickCatalogProviderDropdown(false, catalogWindow); // close menu - return labels; - } - - async function getFirstCatalogItemProvider(catalogWindow: WebElement): Promise { - const listItem = await catalogWindow.findElement(locators.KaotoView.catalog.list).findElement(locators.KaotoView.catalog.listItem); - const provider = await listItem.findElement(locators.KaotoView.catalog.listItemProvider).getText(); - return provider; - } - - async function openCatalogInListView(): Promise { - // wait for the catalog button to be visible and clickable - const catalog = await driver.wait(until.elementLocated(locators.KaotoView.catalogButton), 10_000, 'Catalog button was not located'); - await driver.wait(until.elementIsVisible(catalog), 10_000, 'Catalog button was not visible'); - await driver.wait(until.elementIsEnabled(catalog), 10_000, 'Catalog button was not enabled'); - await catalog.click(); - - // wait catalog modal dialog is open - await driver.wait(until.elementLocated(locators.KaotoView.catalog.window), 15_000); - const catalogWindow = await driver.findElement(locators.KaotoView.catalog.window); - - // switch to list view - await catalogWindow.findElement(locators.KaotoView.catalog.listButton).click(); - await driver.wait(until.elementLocated(locators.KaotoView.catalog.list), 10_000); - - return catalogWindow; - } - - async function switchBackCatalogToGalleryViewAndClose(catalogWindow: WebElement): Promise { - try { - await catalogWindow.findElement(locators.KaotoView.catalog.galleryButton).click(); - await driver.wait(until.elementLocated(locators.KaotoView.catalog.gallery), 5_000); - } catch (error) { - // it can happen that because of hover text for list view button - // the gallery view button is overlapped and it is not clickable at the moment - await driver.actions().sendKeys(Key.ENTER).perform(); // WORKAROUND - await catalogWindow.findElement(locators.KaotoView.catalog.galleryButton).click(); - await driver.wait(until.elementLocated(locators.KaotoView.catalog.gallery), 5_000); - } - - // close catalog view - await catalogWindow.findElement(locators.KaotoView.catalog.closeButton).click(); - } - async function clickCatalogStatusBarItem(): Promise { const statusBar = new StatusBar(); const catalogItem = await driver.wait( diff --git a/it-tests/settings/NodeLabelSettings.test.ts b/it-tests/settings/NodeLabelSettings.test.ts index b4b4e55e..3858aa52 100644 --- a/it-tests/settings/NodeLabelSettings.test.ts +++ b/it-tests/settings/NodeLabelSettings.test.ts @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ActivityBar, after, before, By, ComboSetting, until, VSBrowser, WebDriver, WebView, Workbench } from 'vscode-extension-tester'; +import { ActivityBar, after, before, ComboSetting, VSBrowser, WebDriver, WebView, Workbench } from 'vscode-extension-tester'; import { checkTopologyLoaded, closeEditor, openAndSwitchToKaotoFrame, resetUserSettings } from '../Util'; import { join } from 'path'; import { expect } from 'chai'; +import { KaotoCanvas } from '../pageObjects'; describe('User Settings', function () { this.timeout(90_000); @@ -27,12 +28,6 @@ describe('User Settings', function () { let driver: WebDriver; let kaotoWebview: WebView; - const locators = { - TimerComponent: { - label: `//*[name()='g' and @data-testid='custom-node__route.from']//*[contains(@class,'custom-node__label__text')]`, - }, - }; - before(async function () { driver = VSBrowser.instance.driver; @@ -73,7 +68,7 @@ describe('User Settings', function () { it(`Check 'id' Node Label is used instead of default 'description'`, async function () { this.timeout(60_000); - const timer = await driver.wait(until.elementLocated(By.xpath(locators.TimerComponent.label)), 10_000); + const timer = await KaotoCanvas.findNodeLabelText(driver, 'custom-node__route.from', 10_000); const label = await timer.getText(); expect(label.split('\n')).to.contains('timerID'); diff --git a/it-tests/views/03_IntegrationsViewNewFile.test.ts b/it-tests/views/03_IntegrationsViewNewFile.test.ts index 356a5423..08475ab8 100644 --- a/it-tests/views/03_IntegrationsViewNewFile.test.ts +++ b/it-tests/views/03_IntegrationsViewNewFile.test.ts @@ -21,10 +21,8 @@ import { afterEach, before, beforeEach, - By, EditorView, InputBox, - until, ViewControl, ViewPanelActionDropdown, ViewSection, @@ -41,6 +39,7 @@ import { openResourcesAndWaitForActivation, switchToKaotoFrame, } from '../Util'; +import { KaotoCanvas } from '../pageObjects'; describe('Integrations View', function () { this.timeout(240_000); @@ -177,14 +176,6 @@ describe('Integrations View', function () { await switchToKaotoAndCheckIntegrationType(PIPE_FILE, 'Pipe', 'timer-source'); }); - async function checkStepWithNodeLabelPresent(nodeLabel: string, timeout: number = 10_000) { - await driver.wait( - until.elementLocated(By.xpath(`//*[name()='g' and @data-nodelabel='${nodeLabel}']`)), - timeout, - `'${nodeLabel}' was not found in current topology.`, - ); - } - async function switchToKaotoAndCheckIntegrationType(filename: string, type: string, nodeLabel: string): Promise { await driver.wait( async function () { @@ -195,7 +186,7 @@ describe('Integrations View', function () { ); const { kaotoWebview } = await switchToKaotoFrame(driver, true); - await checkStepWithNodeLabelPresent(nodeLabel); + await KaotoCanvas.waitForNodeByLabel(driver, nodeLabel); await kaotoWebview.switchBack(); } }); diff --git a/it-tests/vscode-settings-minikube.json b/it-tests/vscode-settings-minikube.json index 33dde912..5be86334 100644 --- a/it-tests/vscode-settings-minikube.json +++ b/it-tests/vscode-settings-minikube.json @@ -12,6 +12,6 @@ "container.image-push=true", "--trait", "container.image-pull-policy=IfNotPresent", - "--camel-version=4.20.0" + "--camel-version=4.21.0" ] } diff --git a/it-tests/vscode-settings.json b/it-tests/vscode-settings.json index 78a8020a..c39a00d9 100644 --- a/it-tests/vscode-settings.json +++ b/it-tests/vscode-settings.json @@ -2,7 +2,7 @@ "git.openRepositoryInParentFolders": "never", "redhat.telemetry.enabled": false, "workbench.secondarySideBar.defaultVisibility": "hidden", - "window.zoomLevel": -1.5, + "window.zoomLevel": -3, "window.openFoldersInNewWindow": "off", "workbench.editor.useModal": "off" } diff --git a/package.json b/package.json index cdf10bdc..5f4c926a 100644 --- a/package.json +++ b/package.json @@ -28,15 +28,15 @@ "watch": "webpack --env dev", "run:webmode": "yarn vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --open-devtools ./resources", "test:unit": "yarn build:test:unit && vscode-test", - "test:it": "yarn build:test:it && extest setup-and-run --yarn --uninstall_extension --extensions_dir ./test-resources --open_resource './test Fixture with speci@l chars' 'out/**/*.test.js'", - "setup:test:it:with-prebuilt-vsix": "yarn build:test:it && extest get-vscode && extest get-chromedriver && extest install-vsix --vsix_file vscode-kaoto-$npm_package_version.vsix --extensions_dir ./test-resources && extest run-tests --uninstall_extension --extensions_dir ./test-resources --open_resource './test Fixture with speci@l chars'", + "test:it": "yarn build:test:it && extest setup-and-run", + "setup:test:it:with-prebuilt-vsix": "yarn build:test:it && extest get-vscode && extest get-chromedriver && extest install-vsix --vsix_file vscode-kaoto-$npm_package_version.vsix --extensions_dir ./test-resources && extest run-tests --uninstall_extension --extensions_dir ./test-resources --open_resource './test Fixture with speci@l chars' --custom_page_objects './out/pageObjects/locators.js'", "test:it:with-prebuilt-vsix": "yarn setup:test:it:with-prebuilt-vsix --code_settings ./it-tests/vscode-settings.json 'out/**/*.test.js'", "test:it:with-prebuilt-vsix:minikube": "yarn setup:test:it:with-prebuilt-vsix --code_settings ./it-tests/vscode-settings-minikube.json 'out/**/*.test.js'", "test:it:clean": "rimraf ./test-resources && rimraf ./out && rimraf *.vsix" }, "dependencies": { - "@kaoto/camel-catalog": "^0.5.5", - "@kaoto/kaoto": "2.11.0", + "@kaoto/camel-catalog": "^0.8.0", + "@kaoto/kaoto": "2.12.0-RC1", "@kie-tools-core/backend": "10.0.0", "@kie-tools-core/editor": "10.0.0", "@kie-tools-core/i18n": "10.0.0", @@ -1354,7 +1354,7 @@ "ts-loader": "9.4.4", "tsconfig-paths-webpack-plugin": "^4.2.0", "typescript": "^5.9.3", - "vscode-extension-tester": "^8.23.0", + "vscode-extension-tester": "^8.24.0", "web-worker": "^1.5.0", "webpack": "5.108.3", "webpack-cli": "7.1.0", diff --git a/src/constants.ts b/src/constants.ts index ee32e104..7cd8dcd5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,6 @@ // ─── Default Values ────────────────────────────────────────────────────────── -export const DEFAULT_CAMEL_VERSION_FALLBACK: string = '4.20.0'; +export const DEFAULT_CAMEL_VERSION_FALLBACK: string = '4.21.0'; // ─── Editor Type ───────────────────────────────────────────────────────────── diff --git a/src/services/openapi-import.service.ts b/src/services/openapi-import.service.ts index 56647d61..889cc5d8 100644 --- a/src/services/openapi-import.service.ts +++ b/src/services/openapi-import.service.ts @@ -39,12 +39,7 @@ export type RestMethods = 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete'; * Union type of all REST method definition types. */ export type RestMethodDefinitions = - | Flatten - | Flatten - | Flatten - | Flatten - | Flatten - | Flatten; + Flatten | Flatten | Flatten | Flatten | Flatten | Flatten; /** * Array of supported REST DSL verbs. diff --git a/test Fixture with speci@l chars/kaoto-view/example-tests/folderA/folderAA/test/citrus-application.properties b/test Fixture with speci@l chars/kaoto-view/example-tests/folderA/folderAA/test/citrus-application.properties index 3cb60089..571ab773 100644 --- a/test Fixture with speci@l chars/kaoto-view/example-tests/folderA/folderAA/test/citrus-application.properties +++ b/test Fixture with speci@l chars/kaoto-view/example-tests/folderA/folderAA/test/citrus-application.properties @@ -1,5 +1,5 @@ # Camel version used by Citrus -citrus.camel.jbang.version=4.20.0 +citrus.camel.jbang.version=4.21.0 # Enable dump of Camel JBang integration output citrus.camel.jbang.dump.integration.output=true diff --git a/test Fixture with speci@l chars/kaoto-view/example-tests/test/citrus-application.properties b/test Fixture with speci@l chars/kaoto-view/example-tests/test/citrus-application.properties index 3cb60089..571ab773 100644 --- a/test Fixture with speci@l chars/kaoto-view/example-tests/test/citrus-application.properties +++ b/test Fixture with speci@l chars/kaoto-view/example-tests/test/citrus-application.properties @@ -1,5 +1,5 @@ # Camel version used by Citrus -citrus.camel.jbang.version=4.20.0 +citrus.camel.jbang.version=4.21.0 # Enable dump of Camel JBang integration output citrus.camel.jbang.dump.integration.output=true diff --git a/test Fixture with speci@l chars/kaoto-view/routes.code-workspace b/test Fixture with speci@l chars/kaoto-view/routes.code-workspace index 20bb4286..47ee5d02 100644 --- a/test Fixture with speci@l chars/kaoto-view/routes.code-workspace +++ b/test Fixture with speci@l chars/kaoto-view/routes.code-workspace @@ -11,7 +11,8 @@ "git.openRepositoryInParentFolders": "never", "redhat.telemetry.enabled": false, "workbench.secondarySideBar.defaultVisibility": "hidden", - "window.zoomLevel": -1.5, + "window.zoomLevel": -3, "window.openFoldersInNewWindow": "off", + "workbench.editor.useModal": "off", }, } diff --git a/yarn.lock b/yarn.lock index 8f4790f2..f336086d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -891,10 +891,10 @@ __metadata: languageName: node linkType: hard -"@kaoto/camel-catalog@npm:^0.5.5": - version: 0.5.5 - resolution: "@kaoto/camel-catalog@npm:0.5.5" - checksum: 10/019977e4072b2ef4993fc9a8a30dc4e33e74c754dc6d9978a4dbeadb0721bed373a346bf842a1ed6d1810b4df6dc11647cf51cae9bd598f5685e703c42628c3a +"@kaoto/camel-catalog@npm:^0.8.0": + version: 0.8.0 + resolution: "@kaoto/camel-catalog@npm:0.8.0" + checksum: 10/c2c24a42e80982d4c00403125616ee6a4dbe914f63d22a9f0b1bef9894f6d2a720d1808486ce32a57f190815142ea2f2c1164748f286b33eff45fadae2811534 languageName: node linkType: hard @@ -916,9 +916,9 @@ __metadata: languageName: node linkType: hard -"@kaoto/kaoto@npm:2.11.0": - version: 2.11.0 - resolution: "@kaoto/kaoto@npm:2.11.0" +"@kaoto/kaoto@npm:2.12.0-RC1": + version: 2.12.0-RC1 + resolution: "@kaoto/kaoto@npm:2.12.0-RC1" dependencies: "@carbon/icons-react": "npm:^11.67.0" "@carbon/react": "npm:^1.102.0" @@ -964,7 +964,7 @@ __metadata: monaco-yaml: ^5.5.1 react: ^19.2.4 react-dom: ^19.2.4 - checksum: 10/32737c27e86a276fc4180cc4f92d0b39dcd3d5ba99cc5f84e5843a3b8a94bd0d6df8f0b55fc051b29feb6222089c98ccd96025467c2588a58fbfb0dfeb4f650e + checksum: 10/7ccfb06fc8317ce9e8fab1b89cf1a14ffa6890a4dce9a7dc94709b4a25ba464050fbda3f2909f4d78d1a7be936da209585867a8b1607eba8c5ae08c7282db78b languageName: node linkType: hard @@ -1603,29 +1603,28 @@ __metadata: languageName: node linkType: hard -"@redhat-developer/locators@npm:^1.20.0": - version: 1.20.0 - resolution: "@redhat-developer/locators@npm:1.20.0" +"@redhat-developer/locators@npm:^1.21.0": + version: 1.21.0 + resolution: "@redhat-developer/locators@npm:1.21.0" peerDependencies: "@redhat-developer/page-objects": ">=1.0.0" selenium-webdriver: ">=4.6.1" - checksum: 10/c8de945ee0f693e895ceaec249dc560a644cf9842f3fd0135e8989231c09a9f267dad6ca5f4bbde58aad541a10bce0b309a9f5ce997fe7207341bccc719cf374 + checksum: 10/5a8dd4b299583e0a777017a086f324b3f91d22fe9134cb93dd445414f64cab316137e03050d0964e73ccc1fda8e3b52135d89b8da2aab5e85ef79a432b89b6bd languageName: node linkType: hard -"@redhat-developer/page-objects@npm:^1.20.0": - version: 1.20.0 - resolution: "@redhat-developer/page-objects@npm:1.20.0" +"@redhat-developer/page-objects@npm:^1.21.0": + version: 1.21.0 + resolution: "@redhat-developer/page-objects@npm:1.21.0" dependencies: - clipboardy: "npm:^5.3.1" + clipboardy: "npm:^5.3.2" clone-deep: "npm:^4.0.1" compare-versions: "npm:^6.1.1" - fs-extra: "npm:^11.3.4" - type-fest: "npm:^4.41.0" + fs-extra: "npm:^11.4.0" peerDependencies: selenium-webdriver: ">=4.6.1" typescript: ">=4.6.2" - checksum: 10/52ce8129191e0c218ee4be22bc98dc54edb4da6aff3360f014e38403b4a846de10659d6a493b9d9f5a849fa57c0a4a52c05bb44e95b2086e75ab0f252925042f + checksum: 10/70326825567a0e2842e0e5692881077cbc498a0c18cb93406ca8dd305c1b7d9920ae4e0e6a204a2e150b8c2078b184a3f5b3808e725fd123c90328bbd25af86e languageName: node linkType: hard @@ -1828,10 +1827,10 @@ __metadata: languageName: node linkType: hard -"@sindresorhus/is@npm:^7.0.1": - version: 7.0.1 - resolution: "@sindresorhus/is@npm:7.0.1" - checksum: 10/d096d98268400c698633e620616b56dec822fd4fc55299caaf6c985e9242215c2c99f81da5701b783295c3019f6298a8299535eb9c2fcb69684fb9b5882aaedd +"@sindresorhus/is@npm:^8.0.0": + version: 8.1.0 + resolution: "@sindresorhus/is@npm:8.1.0" + checksum: 10/4511f60fa5f21c28694eabff61b4c2fd7b65d7ac895a9330a2e862fd35e880b9b10393c6ccb44c94fa0a1d2320044ac413277ffbdb33e75cffcb0fca8fa56b76 languageName: node linkType: hard @@ -2428,10 +2427,10 @@ __metadata: languageName: node linkType: hard -"@types/http-cache-semantics@npm:^4.0.4": - version: 4.0.4 - resolution: "@types/http-cache-semantics@npm:4.0.4" - checksum: 10/a59566cff646025a5de396d6b3f44a39ab6a74f2ed8150692e0f31cc52f3661a68b04afe3166ebe0d566bd3259cb18522f46e949576d5204781cd6452b7fe0c5 +"@types/http-cache-semantics@npm:^4.2.0": + version: 4.2.0 + resolution: "@types/http-cache-semantics@npm:4.2.0" + checksum: 10/01ea0dc9c1852267f5f9a0190846ac158707994b8e325bca190385ec97aa773d4a99cd333e22e838bde3c9aba59e2b5a6ac399b494c203587c6d8f28d21d6326 languageName: node linkType: hard @@ -2560,13 +2559,13 @@ __metadata: languageName: node linkType: hard -"@types/selenium-webdriver@npm:^4.35.5": - version: 4.35.5 - resolution: "@types/selenium-webdriver@npm:4.35.5" +"@types/selenium-webdriver@npm:^4.35.6": + version: 4.35.6 + resolution: "@types/selenium-webdriver@npm:4.35.6" dependencies: "@types/node": "npm:*" "@types/ws": "npm:*" - checksum: 10/87bfc4948739ab2b69d6c133dfbe7cbd3134191e5c618c571b6976bf4f3cb74ec5b493a17f53c6a80acff5f6aafb22c5e5b6a12dcf7b42486a0831316207bf85 + checksum: 10/9b698c7d88eaaa2892a6d3a95d08ec6991ed43ed3cbdd669da1639836d3d0eed97843dd8ac052de7ce1ca610992e92ba198c1ecf066f9690470501cf71664a15 languageName: node linkType: hard @@ -3018,49 +3017,6 @@ __metadata: languageName: node linkType: hard -"@vscode/vsce@npm:^3.7.1": - version: 3.7.1 - resolution: "@vscode/vsce@npm:3.7.1" - dependencies: - "@azure/identity": "npm:^4.1.0" - "@secretlint/node": "npm:^10.1.2" - "@secretlint/secretlint-formatter-sarif": "npm:^10.1.2" - "@secretlint/secretlint-rule-no-dotenv": "npm:^10.1.2" - "@secretlint/secretlint-rule-preset-recommend": "npm:^10.1.2" - "@vscode/vsce-sign": "npm:^2.0.0" - azure-devops-node-api: "npm:^12.5.0" - chalk: "npm:^4.1.2" - cheerio: "npm:^1.0.0-rc.9" - cockatiel: "npm:^3.1.2" - commander: "npm:^12.1.0" - form-data: "npm:^4.0.0" - glob: "npm:^11.0.0" - hosted-git-info: "npm:^4.0.2" - jsonc-parser: "npm:^3.2.0" - keytar: "npm:^7.7.0" - leven: "npm:^3.1.0" - markdown-it: "npm:^14.1.0" - mime: "npm:^1.3.4" - minimatch: "npm:^3.0.3" - parse-semver: "npm:^1.1.1" - read: "npm:^1.0.7" - secretlint: "npm:^10.1.2" - semver: "npm:^7.5.2" - tmp: "npm:^0.2.3" - typed-rest-client: "npm:^1.8.4" - url-join: "npm:^4.0.1" - xml2js: "npm:^0.5.0" - yauzl: "npm:^2.3.1" - yazl: "npm:^2.2.2" - dependenciesMeta: - keytar: - optional: true - bin: - vsce: vsce - checksum: 10/33fa4c24821577e4ee6e89f075b1530063fe3564300e46a14708ed283ec0a01f7626ebc859b8e6fa2f3b728de92fb9748c7124faf09d6b43100fc700484b587b - languageName: node - linkType: hard - "@vscode/vsce@npm:^3.9.2": version: 3.9.2 resolution: "@vscode/vsce@npm:3.9.2" @@ -4040,6 +3996,32 @@ __metadata: languageName: node linkType: hard +"c8@npm:^12.0.0": + version: 12.0.0 + resolution: "c8@npm:12.0.0" + dependencies: + "@bcoe/v8-coverage": "npm:^1.0.1" + "@istanbuljs/schema": "npm:^0.1.3" + find-up: "npm:^5.0.0" + foreground-child: "npm:^3.1.1" + istanbul-lib-coverage: "npm:^3.2.0" + istanbul-lib-report: "npm:^3.0.1" + istanbul-reports: "npm:^3.1.6" + test-exclude: "npm:^8.0.0" + v8-to-istanbul: "npm:^9.0.0" + yargs: "npm:^18.0.0" + yargs-parser: "npm:^21.1.1" + peerDependencies: + monocart-coverage-reports: ^2 + peerDependenciesMeta: + monocart-coverage-reports: + optional: true + bin: + c8: bin/c8.js + checksum: 10/cb4b08b8170b9004766f2e51bbe41916454c0952ac5f811a11d98250fc1539704b92b65dfe6b76f1319d64d1111596b7f04fe088f6df69942345960859a65d49 + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.2 resolution: "cacache@npm:18.0.2" @@ -4067,18 +4049,18 @@ __metadata: languageName: node linkType: hard -"cacheable-request@npm:^13.0.12": - version: 13.0.12 - resolution: "cacheable-request@npm:13.0.12" +"cacheable-request@npm:^13.0.18": + version: 13.0.19 + resolution: "cacheable-request@npm:13.0.19" dependencies: - "@types/http-cache-semantics": "npm:^4.0.4" + "@types/http-cache-semantics": "npm:^4.2.0" get-stream: "npm:^9.0.1" http-cache-semantics: "npm:^4.2.0" - keyv: "npm:^5.5.3" + keyv: "npm:^5.6.0" mimic-response: "npm:^4.0.0" - normalize-url: "npm:^8.1.0" - responselike: "npm:^3.0.0" - checksum: 10/7923a29f6283cacfb320edbabbe0ddd1f9b99233eea5494ff1f86eafc6f5a14faf784055df189999f0cc758f9abc38f95181ec1985472b35d04217cbf4c555bb + normalize-url: "npm:^8.1.1" + responselike: "npm:^4.0.2" + checksum: 10/d4a942cec534ebc124b25fd3881774be45ab415502597da8d928ef81aef41ed75c7c003516fa01743041715055ed585fb44ba7856e4ea252cbc217454c1d40c5 languageName: node linkType: hard @@ -4327,6 +4309,13 @@ __metadata: languageName: node linkType: hard +"chunk-data@npm:^0.1.0": + version: 0.1.0 + resolution: "chunk-data@npm:0.1.0" + checksum: 10/3ab3f7b7da54481bafed8f83f2d9f844b8f43f37c01f7b8079c440e10a5373b6572023743472f49304e20e6b750c8c830c42e3eff3413c073c4ba5448d64eed7 + languageName: node + linkType: hard + "classnames@npm:2.5.1, classnames@npm:^2.3.1": version: 2.5.1 resolution: "classnames@npm:2.5.1" @@ -4375,9 +4364,9 @@ __metadata: languageName: node linkType: hard -"clipboardy@npm:^5.3.1": - version: 5.3.1 - resolution: "clipboardy@npm:5.3.1" +"clipboardy@npm:^5.3.2": + version: 5.3.2 + resolution: "clipboardy@npm:5.3.2" dependencies: clipboard-image: "npm:^0.1.0" execa: "npm:^9.6.1" @@ -4385,7 +4374,7 @@ __metadata: is-wsl: "npm:^3.1.0" is64bit: "npm:^2.0.0" powershell-utils: "npm:^0.2.0" - checksum: 10/027325dd58b6f10e84704116a461212816b420b74b44fe1c6d518a358e73525273a72d8848d358ac73ee229a35309215e803ae5f1f539c5fe51564564840ebe0 + checksum: 10/9ae0f091179372a82ca850f9b638577d4816632cec5e4647d1fc984a7af54ac96246e88e40183d40094b1147b408f5134a5f8649b87dad431e7e1174058db349 languageName: node linkType: hard @@ -6380,15 +6369,6 @@ __metadata: languageName: node linkType: hard -"fd-slicer@npm:~1.1.0": - version: 1.1.0 - resolution: "fd-slicer@npm:1.1.0" - dependencies: - pend: "npm:~1.2.0" - checksum: 10/db3e34fa483b5873b73f248e818f8a8b59a6427fd8b1436cd439c195fdf11e8659419404826059a642b57d18075c856d06d6a50a1413b714f12f833a9341ead3 - languageName: node - linkType: hard - "fdir@npm:^6.4.3": version: 6.4.3 resolution: "fdir@npm:6.4.3" @@ -6628,13 +6608,6 @@ __metadata: languageName: node linkType: hard -"form-data-encoder@npm:^4.0.2": - version: 4.0.2 - resolution: "form-data-encoder@npm:4.0.2" - checksum: 10/71a00a1e954267f8b87d4301936dda7177cfe18714a92930c87ebc132acb8b19ed12e9b9f8ad47af52c1cc582fd45c1771f66f850a6d319147731160f6b9b3fa - languageName: node - linkType: hard - "form-data@npm:^4.0.0": version: 4.0.0 resolution: "form-data@npm:4.0.0" @@ -6695,36 +6668,25 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.2.0": - version: 11.2.0 - resolution: "fs-extra@npm:11.2.0" - dependencies: - graceful-fs: "npm:^4.2.0" - jsonfile: "npm:^6.0.1" - universalify: "npm:^2.0.0" - checksum: 10/0579bf6726a4cd054d4aa308f10b483f52478bb16284f32cf60b4ce0542063d551fca1a08a2af365e35db21a3fa5a06cf2a6ed614004b4368982bc754cb816b3 - languageName: node - linkType: hard - -"fs-extra@npm:^11.3.4": - version: 11.3.4 - resolution: "fs-extra@npm:11.3.4" +"fs-extra@npm:^11.3.6": + version: 11.3.6 + resolution: "fs-extra@npm:11.3.6" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/1b8deea9c540a2efe63c750bc9e1ba6238115579d1571d67fe8fb58e3fb6df19aba29fd4ebb81217cf0bf5bce0df30ca68dbc3e06f6652b856edd385ce0ff649 + checksum: 10/34a981697b199002ea3b85c3483bcf353637d33ec4c922b136a8d8a597faa0495638d69b20c181448be041c31778c2bb7f7799d589819edf33da50f75b1abde9 languageName: node linkType: hard -"fs-extra@npm:^11.3.6": - version: 11.3.6 - resolution: "fs-extra@npm:11.3.6" +"fs-extra@npm:^11.4.0": + version: 11.4.0 + resolution: "fs-extra@npm:11.4.0" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/34a981697b199002ea3b85c3483bcf353637d33ec4c922b136a8d8a597faa0495638d69b20c181448be041c31778c2bb7f7799d589819edf33da50f75b1abde9 + checksum: 10/a1bd91076fe9de9114bd037a433f1f498857eb3f0cf778e647fd00dc5984374dee5e088854de3d6ca77cd9f11554a85f6f016a6004d0838bbc7cd722e33601ce languageName: node linkType: hard @@ -6964,22 +6926,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^11.0.0": - version: 11.0.0 - resolution: "glob@npm:11.0.0" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^4.0.1" - minimatch: "npm:^10.0.0" - minipass: "npm:^7.1.2" - package-json-from-dist: "npm:^1.0.0" - path-scurry: "npm:^2.0.0" - bin: - glob: dist/esm/bin.mjs - checksum: 10/e66939201d11ae30fe97e3364ac2be5c59d6c9bfce18ac633edfad473eb6b46a7553f6f73658f67caaf6cccc1df1ae336298a45e9021fa5695fd78754cc1603e - languageName: node - linkType: hard - "glob@npm:^13.0.3": version: 13.0.5 resolution: "glob@npm:13.0.5" @@ -7042,23 +6988,23 @@ __metadata: languageName: node linkType: hard -"got@npm:^14.6.6": - version: 14.6.6 - resolution: "got@npm:14.6.6" +"got@npm:^15.1.0": + version: 15.1.0 + resolution: "got@npm:15.1.0" dependencies: - "@sindresorhus/is": "npm:^7.0.1" + "@sindresorhus/is": "npm:^8.0.0" byte-counter: "npm:^0.1.0" cacheable-lookup: "npm:^7.0.0" - cacheable-request: "npm:^13.0.12" + cacheable-request: "npm:^13.0.18" + chunk-data: "npm:^0.1.0" decompress-response: "npm:^10.0.0" - form-data-encoder: "npm:^4.0.2" http2-wrapper: "npm:^2.2.1" - keyv: "npm:^5.5.3" - lowercase-keys: "npm:^3.0.0" - p-cancelable: "npm:^4.0.1" + keyv: "npm:^5.6.0" + lowercase-keys: "npm:^4.0.1" responselike: "npm:^4.0.2" - type-fest: "npm:^4.26.1" - checksum: 10/b11f0fa719921da9fe6304250584eb59dca737b9bc8f846b43c1305442271ece1635adb0bb5db115db63c22c11f408bb7dd73e9bb8fb310449b9ee2b366b47d5 + type-fest: "npm:^5.6.0" + uint8array-extras: "npm:^1.5.0" + checksum: 10/ca875c2f5df2d63558a62172dba3442d20074c1e94ec22362005a3f935021652d97e555bb46b63ea6566e9a00eebe695b4aaebdfe64119f1e959ce81b1f0086b languageName: node linkType: hard @@ -8173,19 +8119,6 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^4.0.1": - version: 4.0.1 - resolution: "jackspeak@npm:4.0.1" - dependencies: - "@isaacs/cliui": "npm:^8.0.2" - "@pkgjs/parseargs": "npm:^0.11.0" - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 10/b20dc0df0dbb2903e4d540ae68308ec7d1dd60944b130e867e218c98b5d77481d65ea734b6c81c812d481500076e8b3fdfccfb38fc81cb1acf165e853da3e26c - languageName: node - linkType: hard - "jest-worker@npm:^27.4.5": version: 27.5.1 resolution: "jest-worker@npm:27.5.1" @@ -8249,14 +8182,14 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" +"js-yaml@npm:^5.2.2": + version: 5.2.3 + resolution: "js-yaml@npm:5.2.3" dependencies: argparse: "npm:^2.0.1" bin: - js-yaml: bin/js-yaml.js - checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77 + js-yaml: bin/js-yaml.mjs + checksum: 10/5d2562f2d7e7bc51bd678b43d2dbc3965129ac854222c794157369c8904d249cfc78325dff8fc64becb505a2506242548c54c8fa50cdb24554bb1a557e460004 languageName: node linkType: hard @@ -8443,12 +8376,12 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^5.5.3": - version: 5.5.3 - resolution: "keyv@npm:5.5.3" +"keyv@npm:^5.6.0": + version: 5.6.0 + resolution: "keyv@npm:5.6.0" dependencies: "@keyv/serialize": "npm:^1.1.1" - checksum: 10/b8d2c354dade07cb4cb6ccffe6495c1a32a67cf9f066a9660eeaea974ab9d8b94dd568b4e71b23f217f587d3e07b558fa6e48158baea1a0bc9514bb15c1de555 + checksum: 10/f1de999fdf635d703d091981a0a3844fb20081e01d45c6743ada7d01f3a6570dc47d1882e10d0a98d31075b342db8e62b4ebe4f5bf473dcd0903064d718709c1 languageName: node linkType: hard @@ -8755,6 +8688,13 @@ __metadata: languageName: node linkType: hard +"lowercase-keys@npm:^4.0.1": + version: 4.0.1 + resolution: "lowercase-keys@npm:4.0.1" + checksum: 10/f9b3f00fc182a2cd0f751d633a454474c7b7d1aeff31ce6dd5f75200e72885b6501a47e473aeead5b096fbc3b064c26ba053712382248750797696d6aabe03e3 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.2.0 resolution: "lru-cache@npm:10.2.0" @@ -9530,15 +9470,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.0.0": - version: 10.0.1 - resolution: "minimatch@npm:10.0.1" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/082e7ccbc090d5f8c4e4e029255d5a1d1e3af37bda837da2b8b0085b1503a1210c91ac90d9ebfe741d8a5f286ece820a1abb4f61dc1f82ce602a055d461d93f3 - languageName: node - linkType: hard - "minimatch@npm:^10.2.1": version: 10.2.1 resolution: "minimatch@npm:10.2.1" @@ -10097,10 +10028,10 @@ __metadata: languageName: node linkType: hard -"normalize-url@npm:^8.1.0": - version: 8.1.0 - resolution: "normalize-url@npm:8.1.0" - checksum: 10/59b765bfe7d1768105d23a9f80716cdf1046a50a618af43eeba5e116475ff8b1a9b3e023e9c534903be436df4dac2fb9c93822cad3809fe689378945662bc8c8 +"normalize-url@npm:^8.1.1": + version: 8.1.1 + resolution: "normalize-url@npm:8.1.1" + checksum: 10/a96519b53692f33d5de19a8aa04fe9de4b8de1c126da89f1c47a24e48d457008867193cd9c19218810426ffabe190034249e1b82f0751c8992f2a9f716304ce0 languageName: node linkType: hard @@ -10351,13 +10282,6 @@ __metadata: languageName: node linkType: hard -"p-cancelable@npm:^4.0.1": - version: 4.0.1 - resolution: "p-cancelable@npm:4.0.1" - checksum: 10/64de7b0be4c8bacc006488e0e90aa66fbcceb4da4f6fb84584573145f015f9650fe6ac26470897b3e82a3b528f6c60ea276b84cc315e35c45e9f12dec062a295 - languageName: node - linkType: hard - "p-defer@npm:^1.0.0": version: 1.0.0 resolution: "p-defer@npm:1.0.0" @@ -11551,15 +11475,6 @@ __metadata: languageName: node linkType: hard -"responselike@npm:^3.0.0": - version: 3.0.0 - resolution: "responselike@npm:3.0.0" - dependencies: - lowercase-keys: "npm:^3.0.0" - checksum: 10/e0cc9be30df4f415d6d83cdede3c5c887cd4a73e7cc1708bcaab1d50a28d15acb68460ac5b02bcc55a42f3d493729c8856427dcf6e57e6e128ad05cba4cfb95e - languageName: node - linkType: hard - "responselike@npm:^4.0.2": version: 4.0.2 resolution: "responselike@npm:4.0.2" @@ -11711,12 +11626,12 @@ __metadata: languageName: node linkType: hard -"sanitize-filename@npm:^1.6.3": - version: 1.6.3 - resolution: "sanitize-filename@npm:1.6.3" +"sanitize-filename@npm:^1.6.4": + version: 1.6.4 + resolution: "sanitize-filename@npm:1.6.4" dependencies: truncate-utf8-bytes: "npm:^1.0.0" - checksum: 10/1c162e2cffa797571221c3ed9fe796fa8c6eabb0812418b52a839e4fc63ab130093eb546ec39e1b94b8d3511c0d7de81db3e67906a7e76d7a7bcb6fbab4ed961 + checksum: 10/9fb32f8ae51b1931eb4f780ac140f3fa9ccbfd8c746231d426cd5f0bc95608557e6e2540186bab81a702b98cd301b622f4a80d3263d82680bebca90a38158b38 languageName: node linkType: hard @@ -11830,15 +11745,15 @@ __metadata: languageName: node linkType: hard -"selenium-webdriver@npm:^4.41.0": - version: 4.41.0 - resolution: "selenium-webdriver@npm:4.41.0" +"selenium-webdriver@npm:^4.46.0": + version: 4.46.0 + resolution: "selenium-webdriver@npm:4.46.0" dependencies: "@bazel/runfiles": "npm:^6.5.0" jszip: "npm:^3.10.1" - tmp: "npm:^0.2.5" - ws: "npm:^8.19.0" - checksum: 10/29eaea5f8f67d173568ffbd82534ed724dbf61aa12d5f1a1e4141f2f8e3a17ec2f1a71aa386c66442936f45e74203a8e5a6f34e9086f93ca971debb8b66f9665 + tmp: "npm:^0.2.7" + ws: "npm:^8.21.0" + checksum: 10/f520f85c600bd3a4d388cf5abcb0a86a2aa073dbab9ab730e42a92aa26d83f07300a7c1f14ae99fe726ab75763c3b0234847ad57872e3946a780071d95aea6d6 languageName: node linkType: hard @@ -12639,6 +12554,13 @@ __metadata: languageName: node linkType: hard +"tagged-tag@npm:^1.0.0": + version: 1.0.0 + resolution: "tagged-tag@npm:1.0.0" + checksum: 10/e37653df3e495daa7ea7790cb161b810b00075bba2e4d6c93fb06a709e747e3ae9da11a120d0489833203926511b39e038a2affbd9d279cfb7a2f3fcccd30b5d + languageName: node + linkType: hard + "tapable@npm:^2.2.0, tapable@npm:^2.2.1": version: 2.2.1 resolution: "tapable@npm:2.2.1" @@ -12879,10 +12801,10 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.5": - version: 0.2.5 - resolution: "tmp@npm:0.2.5" - checksum: 10/dd4b78b32385eab4899d3ae296007b34482b035b6d73e1201c4a9aede40860e90997a1452c65a2d21aee73d53e93cd167d741c3db4015d90e63b6d568a93d7ec +"tmp@npm:^0.2.7": + version: 0.2.7 + resolution: "tmp@npm:0.2.7" + checksum: 10/0a3bc90beb0c6275273c3475fb57e466eaab1c9c4a101d029ff62b18146ce136e7f75d09de34863d9f2c2a492751402508f9e028bc98eb34a1416195d4b15619 languageName: node linkType: hard @@ -13088,20 +13010,22 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.26.1": - version: 4.26.1 - resolution: "type-fest@npm:4.26.1" - checksum: 10/b82676194f80af228cb852e320d2ea8381c89d667d2e4d9f2bdfc8f254bccc039c7741a90c53617a4de0c9fdca8265ed18eb0888cd628f391c5c381c33a9f94b - languageName: node - linkType: hard - -"type-fest@npm:^4.39.1, type-fest@npm:^4.41.0, type-fest@npm:^4.6.0": +"type-fest@npm:^4.39.1, type-fest@npm:^4.6.0": version: 4.41.0 resolution: "type-fest@npm:4.41.0" checksum: 10/617ace794ac0893c2986912d28b3065ad1afb484cad59297835a0807dc63286c39e8675d65f7de08fafa339afcb8fe06a36e9a188b9857756ae1e92ee8bda212 languageName: node linkType: hard +"type-fest@npm:^5.6.0": + version: 5.8.0 + resolution: "type-fest@npm:5.8.0" + dependencies: + tagged-tag: "npm:^1.0.0" + checksum: 10/4dbe4c78ac6933d3d165b01f9d552991a84d63e5352110e01bebb5c46499fb1f44d199fc88835b18ee5662ee44c73926621f59525e46f7faa031086ac00b2686 + languageName: node + linkType: hard + "type-is@npm:^2.0.1": version: 2.0.1 resolution: "type-is@npm:2.0.1" @@ -13213,6 +13137,13 @@ __metadata: languageName: node linkType: hard +"uint8array-extras@npm:^1.5.0": + version: 1.5.0 + resolution: "uint8array-extras@npm:1.5.0" + checksum: 10/94fd56a2dda6a7445f5176f301f491814c87757d38e4b3c932299ab54d69ec504830e5d5c18ffa20cf694a69a210315be8b4a2c9952c6334da817ea2d2e1dce0 + languageName: node + linkType: hard + "unbox-primitive@npm:^1.1.0": version: 1.1.0 resolution: "unbox-primitive@npm:1.1.0" @@ -13359,19 +13290,6 @@ __metadata: languageName: node linkType: hard -"unzipper@npm:^0.12.3": - version: 0.12.3 - resolution: "unzipper@npm:0.12.3" - dependencies: - bluebird: "npm:~3.7.2" - duplexer2: "npm:~0.1.4" - fs-extra: "npm:^11.2.0" - graceful-fs: "npm:^4.2.2" - node-int64: "npm:^0.4.0" - checksum: 10/b210c421308e1913e01b54faad4ae79e758c674311892414a0697acacba9f82fa0051b677faa77e62fab422eef928c858f2d5cda9ddb47a2f3db95b0e9b36359 - languageName: node - linkType: hard - "unzipper@npm:^0.12.5": version: 0.12.5 resolution: "unzipper@npm:0.12.5" @@ -13540,33 +13458,33 @@ __metadata: languageName: node linkType: hard -"vscode-extension-tester@npm:^8.23.0": - version: 8.23.0 - resolution: "vscode-extension-tester@npm:8.23.0" +"vscode-extension-tester@npm:^8.24.0": + version: 8.24.0 + resolution: "vscode-extension-tester@npm:8.24.0" dependencies: - "@redhat-developer/locators": "npm:^1.20.0" - "@redhat-developer/page-objects": "npm:^1.20.0" - "@types/selenium-webdriver": "npm:^4.35.5" - "@vscode/vsce": "npm:^3.7.1" - c8: "npm:^11.0.0" + "@redhat-developer/locators": "npm:^1.21.0" + "@redhat-developer/page-objects": "npm:^1.21.0" + "@types/selenium-webdriver": "npm:^4.35.6" + "@vscode/vsce": "npm:^3.9.2" + c8: "npm:^12.0.0" commander: "npm:^14.0.3" compare-versions: "npm:^6.1.1" find-up: "npm:8.0.0" - fs-extra: "npm:^11.3.4" + fs-extra: "npm:^11.4.0" glob: "npm:^13.0.6" - got: "npm:^14.6.6" + got: "npm:^15.1.0" hpagent: "npm:^1.2.0" - js-yaml: "npm:^4.1.1" - sanitize-filename: "npm:^1.6.3" - selenium-webdriver: "npm:^4.41.0" + js-yaml: "npm:^5.2.2" + sanitize-filename: "npm:^1.6.4" + selenium-webdriver: "npm:^4.46.0" targz: "npm:^1.0.1" - unzipper: "npm:^0.12.3" + unzipper: "npm:^0.12.5" peerDependencies: mocha: ">=5.2.0" typescript: ">=4.6.2" bin: extest: out/cli.js - checksum: 10/e5c2ddb99033ed13ccf8b50edf2a867f9b2c9e7e99a021b8fc8f69169d4aadfe1984589ae61e31e46a6a04a6ffc6828b67252762c09a9ea5688daaa5794b9a70 + checksum: 10/f7592f655fe4ba22841c4cffdeb1d22f074d7a93152ae57407922f28a65580952fd355d09f896cf1b2b7c4a267386c668c3093aa04356a32afb9cded0324085d languageName: node linkType: hard @@ -13574,8 +13492,8 @@ __metadata: version: 0.0.0-use.local resolution: "vscode-kaoto@workspace:." dependencies: - "@kaoto/camel-catalog": "npm:^0.5.5" - "@kaoto/kaoto": "npm:2.11.0" + "@kaoto/camel-catalog": "npm:^0.8.0" + "@kaoto/kaoto": "npm:2.12.0-RC1" "@kie-tools-core/backend": "npm:10.0.0" "@kie-tools-core/editor": "npm:10.0.0" "@kie-tools-core/i18n": "npm:10.0.0" @@ -13641,7 +13559,7 @@ __metadata: typescript: "npm:^5.9.3" unzipper: "npm:^0.12.5" valid-filename: "npm:4.0.0" - vscode-extension-tester: "npm:^8.23.0" + vscode-extension-tester: "npm:^8.24.0" wait-on: "npm:^9.0.10" web-worker: "npm:^1.5.0" webpack: "npm:5.108.3" @@ -13984,9 +13902,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.19.0": - version: 8.19.0 - resolution: "ws@npm:8.19.0" +"ws@npm:^8.21.0": + version: 8.21.2 + resolution: "ws@npm:8.21.2" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -13995,7 +13913,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b + checksum: 10/1fa65bfadc0dc73674638482b448e2a8cf1833d0acc2263fd27bf74b98f6f2917b2db8263b460a1ff37b6c2b8b892eba0e0eca33325a1ed7a8fe187f38550b40 languageName: node linkType: hard @@ -14131,16 +14049,6 @@ __metadata: languageName: node linkType: hard -"yauzl@npm:^2.3.1": - version: 2.10.0 - resolution: "yauzl@npm:2.10.0" - dependencies: - buffer-crc32: "npm:~0.2.3" - fd-slicer: "npm:~1.1.0" - checksum: 10/1e4c311050dc0cf2ee3dbe8854fe0a6cde50e420b3e561a8d97042526b4cf7a0718d6c8d89e9e526a152f4a9cec55bcea9c3617264115f48bd6704cf12a04445 - languageName: node - linkType: hard - "yauzl@npm:^3.2.1": version: 3.3.0 resolution: "yauzl@npm:3.3.0"