diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 16eee0a12..821e9d2b8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -280,6 +280,7 @@ jobs: out/log out/test-resources/settings/logs out/userdata/logs + out/test-resources/screenshots if-no-files-found: ignore retention-days: 90 diff --git a/.mocharc.yml b/.mocharc.yml index a9d5d2f8c..263c1ffbe 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1,7 +1,7 @@ extension: ["ts"] require: ts-node/register package: "./package.json" -timeout: 10001 # default is 2000 +timeout: 30003 # default is 2000 # most UI tests are >22s due to our current wait times and we do not want # red slow marker to distract us until we sort that part yet. Red is expected # to appear on unexpected long tests, not on an expected duration. diff --git a/test/mockLightspeedServer/openUrl.ts b/test/mockLightspeedServer/openUrl.ts index aaa08de77..219465fb4 100644 --- a/test/mockLightspeedServer/openUrl.ts +++ b/test/mockLightspeedServer/openUrl.ts @@ -40,8 +40,6 @@ export function openUrl(url: string) { errorText += data; }); child.stderr.on("end", function () { - if (errorText) { - throw new Error(errorText); - } + console.log(`error: ${errorText}`); }); } diff --git a/test/mockLightspeedServer/server.ts b/test/mockLightspeedServer/server.ts index e5cc11939..f7fda781f 100644 --- a/test/mockLightspeedServer/server.ts +++ b/test/mockLightspeedServer/server.ts @@ -47,11 +47,7 @@ export const logger = winston.createLogger({ ], }); -let url = new URL("http://127.0.0.1:3000"); -// Do not try to use envvars on macos -- ref: https://github.com/microsoft/vscode/issues/204005 -if (process.platform !== "darwin" && process.env.TEST_LIGHTSPEED_URL) { - url = new URL(process.env.TEST_LIGHTSPEED_URL); -} +const url = new URL("http://127.0.0.1:3000"); export function permissionDeniedCanApplyForTrial(): { code: string; @@ -141,6 +137,10 @@ export default class Server { res.status(200).send(); }); + app.get("/__debug__/kill", () => { + process.exit(0); + }); + app.listen(parseInt(url.port), url.hostname, () => { logger.info(`Listening on port ${url.port} at ${url.hostname}`); }); diff --git a/test/testFixtures/settings.json b/test/testFixtures/settings.json index 40c90ea15..782c0919b 100644 --- a/test/testFixtures/settings.json +++ b/test/testFixtures/settings.json @@ -20,6 +20,7 @@ "ansibleServer.trace.server": "verbose", "ansible.lightspeed.enabled": false, "ansible.lightspeed.suggestions.enabled": false, + "ansible.lightspeed.URL": "https://c.ai.ansible.redhat.com", "extensions.autoUpdate": false, "extensions.autoCheckUpdates": false, diff --git a/test/ui-test/allTestsSuite.ts b/test/ui-test/allTestsSuite.ts deleted file mode 100644 index 2e63df916..000000000 --- a/test/ui-test/allTestsSuite.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { contentCreatorUiTest } from "./contentCreatorUiTest"; -import { extensionUIAssetsTest } from "./extensionUITest"; -import { lightspeedUILoginTest } from "./lightspeedAuthUiTest"; -import { lightspeedOneClickTrialUITest } from "./lightspeedOneClickTrialUITest"; -import { lightspeedUIAssetsTest } from "./lightspeedUiTest"; -import { terminalUITests } from "./terminalUiTest"; -import { walkthroughUiTest } from "./walkthroughUiTest"; -import { welcomePageUITest } from "./welcomePageUITest"; - -describe("VSCode Ansible - UI tests", function () { - this.timeout(30000); - extensionUIAssetsTest(); - lightspeedUIAssetsTest(); - terminalUITests(); - welcomePageUITest(); - - // Skip this on MacOS due to the functional limitation on menu support - if (process.platform === "darwin") { - lightspeedUILoginTest(); - } else { - lightspeedOneClickTrialUITest(); - // lightspeedUILoginTest(); - // lightspeedUISignOutTest(); - } - walkthroughUiTest(); - contentCreatorUiTest(); -}); diff --git a/test/ui-test/contentCreatorUiTest.ts b/test/ui-test/contentCreatorUiTest.ts index 430493d1b..5bbefd469 100644 --- a/test/ui-test/contentCreatorUiTest.ts +++ b/test/ui-test/contentCreatorUiTest.ts @@ -1,92 +1,93 @@ -import { By, EditorView, WebElement, Workbench } from "vscode-extension-tester"; -import { getWebviewByLocator, sleep } from "./uiTestHelper"; +import { By, EditorView, WebElement } from "vscode-extension-tester"; +import { + getWebviewByLocator, + sleep, + workbenchExecuteCommand, +} from "./uiTestHelper"; import { config, expect } from "chai"; config.truncateThreshold = 0; -export function contentCreatorUiTest(): void { - describe("Test Ansible playbook project scaffolding", () => { - let workbench: Workbench; - let createButton: WebElement; - let editorView: EditorView; - before(async () => { - workbench = new Workbench(); - editorView = new EditorView(); - if (editorView) { - await editorView.closeAllEditors(); - } - }); +describe("Test Ansible playbook project scaffolding", () => { + let createButton: WebElement; + let editorView: EditorView; - async function testWebViewElements( - command: string, - editorTitle: string, - namespaceName: string, - collectionName: string, - ) { - await workbench.executeCommand(command); - await sleep(4000); + before(async () => { + editorView = new EditorView(); + if (editorView) { + await editorView.closeAllEditors(); + } + }); - await new EditorView().openEditor(editorTitle); - const webview = await getWebviewByLocator( - By.xpath("//vscode-text-field[@id='namespace-name']"), - ); + async function testWebViewElements( + command: string, + editorTitle: string, + namespaceName: string, + collectionName: string, + ) { + await workbenchExecuteCommand(command); + await sleep(4000); - const namespaceTextField = await webview.findWebElement( - By.xpath("//vscode-text-field[@id='namespace-name']"), - ); - expect(namespaceTextField, "namespaceTextField should not be undefined") - .not.to.be.undefined; - await namespaceTextField.sendKeys(namespaceName); + await new EditorView().openEditor(editorTitle); + const webview = await getWebviewByLocator( + By.xpath("//vscode-text-field[@id='namespace-name']"), + ); - const collectionTextField = await webview.findWebElement( - By.xpath("//vscode-text-field[@id='collection-name']"), - ); - expect(collectionTextField, "collectionTextField should not be undefined") - .not.to.be.undefined; - await collectionTextField.sendKeys(collectionName); + const namespaceTextField = await webview.findWebElement( + By.xpath("//vscode-text-field[@id='namespace-name']"), + ); + expect(namespaceTextField, "namespaceTextField should not be undefined").not + .to.be.undefined; + await namespaceTextField.sendKeys(namespaceName); - const overwriteCheckbox = await webview.findWebElement( - By.xpath("//vscode-checkbox[@id='overwrite-checkbox']"), - ); - expect(overwriteCheckbox, "overwriteCheckbox should not be undefined").not - .to.be.undefined; - await overwriteCheckbox.click(); + const collectionTextField = await webview.findWebElement( + By.xpath("//vscode-text-field[@id='collection-name']"), + ); + expect(collectionTextField, "collectionTextField should not be undefined") + .not.to.be.undefined; + await collectionTextField.sendKeys(collectionName); - createButton = await webview.findWebElement( - By.xpath("//vscode-button[@id='create-button']"), - ); - expect(createButton, "createButton should not be undefined").not.to.be - .undefined; + const overwriteCheckbox = await webview.findWebElement( + By.xpath("//vscode-checkbox[@id='overwrite-checkbox']"), + ); + expect(overwriteCheckbox, "overwriteCheckbox should not be undefined").not + .to.be.undefined; + await overwriteCheckbox.click(); - expect( - await createButton.isEnabled(), - "Create button should be enabled now", - ).to.be.true; + createButton = await webview.findWebElement( + By.xpath("//vscode-button[@id='create-button']"), + ); + expect(createButton, "createButton should not be undefined").not.to.be + .undefined; - await createButton.click(); - await webview.switchBack(); - editorView = new EditorView(); - if (editorView) { - await editorView.closeAllEditors(); - } + expect( + await createButton.isEnabled(), + "Create button should be enabled now", + ).to.be.true; + + await createButton.click(); + await webview.switchBack(); + editorView = new EditorView(); + if (editorView) { + await editorView.closeAllEditors(); } + } - it("Check create-ansible-project webview elements", async () => { - await testWebViewElements( - "Ansible: Create New Playbook Project", - "Create Ansible project", - "test_namespace", - "test_collection", - ); - }); + it("Check create-ansible-project webview elements", async () => { + await testWebViewElements( + "Ansible: Create New Playbook Project", + "Create Ansible project", + "test_namespace", + "test_collection", + ); + }); - it("Check create-ansible-collection webview elements", async () => { - await testWebViewElements( - "Ansible: Create New Collection", - "Create Ansible collection", - "test_namespace", - "test_collection", - ); - }); + it("Check create-ansible-collection webview elements", async () => { + await testWebViewElements( + "Ansible: Create New Collection", + "Create Ansible collection", + "test_namespace", + "test_collection", + ); }); -} +}); diff --git a/test/ui-test/extensionUITest.ts b/test/ui-test/extensionUITest.ts index e587051d2..71aba2e79 100644 --- a/test/ui-test/extensionUITest.ts +++ b/test/ui-test/extensionUITest.ts @@ -9,37 +9,35 @@ import { const WAIT_TIME = 10000; config.truncateThreshold = 0; -export function extensionUIAssetsTest(): void { - describe("Verify base assets are available after installation", () => { - let view: ViewControl; - let sideBar: SideBarView; +describe("Verify base assets are available after installation", () => { + let view: ViewControl; + let sideBar: SideBarView; - before(async function () { - this.timeout(WAIT_TIME); - view = (await new ActivityBar().getViewControl( - "Extensions", - )) as ViewControl; - sideBar = await view.openView(); - }); + before(async function () { + this.timeout(WAIT_TIME); + view = (await new ActivityBar().getViewControl( + "Extensions", + )) as ViewControl; + sideBar = await view.openView(); + }); - it("VSCode Ansible extension is installed", async function () { - // Execute only when code coverage is not enabled. - // When code coverage is enabled, the extension is not installed. - if (!process.env.COVERAGE) { - this.retries(3); - this.timeout(20000); // even 18s failed - const section = await sideBar.getContent().getSection("Installed"); - const item = await section.findItem("@installed Ansible"); - expect(item, "Failed to find Ansible extension").not.undefined; - expect(await item?.getText()).to.contain("Ansible language support"); - } else { - this.skip(); - } - }); + it("VSCode Ansible extension is installed", async function () { + // Execute only when code coverage is not enabled. + // When code coverage is enabled, the extension is not installed. + if (!process.env.COVERAGE) { + this.retries(3); + this.timeout(20000); // even 18s failed + const section = await sideBar.getContent().getSection("Installed"); + const item = await section.findItem("@installed Ansible"); + expect(item, "Failed to find Ansible extension").not.undefined; + expect(await item?.getText()).to.contain("Ansible language support"); + } else { + this.skip(); + } + }); - after(async function () { - this.timeout(8000); - await new Workbench().executeCommand("Clear Extensions Search Results"); - }); + after(async function () { + this.timeout(8000); + await new Workbench().executeCommand("Clear Extensions Search Results"); }); -} +}); diff --git a/test/ui-test/lightspeedAuthUiTest.ts b/test/ui-test/lightspeedAuthUiTest.ts index 74c2bcba9..82b505c88 100644 --- a/test/ui-test/lightspeedAuthUiTest.ts +++ b/test/ui-test/lightspeedAuthUiTest.ts @@ -18,146 +18,145 @@ import { } from "./uiTestHelper"; import { expect } from "chai"; -export function lightspeedUILoginTest(): void { - describe("Login to Lightspeed", () => { - let workbench: Workbench; - let explorerView: WebviewView; - let modalDialog: ModalDialog; - let dialogMessage: string; - let sideBar: SideBarView; - let viewControl: ViewControl; - let adtView: ViewSection; - - before(async () => { - // Enable Lightspeed and open Ansible Light view on sidebar - workbench = new Workbench(); - const settingsEditor = await workbench.openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); - await updateSettings( - settingsEditor, - "ansible.lightspeed.URL", - process.env.TEST_LIGHTSPEED_URL, - ); +before(function () { + // Disable my default + this.skip(); +}); + +describe("Login to Lightspeed", () => { + let workbench: Workbench; + let explorerView: WebviewView; + let modalDialog: ModalDialog; + let dialogMessage: string; + let sideBar: SideBarView; + let viewControl: ViewControl; + let adtView: ViewSection; + + before(async () => { + // Enable Lightspeed and open Ansible Light view on sidebar + workbench = new Workbench(); + const settingsEditor = await workbench.openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); + await updateSettings( + settingsEditor, + "ansible.lightspeed.URL", + process.env.TEST_LIGHTSPEED_URL, + ); + + viewControl = (await new ActivityBar().getViewControl( + "Ansible", + )) as ViewControl; + sideBar = await viewControl.openView(); + + adtView = await sideBar + .getContent() + .getSection("Ansible Development Tools"); + adtView.collapse(); + await sleep(3000); + }); - viewControl = (await new ActivityBar().getViewControl( - "Ansible", - )) as ViewControl; - sideBar = await viewControl.openView(); - - adtView = await sideBar - .getContent() - .getSection("Ansible Development Tools"); - adtView.collapse(); - await sleep(3000); - }); - - it("Focus on Ansible Lightspeed View", async () => { - explorerView = new WebviewView(); - expect(explorerView, "contentCreatorWebView should not be undefined").not - .to.be.undefined; - }); - - it("Click Connect button Ansible Lightspeed webview", async () => { - await explorerView.switchToFrame(5000); - const connectButton = await explorerView.findWebElement( - By.id("lightspeed-explorer-connect"), - ); - expect(connectButton).not.to.be.undefined; - if (connectButton) { - await connectButton.click(); - } - await explorerView.switchBack(); - }); - - it("Click Allow to use Lightspeed", async () => { - // Click Allow to use Lightspeed - const { dialog, message } = await getModalDialogAndMessage(true); - expect(message).equals( - "The extension 'Ansible' wants to sign in using Ansible Lightspeed.", - ); - await dialog.pushButton("Allow"); - }); - - it("Verify a modal dialog pops up", async () => { - const { dialog, message } = await getModalDialogAndMessage(); - expect(dialog).not.to.be.undefined; - expect(message).not.to.be.undefined; - modalDialog = dialog; - dialogMessage = message; - }); - - it("Click Open if a dialog shows up for opening the external website", async () => { - // If the dialog to open the external website is not suppressed, click Open - if (dialogMessage === "Do you want Code to open the external website?") { - await modalDialog.pushButton("Configure Trusted Domains"); - const input = await InputBox.create(); - input.confirm(); - - const d = await getModalDialogAndMessage(); - modalDialog = d.dialog; - dialogMessage = d.message; - } - }); - - it("Click Open to open the callback URI", async () => { - // Click Open to allow Ansible extension to open the callback URI - expect(dialogMessage).equals( - "Allow 'Ansible' extension to open this URI?", - ); - await modalDialog.pushButton("Open"); - await sleep(2000); - }); - - it("Verify Ansible Lightspeed webview now contains user's information", async () => { - await explorerView.switchToFrame(5000); - const div = await explorerView.findWebElement( - By.id("lightspeedExplorerView"), + it("Focus on Ansible Lightspeed View", async () => { + explorerView = new WebviewView(); + expect(explorerView, "contentCreatorWebView should not be undefined").not.to + .be.undefined; + }); + + it("Click Connect button Ansible Lightspeed webview", async () => { + await explorerView.switchToFrame(5000); + const connectButton = await explorerView.findWebElement( + By.id("lightspeed-explorer-connect"), + ); + expect(connectButton).not.to.be.undefined; + if (connectButton) { + await connectButton.click(); + } + await explorerView.switchBack(); + }); + + it("Click Allow to use Lightspeed", async () => { + // Click Allow to use Lightspeed + const { dialog, message } = await getModalDialogAndMessage(true); + expect(message).equals( + "The extension 'Ansible' wants to sign in using Ansible Lightspeed.", + ); + await dialog.pushButton("Allow"); + }); + + it("Verify a modal dialog pops up", async () => { + const { dialog, message } = await getModalDialogAndMessage(); + expect(dialog).not.to.be.undefined; + expect(message).not.to.be.undefined; + modalDialog = dialog; + dialogMessage = message; + }); + + it("Click Open if a dialog shows up for opening the external website", async () => { + // If the dialog to open the external website is not suppressed, click Open + if (dialogMessage === "Do you want Code to open the external website?") { + await modalDialog.pushButton("Configure Trusted Domains"); + const input = await InputBox.create(); + input.confirm(); + + const d = await getModalDialogAndMessage(); + modalDialog = d.dialog; + dialogMessage = d.message; + } + }); + + it("Click Open to open the callback URI", async () => { + // Click Open to allow Ansible extension to open the callback URI + expect(dialogMessage).equals("Allow 'Ansible' extension to open this URI?"); + await modalDialog.pushButton("Open"); + await sleep(2000); + }); + + it("Verify Ansible Lightspeed webview now contains user's information", async () => { + await explorerView.switchToFrame(5000); + const div = await explorerView.findWebElement( + By.id("lightspeedExplorerView"), + ); + const text = await div.getText(); + expect(text).contains("Logged in as:"); + await explorerView.switchBack(); + }); +}); + +describe("Sign out from Lightspeed", () => { + let workbench: Workbench; + + it("Sign out using Accounts global action", async () => { + workbench = new Workbench(); + const activityBar = new ActivityBar(); + const actions = (await activityBar.getGlobalAction( + "Accounts", + )) as ActionsControl; + expect(actions).not.to.be.undefined; + await actions.click(); + const menus = await workbench.findElements(By.className("context-view")); + expect(menus.length).greaterThan(0); + const menu = new ContextMenu(workbench); + expect(menu).not.to.be.undefined; + if (menu) { + await menu.select( + "EXTERNAL_USERNAME (licensed) (Ansible Lightspeed)", + "Sign Out", ); - const text = await div.getText(); - expect(text).contains("Logged in as:"); - await explorerView.switchBack(); - }); + } }); -} - -export function lightspeedUISignOutTest(): void { - describe("Sign out from Lightspeed", () => { - let workbench: Workbench; - - it("Sign out using Accounts global action", async () => { - workbench = new Workbench(); - const activityBar = new ActivityBar(); - const actions = (await activityBar.getGlobalAction( - "Accounts", - )) as ActionsControl; - expect(actions).not.to.be.undefined; - await actions.click(); - const menus = await workbench.findElements(By.className("context-view")); - expect(menus.length).greaterThan(0); - const menu = new ContextMenu(workbench); - expect(menu).not.to.be.undefined; - if (menu) { - await menu.select( - "EXTERNAL_USERNAME (licensed) (Ansible Lightspeed)", - "Sign Out", - ); - } - }); - - it("Click Sign Out button on the modal dialog", async () => { - const { dialog, message } = await getModalDialogAndMessage(true); - expect(dialog).not.to.be.undefined; - expect(message).contains("Sign out from these extensions?"); - await dialog.pushButton("Sign Out"); - }); - - it("Verify the notification message", async () => { - await sleep(1000); - const notifications = await workbench.getNotifications(); - const notification = notifications[0]; - const message = await notification.getMessage(); - expect(message).equals("Successfully signed out."); - await notification.dismiss(); - }); + + it("Click Sign Out button on the modal dialog", async () => { + const { dialog, message } = await getModalDialogAndMessage(true); + expect(dialog).not.to.be.undefined; + expect(message).contains("Sign out from these extensions?"); + await dialog.pushButton("Sign Out"); + }); + + it("Verify the notification message", async () => { + await sleep(1000); + const notifications = await workbench.getNotifications(); + const notification = notifications[0]; + const message = await notification.getMessage(); + expect(message).equals("Successfully signed out."); + await notification.dismiss(); }); -} +}); diff --git a/test/ui-test/lightspeedOneClickTrialUITest.ts b/test/ui-test/lightspeedOneClickTrialUITest.ts index b75dc2740..1c4499631 100644 --- a/test/ui-test/lightspeedOneClickTrialUITest.ts +++ b/test/ui-test/lightspeedOneClickTrialUITest.ts @@ -1,3 +1,4 @@ +// BEFORE: ansible.lightspeed.enabled: true import { ActionsControl, ActivityBar, @@ -21,7 +22,6 @@ import { getModalDialogAndMessage, getWebviewByLocator, sleep, - updateSettings, } from "./uiTestHelper"; import { Key } from "selenium-webdriver"; import { expect } from "chai"; @@ -30,261 +30,264 @@ import axios from "axios"; const trialNotificationMessage = "Ansible Lightspeed is not configured for your organization, click here to start a 90-day trial."; -export function lightspeedOneClickTrialUITest(): void { - describe("Test One Click Trial feature", () => { - let workbench: Workbench; - let explorerView: WebviewView; - let modalDialog: ModalDialog; - let dialogMessage: string; - let playbookGeneration: WebView; - let submitButton: WebElement; - let sideBar: SideBarView; - let view: ViewControl; - let adtView: ViewSection; - - before(async () => { - // Enable Lightspeed and open Ansible Light view on sidebar - workbench = new Workbench(); - const settingsEditor = await workbench.openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); - await updateSettings( - settingsEditor, - "ansible.lightspeed.URL", - process.env.TEST_LIGHTSPEED_URL, - ); - await updateSettings( - settingsEditor, - "ansible.lightspeed.suggestions.enabled", - true, - ); - // Close settings and other open editors (if any) - await new EditorView().closeAllEditors(); - - // Set "UI Test" and "One Click" options for mock server - await axios.post( - `${process.env.TEST_LIGHTSPEED_URL}/__debug__/options`, - ["--ui-test", "--one-click"], - { headers: { "Content-Type": "application/json" } }, - ); - }); +describe("Test One Click Trial feature", () => { + let workbench: Workbench; + let explorerView: WebviewView; + let modalDialog: ModalDialog; + let dialogMessage: string; + let playbookGeneration: WebView; + let submitButton: WebElement; + let sideBar: SideBarView; + let view: ViewControl; + let adtView: ViewSection; + + beforeEach(function () { + if (process.platform === "darwin") { + this.skip(); + } + + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + }); - it("Focus on Ansible Lightspeed View", async () => { - view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; - sideBar = await view.openView(); + before(async () => { + // We need this because before() is called ... before beforeEach() + if (process.platform === "darwin") { + return; + } + + if (!process.env.TEST_LIGHTSPEED_URL) { + return; + } + + // Enable Lightspeed and open Ansible Light view on sidebar + workbench = new Workbench(); + // Close settings and other open editors (if any) + await new EditorView().closeAllEditors(); + + // Set "UI Test" and "One Click" options for mock server + await axios.post( + `${process.env.TEST_LIGHTSPEED_URL}/__debug__/options`, + ["--ui-test", "--one-click"], + { headers: { "Content-Type": "application/json" } }, + ); + }); - adtView = await sideBar - .getContent() - .getSection("Ansible Development Tools"); - adtView.collapse(); + it("Focus on Ansible Lightspeed View", async () => { + view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; + sideBar = await view.openView(); - await sleep(3000); - explorerView = new WebviewView(); - expect(explorerView, "contentCreatorWebView should not be undefined").not - .to.be.undefined; - }); + adtView = await sideBar + .getContent() + .getSection("Ansible Development Tools"); + adtView.collapse(); - it("Click Connect button Ansible Lightspeed webview", async () => { - await explorerView.switchToFrame(5000); + await sleep(3000); + explorerView = new WebviewView(); + expect(explorerView, "contentCreatorWebView should not be undefined").not.to + .be.undefined; + }); - const connectButton = await explorerView.findWebElement( - By.id("lightspeed-explorer-connect"), - ); - expect(connectButton).not.to.be.undefined; - if (connectButton) { - await connectButton.click(); - } - await explorerView.switchBack(); - }); - - it("Click Allow to use Lightspeed", async () => { - // Click Allow to use Lightspeed - const { dialog, message } = await getModalDialogAndMessage(true); - expect(message).equals( - "The extension 'Ansible' wants to sign in using Ansible Lightspeed.", - ); - await dialog.pushButton("Allow"); - }); - - it("Verify a modal dialog pops up", async () => { - const { dialog, message } = await getModalDialogAndMessage(); - expect(dialog).not.to.be.undefined; - expect(message).not.to.be.undefined; - modalDialog = dialog; - dialogMessage = message; - }); - - it("Click Open if a dialog shows up for opening the external website", async () => { - // If the dialog to open the external website is not suppressed, click Open - if (dialogMessage === "Do you want Code to open the external website?") { - await modalDialog.pushButton("Configure Trusted Domains"); - const input = await InputBox.create(); - input.confirm(); - - const d = await getModalDialogAndMessage(); - modalDialog = d.dialog; - dialogMessage = d.message; - } - }); - - it("Click Open to open the callback URI", async () => { - // Click Open to allow Ansible extension to open the callback URI - expect(dialogMessage).equals( - "Allow 'Ansible' extension to open this URI?", - ); - await modalDialog.pushButton("Open"); - await sleep(2000); - }); - - it("Verify Ansible Lightspeed webview now contains user's information", async () => { - await explorerView.switchToFrame(5000); - const div = await explorerView.findWebElement( - By.id("lightspeedExplorerView"), - ); - const text = await div.getText(); - expect(text).contains("Logged in as: ONE_CLICK_USER (unlicensed)"); - await explorerView.switchBack(); - await expectNotification("Welcome back ONE_CLICK_USER (unlicensed)"); - }); - - it("Verify the Refresh icon is found on the title of Explorer view", async () => { - const refreshIcon = await explorerView.findWebElement( - By.xpath("//a[contains(@class, 'codicon-refresh')]"), - ); - expect(refreshIcon, "refreshIcon should not be undefined").not.to.be - .undefined; - - // Set "UI Test", One Click" and "Me Uppercase" options for mock server - await axios.post( - `${process.env.TEST_LIGHTSPEED_URL}/__debug__/options`, - ["--ui-test", "--one-click", "--me-uppercase"], - { headers: { "Content-Type": "application/json" } }, - ); + it("Click Connect button Ansible Lightspeed webview", async () => { + await explorerView.switchToFrame(5000); + + const connectButton = await explorerView.findWebElement( + By.id("lightspeed-explorer-connect"), + ); + expect(connectButton).not.to.be.undefined; + if (connectButton) { + await connectButton.click(); + } + await explorerView.switchBack(); + }); - await refreshIcon.click(); // make sure if it could be clicked + it("Click Allow to use Lightspeed", async () => { + // Click Allow to use Lightspeed + const { dialog, message } = await getModalDialogAndMessage(true); + expect(message).equals( + "The extension 'Ansible' wants to sign in using Ansible Lightspeed.", + ); + await dialog.pushButton("Allow"); + }); - await sleep(2000); - await explorerView.switchToFrame(5000); - const div = await explorerView.findWebElement( - By.id("lightspeedExplorerView"), - ); - const text = await div.getText(); - expect(text).contains("LOGGED IN AS: ONE_CLICK_USER (UNLICENSED)"); - await explorerView.switchBack(); - }); - - it("Invoke Playbook generation", async () => { - await workbench.executeCommand("Ansible Lightspeed: Playbook generation"); - await sleep(2000); - playbookGeneration = await getWebviewByLocator( - By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), - ); + it("Verify a modal dialog pops up", async () => { + const { dialog, message } = await getModalDialogAndMessage(); + expect(dialog).not.to.be.undefined; + expect(message).not.to.be.undefined; + modalDialog = dialog; + dialogMessage = message; + }); - // Set input text and invoke summaries API - const textArea = await playbookGeneration.findWebElement( - By.xpath("//vscode-text-area"), - ); - expect(textArea, "textArea should not be undefined").not.to.be.undefined; - submitButton = await playbookGeneration.findWebElement( - By.xpath("//vscode-button[@id='submit-button']"), - ); - expect(submitButton, "submitButton should not be undefined").not.to.be - .undefined; - // - // Note: Following line should succeed, but fails for some unknown reasons. - // - // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; - await textArea.sendKeys("Create an azure network."); - expect( - await submitButton.isEnabled(), - "submit button should be enabled now", - ).to.be.true; - await submitButton.click(); - await playbookGeneration.switchBack(); - await sleep(2000); - await expectNotification( - trialNotificationMessage, - true, // click button - ); - await new EditorView().closeAllEditors(); - }); + it("Click Open if a dialog shows up for opening the external website", async () => { + // If the dialog to open the external website is not suppressed, click Open + if (dialogMessage === "Do you want Code to open the external website?") { + await modalDialog.pushButton("Configure Trusted Domains"); + const input = await InputBox.create(); + input.confirm(); + + const d = await getModalDialogAndMessage(); + modalDialog = d.dialog; + dialogMessage = d.message; + } + }); - it("Invoke Playbook explanation", async () => { - const folder = "lightspeed"; - const file = "playbook_4.yml"; - const filePath = getFixturePath(folder, file); + it("Click Open to open the callback URI", async () => { + // Click Open to allow Ansible extension to open the callback URI + expect(dialogMessage).equals("Allow 'Ansible' extension to open this URI?"); + await modalDialog.pushButton("Open"); + await sleep(2000); + }); - // Open file in the editor - await VSBrowser.instance.openResources(filePath); - await sleep(1000); + it("Verify Ansible Lightspeed webview now contains user's information", async () => { + await explorerView.switchToFrame(5000); + const div = await explorerView.findWebElement( + By.id("lightspeedExplorerView"), + ); + const text = await div.getText(); + expect(text).contains("Logged in as: ONE_CLICK_USER (unlicensed)"); + await explorerView.switchBack(); + await expectNotification("Welcome back ONE_CLICK_USER (unlicensed)"); + }); - // Open playbook explanation webview. - await workbench.executeCommand( - "Explain the playbook with Ansible Lightspeed", - ); - await sleep(2000); - await expectNotification( - trialNotificationMessage, - true, // click button - ); - await new EditorView().closeAllEditors(); - }); - - it("Invoke Completion", async () => { - const folder = "lightspeed"; - const file = "playbook_3.yml"; - const filePath = getFixturePath(folder, file); - await VSBrowser.instance.openResources(filePath); - await sleep(1000); - - const editorView = new EditorView(); - const tab = await editorView.getTabByTitle(file); - await tab.select(); - - // Trigger completions API. First space key and backspace look redundant, - // but just sending page down and 4 spaces did not work... - await tab.sendKeys(" ", Key.BACK_SPACE, Key.PAGE_DOWN, " "); - - await sleep(4000); - await expectNotification( - trialNotificationMessage, - true, // click button + it("Verify the Refresh icon is found on the title of Explorer view", async () => { + const refreshIcon = await explorerView.findWebElement( + By.xpath("//a[contains(@class, 'codicon-refresh')]"), + ); + expect(refreshIcon, "refreshIcon should not be undefined").not.to.be + .undefined; + + // Set "UI Test", One Click" and "Me Uppercase" options for mock server + await axios.post( + `${process.env.TEST_LIGHTSPEED_URL}/__debug__/options`, + ["--ui-test", "--one-click", "--me-uppercase"], + { headers: { "Content-Type": "application/json" } }, + ); + + await refreshIcon.click(); // make sure if it could be clicked + + await sleep(2000); + await explorerView.switchToFrame(5000); + const div = await explorerView.findWebElement( + By.id("lightspeedExplorerView"), + ); + const text = await div.getText(); + expect(text).contains("LOGGED IN AS: ONE_CLICK_USER (UNLICENSED)"); + await explorerView.switchBack(); + }); + + it("Invoke Playbook generation", async () => { + await workbench.executeCommand("Ansible Lightspeed: Playbook generation"); + await sleep(2000); + playbookGeneration = await getWebviewByLocator( + By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), + ); + + // Set input text and invoke summaries API + const textArea = await playbookGeneration.findWebElement( + By.xpath("//vscode-text-area"), + ); + expect(textArea, "textArea should not be undefined").not.to.be.undefined; + submitButton = await playbookGeneration.findWebElement( + By.xpath("//vscode-button[@id='submit-button']"), + ); + expect(submitButton, "submitButton should not be undefined").not.to.be + .undefined; + // + // Note: Following line should succeed, but fails for some unknown reasons. + // + // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; + await textArea.sendKeys("Create an azure network."); + expect( + await submitButton.isEnabled(), + "submit button should be enabled now", + ).to.be.true; + await submitButton.click(); + await playbookGeneration.switchBack(); + await sleep(2000); + await expectNotification( + trialNotificationMessage, + true, // click button + ); + await new EditorView().closeAllEditors(); + }); + + it("Invoke Playbook explanation", async () => { + const folder = "lightspeed"; + const file = "playbook_4.yml"; + const filePath = getFixturePath(folder, file); + + // Open file in the editor + await VSBrowser.instance.openResources(filePath); + await sleep(1000); + + // Open playbook explanation webview. + await workbench.executeCommand( + "Explain the playbook with Ansible Lightspeed", + ); + await sleep(2000); + await expectNotification( + trialNotificationMessage, + true, // click button + ); + await new EditorView().closeAllEditors(); + }); + + it("Invoke Completion", async () => { + const folder = "lightspeed"; + const file = "playbook_3.yml"; + const filePath = getFixturePath(folder, file); + await VSBrowser.instance.openResources(filePath); + await sleep(1000); + + const editorView = new EditorView(); + const tab = await editorView.getTabByTitle(file); + await tab.select(); + + // Trigger completions API. First space key and backspace look redundant, + // but just sending page down and 4 spaces did not work... + await tab.sendKeys(" ", Key.BACK_SPACE, Key.PAGE_DOWN, " "); + + await sleep(4000); + await expectNotification( + trialNotificationMessage, + true, // click button + ); + + // Revert changes made + await tab.select(); + await tab.sendKeys(Key.CONTROL, "z", "z", "z", Key.NULL); + await editorView.closeAllEditors(); + }); + + it("Sign out using Accounts global action", async () => { + workbench = new Workbench(); + const activityBar = new ActivityBar(); + const actions = (await activityBar.getGlobalAction( + "Accounts", + )) as ActionsControl; + expect(actions).not.to.be.undefined; + await actions.click(); + const menus = await workbench.findElements(By.className("context-view")); + expect(menus.length).greaterThan(0); + const menu = new ContextMenu(workbench); + expect(menu).not.to.be.undefined; + if (menu) { + await menu.select( + "ONE_CLICK_USER (unlicensed) (Ansible Lightspeed)", + "Sign Out", ); + } + }); + + it("Click Sign Out button on the modal dialog", async () => { + const dialog = new ModalDialog(); + expect(dialog).not.to.be.undefined; + await dialog.pushButton("Sign Out"); + }); - // Revert changes made - await tab.select(); - await tab.sendKeys(Key.CONTROL, "z", "z", "z", Key.NULL); - await editorView.closeAllEditors(); - }); - - it("Sign out using Accounts global action", async () => { - workbench = new Workbench(); - const activityBar = new ActivityBar(); - const actions = (await activityBar.getGlobalAction( - "Accounts", - )) as ActionsControl; - expect(actions).not.to.be.undefined; - await actions.click(); - const menus = await workbench.findElements(By.className("context-view")); - expect(menus.length).greaterThan(0); - const menu = new ContextMenu(workbench); - expect(menu).not.to.be.undefined; - if (menu) { - await menu.select( - "ONE_CLICK_USER (unlicensed) (Ansible Lightspeed)", - "Sign Out", - ); - } - }); - - it("Click Sign Out button on the modal dialog", async () => { - const dialog = new ModalDialog(); - expect(dialog).not.to.be.undefined; - await dialog.pushButton("Sign Out"); - }); - - it("Verify the notification message", async () => { - await sleep(1000); - await expectNotification("Successfully signed out."); - }); + it("Verify the notification message", async () => { + await sleep(1000); + await expectNotification("Successfully signed out."); }); -} +}); diff --git a/test/ui-test/lightspeedUiTest.ts b/test/ui-test/lightspeedUiTest.ts deleted file mode 100644 index 4fc646e10..000000000 --- a/test/ui-test/lightspeedUiTest.ts +++ /dev/null @@ -1,863 +0,0 @@ -import { expect, config } from "chai"; -import axios from "axios"; -import { - ActivityBar, - By, - SideBarView, - ViewControl, - Workbench, - StatusBar, - VSBrowser, - EditorView, - SettingsEditor, - WebView, - ModalDialog, - ViewSection, - WebviewView, - WebElement, -} from "vscode-extension-tester"; -import { - getFixturePath, - sleep, - updateSettings, - getWebviewByLocator, - workbenchExecuteCommand, - openSettings, -} from "./uiTestHelper"; -import { WizardGenerationActionType } from "../../src/definitions/lightspeed"; -import { PlaybookGenerationActionEvent } from "../../src/interfaces/lightspeed"; - -config.truncateThreshold = 0; - -export function lightspeedUIAssetsTest(): void { - describe("Verify the presence of lightspeed login button in the activity bar", () => { - let view: ViewControl; - let sideBar: SideBarView; - let adtView: ViewSection; - let webviewView: WebView; - - before(async function () { - const settingsEditor = await openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); - - view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; - sideBar = await view.openView(); - - await sideBar.getContent().getSection("Ansible Lightspeed"); - - adtView = await sideBar - .getContent() - .getSection("Ansible Development Tools"); - adtView.collapse(); - - await sleep(2000); - - webviewView = await getWebviewByLocator( - By.xpath("//vscode-button[text()='Connect']"), - ); - }); - - after(async function () { - if (webviewView) { - await webviewView.switchBack(); - } - const settingsEditor = await openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", false); - }); - - it("Ansible Lightspeed welcome message is present", async function () { - const body = await webviewView.findWebElement(By.xpath("//body")); - const welcomeMessage = await body.getText(); - expect(welcomeMessage).to.contain( - "Experience smarter automation using Ansible Lightspeed", - ); - }); - - it("Ansible Lightspeed login button is present", async function () { - const loginButton = await webviewView.findWebElement( - By.xpath("//vscode-button[text()='Connect']"), - ); - expect(loginButton).not.undefined; - }); - }); - - describe("Verify the presence of lightspeed element in the status bar and the explorer view", () => { - let statusBar: StatusBar; - let settingsEditor: SettingsEditor; - let editorView: EditorView; - const folder = "lightspeed"; - const file = "playbook_1.yml"; - const filePath = getFixturePath(folder, file); - - before(async function () { - statusBar = new StatusBar(); - editorView = new EditorView(); - - // open file in the editor - await VSBrowser.instance.openResources(filePath); - }); - - it("Ansible Lightspeed status bar item absent when settings not enabled", async function () { - await editorView.openEditor(file); - - // The following lines replaced the original code that was using StatusBar.getItem() API. - const items = await statusBar.findElements( - By.xpath( - "//div[contains(@class, 'statusbar-item') and " + - ".//a/text()='Lightspeed (not logged in))']", - ), - ); - expect(items.length).equals(0); - }); - - it("Connect button exists in Lightspeed explorer view when settings not enabled", async function () { - workbenchExecuteCommand("Ansible: Focus on Ansible Lightspeed View"); - await sleep(3000); - const explorerView = new WebviewView(); - expect(explorerView, "contentCreatorWebView should not be undefined").not - .to.be.undefined; - await explorerView.switchToFrame(5000); - const connectButton = await explorerView.findWebElement( - By.id("lightspeed-explorer-connect"), - ); - expect(connectButton).not.to.be.undefined; - await explorerView.switchBack(); - }); - - it("Ansible Lightspeed status bar item present when only lightspeed is enabled (with warning color)", async () => { - settingsEditor = await openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); - await editorView.openEditor(file); - - // The following lines replaced the original code that was using StatusBar.getItem() API. - const lightspeedStatusBarItem = await statusBar.findElement( - By.xpath( - "//div[contains(@class, 'statusbar-item') and " + - "contains(@class, 'has-background-color') and " + - "contains(@class, 'warning-kind') and " + - ".//a/text()='Lightspeed (Not logged in)']", - ), - ); - expect(lightspeedStatusBarItem).not.to.be.undefined; - }); - - it("Ansible Lightspeed status bar item present when lightspeed and lightspeed suggestions are enabled (with normal color)", async () => { - settingsEditor = await openSettings(); - await updateSettings( - settingsEditor, - "ansible.lightspeed.suggestions.enabled", - true, - ); - await editorView.openEditor(file); - - // The following lines replaced the original code that was using StatusBar.getItem() API. - const lightspeedStatusBarItem = await statusBar.findElement( - By.xpath( - "//div[contains(@class, 'statusbar-item') and " + - "not (contains(@class, 'has-background-color')) and " + - ".//a/text()='Lightspeed (Not logged in)']", - ), - ); - expect(lightspeedStatusBarItem).not.to.be.undefined; - }); - }); - - describe("Verify playbook generation and explanation features work as expected", function () { - let workbench: Workbench; - let settingsEditor: SettingsEditor; - let outlineList: WebElement; - let resetButton: WebElement; - let adtView: ViewSection; - let webviewView: InstanceType; - - before(async function () { - if (process.env.TEST_LIGHTSPEED_URL) { - workbench = new Workbench(); - settingsEditor = await openSettings(); - await updateSettings( - settingsEditor, - "ansible.lightspeed.enabled", - true, - ); - await updateSettings( - settingsEditor, - "ansible.lightspeed.URL", - process.env.TEST_LIGHTSPEED_URL, - ); - // await workbenchExecuteCommand( - // "Ansible Lightspeed: Enable experimental features", - // ); - await workbenchExecuteCommand("View: Close All Editor Groups"); - - const notifications = await workbench.getNotifications(); - for (let i = 0; i < notifications.length; i++) { - const n = notifications[i]; - await n.dismiss(); - } - // await sleep(500); // Needed? - } - }); - - it("Playbook generation webview works as expected (full path) - part 1", async function () { - // Open Ansible Development Tools by clicking the Getting started button on the side bar - const view = (await new ActivityBar().getViewControl( - "Ansible", - )) as ViewControl; - const sideBar = await view.openView(); - adtView = await sideBar - .getContent() - .getSection("Ansible Development Tools"); - adtView.expand(); - await sleep(1000); - - webviewView = new WebviewView(); - expect(webviewView).not.undefined; - await webviewView.switchToFrame(1000); - - const welcomePageLink = await webviewView.findWebElement( - By.xpath( - "//a[contains(@title, 'Ansible Development Tools welcome page')]", - ), - ); - - expect(welcomePageLink).not.to.be.undefined; - if (welcomePageLink) { - await welcomePageLink.click(); - } - await sleep(5000); - webviewView.switchBack(); - - const contentCreatorWebView = await getWebviewByLocator( - By.xpath( - "//a[contains(@href,'command:ansible.lightspeed.playbookGeneration')]", - ), - ); - const createContentButton = await contentCreatorWebView.findWebElement( - By.xpath( - "//a[contains(@href,'command:ansible.lightspeed.playbookGeneration')]", - ), - ); - expect(createContentButton).not.to.be.undefined; - if (createContentButton) { - await createContentButton.click(); - } - await contentCreatorWebView.switchBack(); - await new EditorView().closeEditor("Ansible Development Tools"); - await sleep(2000); - - // Start operations on Playbook Generation UI - const webView = await getWebviewByLocator( - By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), - ); - - // Set input text and invoke summaries API - const textArea = await webView.findWebElement( - By.xpath("//vscode-text-area"), - ); - const submitButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='submit-button']"), - ); - expect(submitButton, "submitButton should not be undefined").not.to.be - .undefined; - // - // Note: Following line should succeed, but fails for some unknown reasons. - // - // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; - await textArea.sendKeys("Create an azure network."); - expect( - await submitButton.isEnabled(), - "submit button should be enabled now", - ).to.be.true; - await submitButton.click(); - await sleep(2000); - - // Verify outline output and text edit - outlineList = await webView.findWebElement( - By.xpath("//ol[@id='outline-list']"), - ); - expect(outlineList, "An ordered list should exist."); - let text = await outlineList.getText(); - expect( - text.includes("Create virtual network peering"), - "Text should include the expected outline", - ); - await outlineList.sendKeys("# COMMENT\n"); - text = await outlineList.getText(); - expect(text.includes("# COMMENT\n")); - - // Verify the prompt is displayed as a static text - const prompt = await webView.findWebElement( - By.xpath("//span[@id='prompt']"), - ); - text = await prompt.getText(); - expect(text.includes("Create an azure network.")); - - // Test Reset button - resetButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='reset-button']"), - ); - expect(resetButton, "resetButton should not be undefined").not.to.be - .undefined; - await resetButton.click(); - await sleep(500); - text = await outlineList.getText(); - expect(!text.includes("# COMMENT\n")); - - // Test Back button - const backButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='back-button']"), - ); - expect(backButton, "backButton should not be undefined").not.to.be - .undefined; - await backButton.click(); - await sleep(500); - - text = await textArea.getText(); - expect(text.startsWith("Create an azure network.")); - await submitButton.click(); - await sleep(1000); - text = await outlineList.getText(); - expect(text.includes("Create virtual network peering")); - - // Test Edit link next to the prompt text - const backAnchor = await webView.findWebElement( - By.xpath("//a[@id='back-anchor']"), - ); - expect(backAnchor, "backAnchor should not be undefined").not.to.be - .undefined; - await backAnchor.click(); - await sleep(500); - - text = await textArea.getText(); - expect(text.startsWith("Create an azure network.")); - await submitButton.click(); - await sleep(1000); - text = await outlineList.getText(); - expect(text.includes("Create virtual network peering")); - }); - - it("Playbook generation webview works as expected (full path) - part 2", async function () { - const webView = await getWebviewByLocator( - By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), - ); - // Click Generate playbook button to invoke the generations API - const generatePlaybookButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='generate-button']"), - ); - expect( - generatePlaybookButton, - "generatePlaybookButton should not be undefined", - ).not.to.be.undefined; - - // Input "(status=400)" to simulate an API error - await outlineList.sendKeys("(status=400)"); - let text = await outlineList.getText(); - expect(text.includes("(status=400)")); - await generatePlaybookButton.click(); // Click Generate Playbook button - await sleep(2000); - - resetButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='reset-button']"), - ); - // Click reset button and make sure the string "(status=400)" is removed - await resetButton.click(); - await sleep(500); - text = await outlineList.getText(); - expect(!text.includes("(status=400)")); - - // Click Generate Playbook button again - await generatePlaybookButton.click(); - await sleep(2000); - - // Make sure the generated playbook is displayed - const formattedCode = await webView.findWebElement( - By.xpath("//span[@id='formatted-code']"), - ); - expect(formattedCode, "formattedCode should not be undefined").not.to.be - .undefined; - await sleep(500); - text = await formattedCode.getText(); - expect(text.startsWith("---")).to.be.true; - - // Test Back (to Page 2) button - const backToPage2Button = await webView.findWebElement( - By.xpath("//vscode-button[@id='back-to-page2-button']"), - ); - expect(backToPage2Button, "backToPage2Button should not be undefined").not - .to.be.undefined; - await backToPage2Button.click(); - await sleep(500); - - // Type in something extra - await outlineList.sendKeys("\nSomething extra"); - const savedOutline = await outlineList.getText(); - - // Click generate playbook button again - generatePlaybookButton.click(); - await sleep(2000); - - // Click Back page again - await backToPage2Button.click(); - await sleep(500); - - // Make sure outline is not updated. - expect(savedOutline).equal(await outlineList.getText()); - - // Click generate playbook button again - generatePlaybookButton.click(); - await sleep(500); - - // Click Open editor button to open the generated playbook in the editor - const openEditorButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='open-editor-button']"), - ); - expect(openEditorButton, "openEditorButton should not be undefined").not - .to.be.undefined; - await openEditorButton.click(); - await sleep(2000); - await webView.switchBack(); - - // Verify a playbook was generated. - const editor = await new EditorView().openEditor("Untitled-1"); - await sleep(2000); - - text = await editor.getText(); - expect( - text.startsWith("---"), - 'The generated playbook should start with "---"', - ).to.be.true; - - await workbenchExecuteCommand("View: Close All Editor Groups"); - const dialog = new ModalDialog(); - await dialog.pushButton(`Don't Save`); - - /* verify generated events */ - const expected = [ - [WizardGenerationActionType.OPEN, undefined, 1], - [WizardGenerationActionType.TRANSITION, 1, 2], - [WizardGenerationActionType.TRANSITION, 2, 1], - [WizardGenerationActionType.TRANSITION, 1, 2], - [WizardGenerationActionType.TRANSITION, 2, 1], - [WizardGenerationActionType.TRANSITION, 1, 2], - [WizardGenerationActionType.TRANSITION, 2, 3], - [WizardGenerationActionType.TRANSITION, 3, 2], - [WizardGenerationActionType.TRANSITION, 2, 3], - [WizardGenerationActionType.TRANSITION, 3, 2], - [WizardGenerationActionType.TRANSITION, 2, 3], - [WizardGenerationActionType.CLOSE_ACCEPT, 3, undefined], - ]; - const res = await axios.get( - `${process.env.TEST_LIGHTSPEED_URL}/__debug__/feedbacks`, - ); - expect(res.data.feedbacks.length).equals(expected.length); - for (let i = 0; i < expected.length; i++) { - const evt: PlaybookGenerationActionEvent = - res.data.feedbacks[i].playbookGenerationAction; - expect(evt.action).equals(expected[i][0]); - expect(evt.fromPage).equals(expected[i][1]); - expect(evt.toPage).equals(expected[i][2]); - } - }); - - it("Playbook generation webview works as expected (fast path)", async function () { - // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - - // Open playbook generation webview. - await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); - await sleep(2000); - const webView = await getWebviewByLocator( - By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), - ); - - // Set input text and invoke summaries API - const textArea = await webView.findWebElement( - By.xpath("//vscode-text-area"), - ); - expect(textArea, "textArea should not be undefined").not.to.be.undefined; - const submitButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='submit-button']"), - ); - expect(submitButton, "submitButton should not be undefined").not.to.be - .undefined; - // - // Note: Following line should succeed, but fails for some unknown reasons. - // - // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; - await textArea.sendKeys("Create an azure network."); - expect( - await submitButton.isEnabled(), - "submit button should be enabled now", - ).to.be.true; - await submitButton.click(); - await sleep(1000); - - // Verify outline output and text edit - const outlineList = await webView.findWebElement( - By.xpath("//ol[@id='outline-list']"), - ); - expect(outlineList, "An ordered list should exist.").to.be.not.undefined; - let text = await outlineList.getText(); - expect(text.includes("Create virtual network peering")).to.be.true; - - // Verify the prompt is displayed as a static text - const prompt = await webView.findWebElement( - By.xpath("//span[@id='prompt']"), - ); - text = await prompt.getText(); - expect(text.includes("Create an azure network.")).to.be.true; - - // Click Generate playbook button to invoke the generations API - const generatePlaybookButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='generate-button']"), - ); - expect( - generatePlaybookButton, - "generatePlaybookButton should not be undefined", - ).not.to.be.undefined; - - const start = new Date().getTime(); - await generatePlaybookButton.click(); - await sleep(300); - - // Verify a playbook was generated. - const formattedCode = await webView.findWebElement( - By.xpath("//span[@id='formatted-code']"), - ); - expect(formattedCode, "formattedCode should not be undefined").not.to.be - .undefined; - text = await formattedCode.getText(); - expect(text.startsWith("---")).to.be.true; - - // Make sure the playbook was generated within 500 msecs, which is the fake latency - // used in the mock server. It means that the playbook returned in the outline generation - // was used and the generations API was not called this time. - const elapsedTime = new Date().getTime() - start; - expect(elapsedTime < 500).to.be.true; - - // Click Open editor button to open the generated playbook in the editor - const openEditorButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='open-editor-button']"), - ); - expect(openEditorButton, "openEditorButton should not be undefined").not - .to.be.undefined; - await openEditorButton.click(); - await sleep(500); - await webView.switchBack(); - - const editor = await new EditorView().openEditor("Untitled-1"); - text = await editor.getText(); - expect( - text.startsWith("---"), - 'The generated playbook should start with "---"', - ).to.be.true; - - await workbenchExecuteCommand("View: Close All Editor Groups"); - const dialog = new ModalDialog(); - await dialog.pushButton(`Don't Save`); - - /* verify generated events */ - const expected = [ - [WizardGenerationActionType.OPEN, undefined, 1], - [WizardGenerationActionType.TRANSITION, 1, 2], - [WizardGenerationActionType.TRANSITION, 2, 3], - [WizardGenerationActionType.CLOSE_ACCEPT, 3, undefined], - ]; - const res = await axios.get( - `${process.env.TEST_LIGHTSPEED_URL}/__debug__/feedbacks`, - ); - expect(res.data.feedbacks.length).equals(expected.length); - for (let i = 0; i < expected.length; i++) { - const evt: PlaybookGenerationActionEvent = - res.data.feedbacks[i].playbookGenerationAction; - expect(evt.action).equals(expected[i][0]); - expect(evt.fromPage).equals(expected[i][1]); - expect(evt.toPage).equals(expected[i][2]); - } - }); - - it("Playbook generation webview works as expected (feature unavailable)", async function () { - // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - - // Open playbook generation webview. - await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); - await sleep(2000); - const webView = await getWebviewByLocator( - By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), - ); - - // Set input text and invoke summaries API - const textArea = await webView.findWebElement( - By.xpath("//vscode-text-area"), - ); - expect(textArea, "textArea should not be undefined").not.to.be.undefined; - const submitButton = await webView.findWebElement( - By.xpath("//vscode-button[@id='submit-button']"), - ); - expect(submitButton, "submitButton should not be undefined").not.to.be - .undefined; - // - // Note: Following line should succeed, but fails for some unknown reasons. - // - // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; - await textArea.sendKeys("Feature not available"); - expect( - await submitButton.isEnabled(), - "submit button should be enabled now", - ).to.be.true; - await submitButton.click(); - await sleep(2000); - - await webView.switchBack(); - - const notifications = await workbench.getNotifications(); - const notification = notifications[0]; - expect(await notification.getMessage()).equals( - "The requested action is not available in your environment.", - ); - }); - - it("Playbook generation webview (multiple instances)", async function () { - // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - - // Ensure all previous instances are closed - await workbenchExecuteCommand("View: Close All Editor Groups"); - await sleep(1000); - - // Open playbook generation webview. - await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); - await sleep(1000); - - // Open another playbook generation webview. - await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); - await sleep(1000); - - const editorView = new EditorView(); - const titles = await editorView.getOpenEditorTitles(); - expect( - titles.filter((value) => value === "Ansible Lightspeed").length, - ).to.equal(2); - - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - it("Playbook explanation webview works as expected", async function () { - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - - const folder = "lightspeed"; - const file = "playbook_4.yml"; - const filePath = getFixturePath(folder, file); - - // Open file in the editor - await VSBrowser.instance.openResources(filePath); - - // Open playbook explanation webview. - await workbenchExecuteCommand( - "Explain the playbook with Ansible Lightspeed", - ); - await sleep(2000); - - // Locate the playbook explanation webview - let webView = (await new EditorView().openEditor( - "Explanation", - 1, - )) as WebView; - expect(webView, "webView should not be undefined").not.to.be.undefined; - webView = await getWebviewByLocator( - By.xpath("//div[@class='playbookGeneration']"), - ); - await webView.findWebElement( - By.xpath("//h2[contains(text(), 'Playbook Overview and Structure')]"), - ); - - await webView.switchBack(); - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - it("Playbook explanation webview works as expected (feature unavailable)", async function () { - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - const folder = "lightspeed"; - const file = "playbook_explanation_feature_unavailable.yml"; - const filePath = getFixturePath(folder, file); - - // Open file in the editor - await VSBrowser.instance.openResources(filePath); - - // Open playbook explanation webview. - await workbenchExecuteCommand( - "Explain the playbook with Ansible Lightspeed", - ); - await sleep(2000); - - // Locate the group 1 of editor view. Since the file does not contain the "hosts" property, - // the explanation view is not opened in the group 1. Therefore, the group 1 should be - // undefined. - const group = await new EditorView().getEditorGroup(1); - expect(group, "Group 1 of the editor view should be undefined").to.be - .undefined; - - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - it("Playbook explanation webview with a playbook with no tasks", async function () { - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - - const folder = "lightspeed"; - const file = "playbook_5.yml"; - const filePath = getFixturePath(folder, file); - - // Open file in the editor - await VSBrowser.instance.openResources(filePath); - - // Open playbook explanation webview. - await workbenchExecuteCommand( - "Explain the playbook with Ansible Lightspeed", - ); - await sleep(2000); - - await new EditorView().openEditor("Explanation", 1); - // Locate the playbook explanation webview - const webView = await getWebviewByLocator( - By.xpath("//div[contains(@class, 'playbookGeneration') ]"), - ); - // Find the main div element of the webview and verify the expected text is found. - const mainDiv = await webView.findWebElement( - By.xpath("//div[contains(@class, 'playbookGeneration') ]"), - ); - expect(mainDiv, "mainDiv should not be undefined").not.to.be.undefined; - const text = await mainDiv.getText(); - expect( - text.includes( - "Explaining a playbook with no tasks in the playbook is not supported.", - ), - ).to.be.true; - - await webView.switchBack(); - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - it("Playbook explanation webview works as expected, no explanation", async function () { - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - const folder = "lightspeed"; - const file = "playbook_explanation_none.yml"; - const filePath = getFixturePath(folder, file); - - // Open file in the editor - await VSBrowser.instance.openResources(filePath); - - // Open playbook explanation webview. - await workbenchExecuteCommand( - "Explain the playbook with Ansible Lightspeed", - ); - await sleep(2000); - - // Locate the playbook explanation webview - await new EditorView().openEditor("Explanation", 1); - const webView = await getWebviewByLocator( - By.xpath("//div[contains(@class, 'playbookGeneration') ]"), - ); - - // Find the main div element of the webview and verify the expected text is found. - const mainDiv = await webView.findWebElement( - By.xpath("//div[contains(@class, 'playbookGeneration') ]"), - ); - expect(mainDiv, "mainDiv should not be undefined").not.to.be.undefined; - const text = await mainDiv.getText(); - expect(text.includes("No explanation provided")).to.be.true; - - await webView.switchBack(); - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - after(async function () { - if (process.env.TEST_LIGHTSPEED_URL) { - settingsEditor = await openSettings(); - await updateSettings( - settingsEditor, - "ansible.lightspeed.enabled", - false, - ); - await updateSettings( - settingsEditor, - "ansible.lightspeed.URL", - "https://c.ai.ansible.redhat.com", - ); - } - }); - }); - - describe("Verify playbook generation page is not opened when Lightspeed is not enabled", function () { - before(async function () { - // await workbenchExecuteCommand( - // "Ansible Lightspeed: Enable experimental features", - // ); - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - - it("Playbook generation command shows an error message when Lightspeed is not enabled", async function () { - // Open playbook generation webview. - await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); - await sleep(2000); - const notifications = await new Workbench().getNotifications(); - const notification = notifications[0]; - expect(await notification.getMessage()).equals( - "Enable lightspeed services from settings to use the feature.", - ); - }); - }); - - describe("Feedback webview provider works as expected", function () { - let editorView: EditorView; - let settingsEditor: SettingsEditor; - - before(async function () { - if (process.env.TEST_LIGHTSPEED_URL) { - settingsEditor = await openSettings(); - await updateSettings( - settingsEditor, - "ansible.lightspeed.enabled", - true, - ); - await workbenchExecuteCommand("View: Close All Editor Groups"); - editorView = new EditorView(); - expect(editorView).not.to.be.undefined; - } - }); - - it("Open Feedback webview", async function () { - // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. - if (!process.env.TEST_LIGHTSPEED_URL) { - this.skip(); - } - await workbenchExecuteCommand("Ansible Lightspeed: Feedback"); - await sleep(2000); - // Locate the playbook explanation webview - const webView = (await editorView.openEditor( - "Ansible Lightspeed Feedback", - )) as WebView; - expect(webView, "webView should not be undefined").not.to.be.undefined; - // Issuing the Lightspeed feedback command should not open a new tab - await workbenchExecuteCommand("Ansible Lightspeed: Feedback"); - await sleep(2000); - const titles = await editorView.getOpenEditorTitles(); - expect(titles.length).equals(1); - await workbenchExecuteCommand("View: Close All Editor Groups"); - }); - }); -} diff --git a/test/ui-test/lightspeedUiTestActiveBarTest.ts b/test/ui-test/lightspeedUiTestActiveBarTest.ts new file mode 100644 index 000000000..4da305ce1 --- /dev/null +++ b/test/ui-test/lightspeedUiTestActiveBarTest.ts @@ -0,0 +1,68 @@ +import { expect, config } from "chai"; +import { + ActivityBar, + By, + SideBarView, + ViewControl, + WebView, + ViewSection, +} from "vscode-extension-tester"; +import { + sleep, + updateSettings, + getWebviewByLocator, + openSettings, +} from "./uiTestHelper"; + +config.truncateThreshold = 0; + +describe("Verify the presence of lightspeed login button in the activity bar", () => { + let view: ViewControl; + let sideBar: SideBarView; + let adtView: ViewSection; + let webviewView: WebView; + + before(async function () { + const settingsEditor = await openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); + + view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; + sideBar = await view.openView(); + + await sideBar.getContent().getSection("Ansible Lightspeed"); + + adtView = await sideBar + .getContent() + .getSection("Ansible Development Tools"); + adtView.collapse(); + + await sleep(2000); + + webviewView = await getWebviewByLocator( + By.xpath("//vscode-button[text()='Connect']"), + ); + }); + + after(async function () { + if (webviewView) { + await webviewView.switchBack(); + } + const settingsEditor = await openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", false); + }); + + it("Ansible Lightspeed welcome message is present", async function () { + const body = await webviewView.findWebElement(By.xpath("//body")); + const welcomeMessage = await body.getText(); + expect(welcomeMessage).to.contain( + "Experience smarter automation using Ansible Lightspeed", + ); + }); + + it("Ansible Lightspeed login button is present", async function () { + const loginButton = await webviewView.findWebElement( + By.xpath("//vscode-button[text()='Connect']"), + ); + expect(loginButton).not.undefined; + }); +}); diff --git a/test/ui-test/lightspeedUiTestPlaybookExpTest.ts b/test/ui-test/lightspeedUiTestPlaybookExpTest.ts new file mode 100644 index 000000000..114326804 --- /dev/null +++ b/test/ui-test/lightspeedUiTestPlaybookExpTest.ts @@ -0,0 +1,89 @@ +// BEFORE: ansible.lightspeed.enabled: true + +import { expect, config } from "chai"; +import { By, VSBrowser, EditorView, WebView } from "vscode-extension-tester"; +import { + getFixturePath, + sleep, + getWebviewByLocator, + workbenchExecuteCommand, +} from "./uiTestHelper"; + +config.truncateThreshold = 0; + +describe("Verify playbook explanation features work as expected", function () { + beforeEach(function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + }); + + it("Playbook explanation webview with a playbook with no tasks", async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + + const folder = "lightspeed"; + const file = "playbook_5.yml"; + const filePath = getFixturePath(folder, file); + + // Open file in the editor + await VSBrowser.instance.openResources(filePath); + + // Open playbook explanation webview. + await workbenchExecuteCommand( + "Explain the playbook with Ansible Lightspeed", + ); + await sleep(2000); + + await new EditorView().openEditor("Explanation", 1); + // Locate the playbook explanation webview + const webView = await getWebviewByLocator( + By.xpath("//div[contains(@class, 'playbookGeneration') ]"), + ); + // Find the main div element of the webview and verify the expected text is found. + const mainDiv = await webView.findWebElement( + By.xpath("//div[contains(@class, 'playbookGeneration') ]"), + ); + expect(mainDiv, "mainDiv should not be undefined").not.to.be.undefined; + const text = await mainDiv.getText(); + expect( + text.includes( + "Explaining a playbook with no tasks in the playbook is not supported.", + ), + ).to.be.true; + + await webView.switchBack(); + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); +}); + +describe("Feedback webview provider works as expected", function () { + let editorView: EditorView; + + before(async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + return; + } + await workbenchExecuteCommand("View: Close All Editor Groups"); + editorView = new EditorView(); + expect(editorView).not.to.be.undefined; + }); + + it("Open Feedback webview", async function () { + // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. + await workbenchExecuteCommand("Ansible Lightspeed: Feedback"); + await sleep(2000); + // Locate the playbook explanation webview + const webView = (await editorView.openEditor( + "Ansible Lightspeed Feedback", + )) as WebView; + expect(webView, "webView should not be undefined").not.to.be.undefined; + // Issuing the Lightspeed feedback command should not open a new tab + await workbenchExecuteCommand("Ansible Lightspeed: Feedback"); + await sleep(2000); + const titles = await editorView.getOpenEditorTitles(); + expect(titles.length).equals(1); + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); +}); diff --git a/test/ui-test/lightspeedUiTestPlaybookExpTestNoExpTest.ts b/test/ui-test/lightspeedUiTestPlaybookExpTestNoExpTest.ts new file mode 100644 index 000000000..3b0c589bc --- /dev/null +++ b/test/ui-test/lightspeedUiTestPlaybookExpTestNoExpTest.ts @@ -0,0 +1,57 @@ +// BEFORE: ansible.lightspeed.enabled: true + +import { expect, config } from "chai"; +import { By, VSBrowser, EditorView } from "vscode-extension-tester"; +import { + getFixturePath, + sleep, + getWebviewByLocator, + workbenchExecuteCommand, +} from "./uiTestHelper"; + +config.truncateThreshold = 0; + +describe("Verify playbook explanation features when no explanation is returned", function () { + beforeEach(function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + }); + + it("Playbook explanation webview works as expected, no explanation", async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + const folder = "lightspeed"; + const file = "playbook_explanation_none.yml"; + const filePath = getFixturePath(folder, file); + + // Open file in the editor + await VSBrowser.instance.openResources(filePath); + + // Open playbook explanation webview. + await workbenchExecuteCommand( + "Explain the playbook with Ansible Lightspeed", + ); + await sleep(5000); + + // Locate the playbook explanation webview + await new EditorView().openEditor("Explanation", 1); + const webView = await getWebviewByLocator( + By.xpath("//div[contains(@class, 'playbookGeneration') ]"), + ); + + // Find the main div element of the webview and verify the expected text is found. + const mainDiv = await webView.findWebElement( + By.xpath("//div[contains(@class, 'playbookGeneration') ]"), + ); + expect(mainDiv, "mainDiv should not be undefined").not.to.be.undefined; + await sleep(5000); + const text = await mainDiv.getText(); + console.log(`text: ${text}`); + expect(text.includes("No explanation provided")).to.be.true; + + await webView.switchBack(); + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); +}); diff --git a/test/ui-test/lightspeedUiTestPlaybookExpTestNoLS.ts b/test/ui-test/lightspeedUiTestPlaybookExpTestNoLS.ts new file mode 100644 index 000000000..748a913db --- /dev/null +++ b/test/ui-test/lightspeedUiTestPlaybookExpTestNoLS.ts @@ -0,0 +1,33 @@ +import { expect } from "chai"; +import { Workbench } from "vscode-extension-tester"; +import { + sleep, + updateSettings, + workbenchExecuteCommand, + openSettings, +} from "./uiTestHelper"; + +describe("Verify playbook generation page is not opened when Lightspeed is not enabled", function () { + before(async function () { + // await workbenchExecuteCommand( + // "Ansible Lightspeed: Enable experimental features", + // ); + const settingsEditor = await openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", false); + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); + + it("Playbook generation command shows an error message when Lightspeed is not enabled", async function () { + // Open playbook generation webview. + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); + await sleep(2000); + const notifications = await new Workbench().getNotifications(); + const notification = notifications[0]; + expect(await notification.getMessage()).equals( + "Enable lightspeed services from settings to use the feature.", + ); + }); +}); diff --git a/test/ui-test/lightspeedUiTestPlaybookGenTest.ts b/test/ui-test/lightspeedUiTestPlaybookGenTest.ts new file mode 100644 index 000000000..c2e9266e3 --- /dev/null +++ b/test/ui-test/lightspeedUiTestPlaybookGenTest.ts @@ -0,0 +1,571 @@ +// BEFORE: ansible.lightspeed.enabled: true + +import { expect, config } from "chai"; +import axios from "axios"; +import { + ActivityBar, + By, + ViewControl, + Workbench, + VSBrowser, + EditorView, + WebView, + ModalDialog, + ViewSection, + WebviewView, + WebElement, +} from "vscode-extension-tester"; +import { + getFixturePath, + sleep, + getWebviewByLocator, + workbenchExecuteCommand, +} from "./uiTestHelper"; +import { WizardGenerationActionType } from "../../src/definitions/lightspeed"; +import { PlaybookGenerationActionEvent } from "../../src/interfaces/lightspeed"; + +config.truncateThreshold = 0; + +describe("Verify playbook generation features work as expected", function () { + let workbench: Workbench; + let outlineList: WebElement; + let resetButton: WebElement; + let adtView: ViewSection; + let webviewView: InstanceType; + + beforeEach(function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + }); + + before(async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + return; + } + workbench = new Workbench(); + // await workbenchExecuteCommand( + // "Ansible Lightspeed: Enable experimental features", + // ); + await workbenchExecuteCommand("View: Close All Editor Groups"); + + const notifications = await workbench.getNotifications(); + for (let i = 0; i < notifications.length; i++) { + const n = notifications[i]; + await n.dismiss(); + } + }); + + it("Playbook generation webview works as expected (full path) - part 1", async function () { + // Open Ansible Development Tools by clicking the Getting started button on the side bar + const view = (await new ActivityBar().getViewControl( + "Ansible", + )) as ViewControl; + const sideBar = await view.openView(); + adtView = await sideBar + .getContent() + .getSection("Ansible Development Tools"); + adtView.expand(); + await sleep(1000); + + webviewView = new WebviewView(); + expect(webviewView).not.undefined; + await webviewView.switchToFrame(1000); + + const welcomePageLink = await webviewView.findWebElement( + By.xpath( + "//a[contains(@title, 'Ansible Development Tools welcome page')]", + ), + ); + + expect(welcomePageLink).not.to.be.undefined; + if (welcomePageLink) { + await welcomePageLink.click(); + } + await sleep(5000); + webviewView.switchBack(); + + const contentCreatorWebView = await getWebviewByLocator( + By.xpath( + "//a[contains(@href,'command:ansible.lightspeed.playbookGeneration')]", + ), + ); + const createContentButton = await contentCreatorWebView.findWebElement( + By.xpath( + "//a[contains(@href,'command:ansible.lightspeed.playbookGeneration')]", + ), + ); + expect(createContentButton).not.to.be.undefined; + if (createContentButton) { + await createContentButton.click(); + } + await contentCreatorWebView.switchBack(); + await new EditorView().closeEditor("Ansible Development Tools"); + await sleep(2000); + + // Start operations on Playbook Generation UI + const webView = await getWebviewByLocator( + By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), + ); + + // Set input text and invoke summaries API + const textArea = await webView.findWebElement( + By.xpath("//vscode-text-area"), + ); + const submitButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='submit-button']"), + ); + expect(submitButton, "submitButton should not be undefined").not.to.be + .undefined; + // + // Note: Following line should succeed, but fails for some unknown reasons. + // + // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; + await textArea.sendKeys("Create an azure network."); + expect( + await submitButton.isEnabled(), + "submit button should be enabled now", + ).to.be.true; + await submitButton.click(); + await sleep(2000); + + // Verify outline output and text edit + outlineList = await webView.findWebElement( + By.xpath("//ol[@id='outline-list']"), + ); + expect(outlineList, "An ordered list should exist."); + let text = await outlineList.getText(); + expect( + text.includes("Create virtual network peering"), + "Text should include the expected outline", + ); + await outlineList.sendKeys("# COMMENT\n"); + text = await outlineList.getText(); + expect(text.includes("# COMMENT\n")); + + // Verify the prompt is displayed as a static text + const prompt = await webView.findWebElement( + By.xpath("//span[@id='prompt']"), + ); + text = await prompt.getText(); + expect(text.includes("Create an azure network.")); + + // Test Reset button + resetButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='reset-button']"), + ); + expect(resetButton, "resetButton should not be undefined").not.to.be + .undefined; + await resetButton.click(); + await sleep(500); + text = await outlineList.getText(); + expect(!text.includes("# COMMENT\n")); + + // Test Back button + const backButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='back-button']"), + ); + expect(backButton, "backButton should not be undefined").not.to.be + .undefined; + await backButton.click(); + await sleep(500); + + text = await textArea.getText(); + expect(text.startsWith("Create an azure network.")); + await submitButton.click(); + await sleep(1000); + text = await outlineList.getText(); + expect(text.includes("Create virtual network peering")); + + // Test Edit link next to the prompt text + const backAnchor = await webView.findWebElement( + By.xpath("//a[@id='back-anchor']"), + ); + expect(backAnchor, "backAnchor should not be undefined").not.to.be + .undefined; + await backAnchor.click(); + await sleep(500); + + text = await textArea.getText(); + expect(text.startsWith("Create an azure network.")); + await submitButton.click(); + await sleep(1000); + text = await outlineList.getText(); + expect(text.includes("Create virtual network peering")); + }); + + it("Playbook generation webview works as expected (full path) - part 2", async function () { + const webView = await getWebviewByLocator( + By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), + ); + // Click Generate playbook button to invoke the generations API + const generatePlaybookButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='generate-button']"), + ); + expect( + generatePlaybookButton, + "generatePlaybookButton should not be undefined", + ).not.to.be.undefined; + + // Input "(status=400)" to simulate an API error + await outlineList.sendKeys("(status=400)"); + let text = await outlineList.getText(); + expect(text.includes("(status=400)")); + await generatePlaybookButton.click(); // Click Generate Playbook button + await sleep(2000); + + resetButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='reset-button']"), + ); + // Click reset button and make sure the string "(status=400)" is removed + await resetButton.click(); + await sleep(500); + text = await outlineList.getText(); + expect(!text.includes("(status=400)")); + + // Click Generate Playbook button again + await generatePlaybookButton.click(); + await sleep(2000); + + // Make sure the generated playbook is displayed + const formattedCode = await webView.findWebElement( + By.xpath("//span[@id='formatted-code']"), + ); + expect(formattedCode, "formattedCode should not be undefined").not.to.be + .undefined; + await sleep(500); + text = await formattedCode.getText(); + expect(text.startsWith("---")).to.be.true; + + // Test Back (to Page 2) button + const backToPage2Button = await webView.findWebElement( + By.xpath("//vscode-button[@id='back-to-page2-button']"), + ); + expect(backToPage2Button, "backToPage2Button should not be undefined").not + .to.be.undefined; + await backToPage2Button.click(); + await sleep(500); + + // Type in something extra + await outlineList.sendKeys("\nSomething extra"); + const savedOutline = await outlineList.getText(); + + // Click generate playbook button again + generatePlaybookButton.click(); + await sleep(2000); + + // Click Back page again + await backToPage2Button.click(); + await sleep(500); + + // Make sure outline is not updated. + expect(savedOutline).equal(await outlineList.getText()); + + // Click generate playbook button again + generatePlaybookButton.click(); + await sleep(500); + + // Click Open editor button to open the generated playbook in the editor + const openEditorButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='open-editor-button']"), + ); + expect(openEditorButton, "openEditorButton should not be undefined").not.to + .be.undefined; + await openEditorButton.click(); + await sleep(2000); + await webView.switchBack(); + + // Verify a playbook was generated. + const editor = await new EditorView().openEditor("Untitled-1"); + await sleep(2000); + + text = await editor.getText(); + expect( + text.startsWith("---"), + 'The generated playbook should start with "---"', + ).to.be.true; + + await workbenchExecuteCommand("View: Close All Editor Groups"); + const dialog = new ModalDialog(); + await dialog.pushButton(`Don't Save`); + + /* verify generated events */ + const expected = [ + [WizardGenerationActionType.OPEN, undefined, 1], + [WizardGenerationActionType.TRANSITION, 1, 2], + [WizardGenerationActionType.TRANSITION, 2, 1], + [WizardGenerationActionType.TRANSITION, 1, 2], + [WizardGenerationActionType.TRANSITION, 2, 1], + [WizardGenerationActionType.TRANSITION, 1, 2], + [WizardGenerationActionType.TRANSITION, 2, 3], + [WizardGenerationActionType.TRANSITION, 3, 2], + [WizardGenerationActionType.TRANSITION, 2, 3], + [WizardGenerationActionType.TRANSITION, 3, 2], + [WizardGenerationActionType.TRANSITION, 2, 3], + [WizardGenerationActionType.CLOSE_ACCEPT, 3, undefined], + ]; + const res = await axios.get( + `${process.env.TEST_LIGHTSPEED_URL}/__debug__/feedbacks`, + ); + expect(res.data.feedbacks.length).equals(expected.length); + for (let i = 0; i < expected.length; i++) { + const evt: PlaybookGenerationActionEvent = + res.data.feedbacks[i].playbookGenerationAction; + expect(evt.action).equals(expected[i][0]); + expect(evt.fromPage).equals(expected[i][1]); + expect(evt.toPage).equals(expected[i][2]); + } + }); + + it("Playbook generation webview works as expected (fast path)", async function () { + // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + + // Open playbook generation webview. + await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); + await sleep(2000); + const webView = await getWebviewByLocator( + By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), + ); + + // Set input text and invoke summaries API + const textArea = await webView.findWebElement( + By.xpath("//vscode-text-area"), + ); + expect(textArea, "textArea should not be undefined").not.to.be.undefined; + const submitButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='submit-button']"), + ); + expect(submitButton, "submitButton should not be undefined").not.to.be + .undefined; + // + // Note: Following line should succeed, but fails for some unknown reasons. + // + // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; + await textArea.sendKeys("Create an azure network."); + expect( + await submitButton.isEnabled(), + "submit button should be enabled now", + ).to.be.true; + await submitButton.click(); + await sleep(1000); + + // Verify outline output and text edit + const outlineList = await webView.findWebElement( + By.xpath("//ol[@id='outline-list']"), + ); + expect(outlineList, "An ordered list should exist.").to.be.not.undefined; + let text = await outlineList.getText(); + expect(text.includes("Create virtual network peering")).to.be.true; + + // Verify the prompt is displayed as a static text + const prompt = await webView.findWebElement( + By.xpath("//span[@id='prompt']"), + ); + text = await prompt.getText(); + expect(text.includes("Create an azure network.")).to.be.true; + + // Click Generate playbook button to invoke the generations API + const generatePlaybookButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='generate-button']"), + ); + expect( + generatePlaybookButton, + "generatePlaybookButton should not be undefined", + ).not.to.be.undefined; + + const start = new Date().getTime(); + await generatePlaybookButton.click(); + await sleep(300); + + // Verify a playbook was generated. + const formattedCode = await webView.findWebElement( + By.xpath("//span[@id='formatted-code']"), + ); + expect(formattedCode, "formattedCode should not be undefined").not.to.be + .undefined; + text = await formattedCode.getText(); + expect(text.startsWith("---")).to.be.true; + + // Make sure the playbook was generated within 500 msecs, which is the fake latency + // used in the mock server. It means that the playbook returned in the outline generation + // was used and the generations API was not called this time. + const elapsedTime = new Date().getTime() - start; + expect(elapsedTime < 500).to.be.true; + + // Click Open editor button to open the generated playbook in the editor + const openEditorButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='open-editor-button']"), + ); + expect(openEditorButton, "openEditorButton should not be undefined").not.to + .be.undefined; + await openEditorButton.click(); + await sleep(500); + await webView.switchBack(); + + const editor = await new EditorView().openEditor("Untitled-1"); + text = await editor.getText(); + expect( + text.startsWith("---"), + 'The generated playbook should start with "---"', + ).to.be.true; + + await workbenchExecuteCommand("View: Close All Editor Groups"); + const dialog = new ModalDialog(); + await dialog.pushButton(`Don't Save`); + + /* verify generated events */ + const expected = [ + [WizardGenerationActionType.OPEN, undefined, 1], + [WizardGenerationActionType.TRANSITION, 1, 2], + [WizardGenerationActionType.TRANSITION, 2, 3], + [WizardGenerationActionType.CLOSE_ACCEPT, 3, undefined], + ]; + const res = await axios.get( + `${process.env.TEST_LIGHTSPEED_URL}/__debug__/feedbacks`, + ); + expect(res.data.feedbacks.length).equals(expected.length); + for (let i = 0; i < expected.length; i++) { + const evt: PlaybookGenerationActionEvent = + res.data.feedbacks[i].playbookGenerationAction; + expect(evt.action).equals(expected[i][0]); + expect(evt.fromPage).equals(expected[i][1]); + expect(evt.toPage).equals(expected[i][2]); + } + }); + + it("Playbook generation webview works as expected (feature unavailable)", async function () { + // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + + // Open playbook generation webview. + await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); + await sleep(2000); + const webView = await getWebviewByLocator( + By.xpath("//*[text()='Create a playbook with Ansible Lightspeed']"), + ); + + // Set input text and invoke summaries API + const textArea = await webView.findWebElement( + By.xpath("//vscode-text-area"), + ); + expect(textArea, "textArea should not be undefined").not.to.be.undefined; + const submitButton = await webView.findWebElement( + By.xpath("//vscode-button[@id='submit-button']"), + ); + expect(submitButton, "submitButton should not be undefined").not.to.be + .undefined; + // + // Note: Following line should succeed, but fails for some unknown reasons. + // + // expect((await submitButton.isEnabled()), "submit button should be disabled by default").is.false; + await textArea.sendKeys("Feature not available"); + expect( + await submitButton.isEnabled(), + "submit button should be enabled now", + ).to.be.true; + await submitButton.click(); + await sleep(2000); + + await webView.switchBack(); + + const notifications = await workbench.getNotifications(); + const notification = notifications[0]; + expect(await notification.getMessage()).equals( + "The requested action is not available in your environment.", + ); + }); + + it("Playbook generation webview (multiple instances)", async function () { + // Execute only when TEST_LIGHTSPEED_URL environment variable is defined. + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + + // Ensure all previous instances are closed + await workbenchExecuteCommand("View: Close All Editor Groups"); + await sleep(1000); + + // Open playbook generation webview. + await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); + await sleep(1000); + + // Open another playbook generation webview. + await workbenchExecuteCommand("Ansible Lightspeed: Playbook generation"); + await sleep(1000); + + const editorView = new EditorView(); + const titles = await editorView.getOpenEditorTitles(); + expect( + titles.filter((value) => value === "Ansible Lightspeed").length, + ).to.equal(2); + + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); + + it("Playbook explanation webview works as expected", async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + + const folder = "lightspeed"; + const file = "playbook_4.yml"; + const filePath = getFixturePath(folder, file); + + // Open file in the editor + await VSBrowser.instance.openResources(filePath); + + // Open playbook explanation webview. + await workbenchExecuteCommand( + "Explain the playbook with Ansible Lightspeed", + ); + await sleep(2000); + + // Locate the playbook explanation webview + let webView = (await new EditorView().openEditor( + "Explanation", + 1, + )) as WebView; + expect(webView, "webView should not be undefined").not.to.be.undefined; + webView = await getWebviewByLocator( + By.xpath("//div[@class='playbookGeneration']"), + ); + await webView.findWebElement( + By.xpath("//h2[contains(text(), 'Playbook Overview and Structure')]"), + ); + + await webView.switchBack(); + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); + + it("Playbook explanation webview works as expected (feature unavailable)", async function () { + if (!process.env.TEST_LIGHTSPEED_URL) { + this.skip(); + } + const folder = "lightspeed"; + const file = "playbook_explanation_feature_unavailable.yml"; + const filePath = getFixturePath(folder, file); + + // Open file in the editor + await VSBrowser.instance.openResources(filePath); + + // Open playbook explanation webview. + await workbenchExecuteCommand( + "Explain the playbook with Ansible Lightspeed", + ); + await sleep(2000); + + // Locate the group 1 of editor view. Since the file does not contain the "hosts" property, + // the explanation view is not opened in the group 1. Therefore, the group 1 should be + // undefined. + const group = await new EditorView().getEditorGroup(1); + expect(group, "Group 1 of the editor view should be undefined").to.be + .undefined; + + await workbenchExecuteCommand("View: Close All Editor Groups"); + }); +}); diff --git a/test/ui-test/lightspeedUiTestStatusBarAndExplorerViewTest.ts b/test/ui-test/lightspeedUiTestStatusBarAndExplorerViewTest.ts new file mode 100644 index 000000000..a5f2b5b44 --- /dev/null +++ b/test/ui-test/lightspeedUiTestStatusBarAndExplorerViewTest.ts @@ -0,0 +1,94 @@ +import { expect, config } from "chai"; +import { + By, + StatusBar, + VSBrowser, + EditorView, + SettingsEditor, +} from "vscode-extension-tester"; +import { + getFixturePath, + sleep, + updateSettings, + workbenchExecuteCommand, + openSettings, + getWebviewByLocator, +} from "./uiTestHelper"; + +config.truncateThreshold = 0; + +describe("Verify the presence of lightspeed element in the status bar and the explorer view", () => { + let statusBar: StatusBar; + let settingsEditor: SettingsEditor; + let editorView: EditorView; + const folder = "lightspeed"; + const file = "playbook_1.yml"; + const filePath = getFixturePath(folder, file); + + before(async function () { + statusBar = new StatusBar(); + editorView = new EditorView(); + + // open file in the editor + await VSBrowser.instance.openResources(filePath); + }); + + it("Ansible Lightspeed status bar item absent when settings not enabled", async function () { + await editorView.openEditor(file); + + // The following lines replaced the original code that was using StatusBar.getItem() API. + const items = await statusBar.findElements( + By.xpath( + "//div[contains(@class, 'statusbar-item') and " + + ".//a/text()='Lightspeed (not logged in))']", + ), + ); + expect(items.length).equals(0); + }); + + it("Connect button exists in Lightspeed explorer view when settings not enabled", async function () { + await workbenchExecuteCommand("Ansible: Focus on Ansible Lightspeed View"); + await sleep(5000); + const explorerView = await getWebviewByLocator( + By.id("lightspeed-explorer-connect"), + ); + await explorerView.switchBack(); + }); + + it("Ansible Lightspeed status bar item present when only lightspeed is enabled (with warning color)", async () => { + settingsEditor = await openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", true); + await editorView.openEditor(file); + + // The following lines replaced the original code that was using StatusBar.getItem() API. + const lightspeedStatusBarItem = await statusBar.findElement( + By.xpath( + "//div[contains(@class, 'statusbar-item') and " + + "contains(@class, 'has-background-color') and " + + "contains(@class, 'warning-kind') and " + + ".//a/text()='Lightspeed (Not logged in)']", + ), + ); + expect(lightspeedStatusBarItem).not.to.be.undefined; + }); + + it("Ansible Lightspeed status bar item present when lightspeed and lightspeed suggestions are enabled (with normal color)", async () => { + settingsEditor = await openSettings(); + await updateSettings( + settingsEditor, + "ansible.lightspeed.suggestions.enabled", + true, + ); + await editorView.openEditor(file); + + // The following lines replaced the original code that was using StatusBar.getItem() API. + const lightspeedStatusBarItem = await statusBar.findElement( + By.xpath( + "//div[contains(@class, 'statusbar-item') and " + + "not (contains(@class, 'has-background-color')) and " + + ".//a/text()='Lightspeed (Not logged in)']", + ), + ); + expect(lightspeedStatusBarItem).not.to.be.undefined; + }); +}); diff --git a/test/ui-test/terminalUiTest.ts b/test/ui-test/terminalUiTest.ts index 3e076970a..8ac60b4df 100644 --- a/test/ui-test/terminalUiTest.ts +++ b/test/ui-test/terminalUiTest.ts @@ -10,125 +10,121 @@ import { getFixturePath, updateSettings, sleep } from "./uiTestHelper"; config.truncateThreshold = 0; -export function terminalUITests(): void { - describe("Verify the execution of playbook using ansible-playbook command", () => { - let workbench: Workbench; - let settingsEditor: SettingsEditor; - const folder = "terminal"; - const file = "playbook.yml"; - const playbookFile = getFixturePath(folder, file); - - before(async function () { - workbench = new Workbench(); - }); - it("Execute ansible-playbook command with arg", async function () { - settingsEditor = await workbench.openSettings(); - await updateSettings( - settingsEditor, - "ansible.playbook.arguments", - "--syntax-check", - ); +describe("Verify the execution of playbook using ansible-playbook command", () => { + let workbench: Workbench; + let settingsEditor: SettingsEditor; + const folder = "terminal"; + const file = "playbook.yml"; + const playbookFile = getFixturePath(folder, file); + + before(async function () { + workbench = new Workbench(); + }); + it("Execute ansible-playbook command with arg", async function () { + settingsEditor = await workbench.openSettings(); + await updateSettings( + settingsEditor, + "ansible.playbook.arguments", + "--syntax-check", + ); - await VSBrowser.instance.openResources(playbookFile); + await VSBrowser.instance.openResources(playbookFile); - await workbench.executeCommand("Run playbook via `ansible-playbook`"); + await workbench.executeCommand("Run playbook via `ansible-playbook`"); - const terminalView = await new BottomBarPanel().openTerminalView(); - const text = await terminalView.getText(); + const terminalView = await new BottomBarPanel().openTerminalView(); + const text = await terminalView.getText(); - expect(text).contains("ansible-playbook --syntax-check"); - await terminalView.killTerminal(); - }); - it("Execute ansible-playbook command without arg", async function () { - const settingsEditor = await workbench.openSettings(); - await updateSettings(settingsEditor, "ansible.playbook.arguments", " "); - await VSBrowser.instance.openResources(playbookFile); + expect(text).contains("ansible-playbook --syntax-check"); + await terminalView.killTerminal(); + }); + it("Execute ansible-playbook command without arg", async function () { + const settingsEditor = await workbench.openSettings(); + await updateSettings(settingsEditor, "ansible.playbook.arguments", " "); + await VSBrowser.instance.openResources(playbookFile); - await workbench.executeCommand("Run playbook via `ansible-playbook`"); + await workbench.executeCommand("Run playbook via `ansible-playbook`"); - const terminalView = await new BottomBarPanel().openTerminalView(); - const text = await terminalView.getText(); + const terminalView = await new BottomBarPanel().openTerminalView(); + const text = await terminalView.getText(); - expect(text).contains("ansible-playbook "); - expect(text).does.not.contain("ansible-playbook --"); + expect(text).contains("ansible-playbook "); + expect(text).does.not.contain("ansible-playbook --"); - await terminalView.killTerminal(); - }); + await terminalView.killTerminal(); }); - describe("Verify the execution of playbook using ansible-navigator command", () => { - let workbench: Workbench; - let settingsEditor: SettingsEditor; - const folder = "terminal"; - const file = "playbook.yml"; - const playbookFile = getFixturePath(folder, file); - before(async function () { - workbench = new Workbench(); - }); - // Skip this test on macOS due to CI container settings - it("Execute playbook with ansible-navigator EE mode", async function () { - if (process.platform !== "darwin") { - settingsEditor = await workbench.openSettings(); - await updateSettings( - settingsEditor, - "ansible.executionEnvironment.enabled", - true, - ); - await updateSettings( - settingsEditor, - "ansible.executionEnvironment.containerEngine", - "podman", - ); - - await VSBrowser.instance.openResources(playbookFile); - - await workbench.executeCommand( - "Run playbook via `ansible-navigator run`", - ); - await sleep(3500); - - const terminalView = await new BottomBarPanel().openTerminalView(); - const text = await terminalView.getText(); - - // assert with just "Play " rather than "Play name" due to CI output formatting issues - expect(text).contains("Play "); - await terminalView.killTerminal(); - } - }); - it("Execute playbook with ansible-navigator without EE mode", async function () { +}); +describe("Verify the execution of playbook using ansible-navigator command", () => { + let workbench: Workbench; + let settingsEditor: SettingsEditor; + const folder = "terminal"; + const file = "playbook.yml"; + const playbookFile = getFixturePath(folder, file); + before(async function () { + workbench = new Workbench(); + }); + // Skip this test on macOS due to CI container settings + it("Execute playbook with ansible-navigator EE mode", async function () { + if (process.platform !== "darwin") { settingsEditor = await workbench.openSettings(); await updateSettings( settingsEditor, "ansible.executionEnvironment.enabled", - false, + true, + ); + await updateSettings( + settingsEditor, + "ansible.executionEnvironment.containerEngine", + "podman", ); + await VSBrowser.instance.openResources(playbookFile); + await workbench.executeCommand( - "Run playbook via `ansible-navigator run``", + "Run playbook via `ansible-navigator run`", ); - await sleep(3000); + await sleep(3500); const terminalView = await new BottomBarPanel().openTerminalView(); const text = await terminalView.getText(); - await terminalView.killTerminal(); // assert with just "Play " rather than "Play name" due to CI output formatting issues expect(text).contains("Play "); - }); - after(async function () { - const folder = "terminal"; - const fixtureFolder = getFixturePath(folder) + "/"; - settingsEditor = await workbench.openSettings(); - - await updateSettings( - settingsEditor, - "ansible.executionEnvironment.containerEngine", - "docker", - ); - fs.readdirSync(fixtureFolder).forEach((file) => { - if (file.includes("playbook-artifact")) { - fs.unlinkSync(fixtureFolder + file); - } - }); + await terminalView.killTerminal(); + } + }); + it("Execute playbook with ansible-navigator without EE mode", async function () { + settingsEditor = await workbench.openSettings(); + await updateSettings( + settingsEditor, + "ansible.executionEnvironment.enabled", + false, + ); + await VSBrowser.instance.openResources(playbookFile); + await workbench.executeCommand("Run playbook via `ansible-navigator run``"); + await sleep(3000); + + const terminalView = await new BottomBarPanel().openTerminalView(); + const text = await terminalView.getText(); + await terminalView.killTerminal(); + + // assert with just "Play " rather than "Play name" due to CI output formatting issues + expect(text).contains("Play "); + }); + after(async function () { + const folder = "terminal"; + const fixtureFolder = getFixturePath(folder) + "/"; + settingsEditor = await workbench.openSettings(); + + await updateSettings( + settingsEditor, + "ansible.executionEnvironment.containerEngine", + "docker", + ); + fs.readdirSync(fixtureFolder).forEach((file) => { + if (file.includes("playbook-artifact")) { + fs.unlinkSync(fixtureFolder + file); + } }); }); -} +}); diff --git a/test/ui-test/walkthroughUiTest.ts b/test/ui-test/walkthroughUiTest.ts index 0712045a9..3bb63ad5a 100644 --- a/test/ui-test/walkthroughUiTest.ts +++ b/test/ui-test/walkthroughUiTest.ts @@ -9,102 +9,94 @@ import { import { updateSettings, sleep } from "./uiTestHelper"; config.truncateThreshold = 0; -export function walkthroughUiTest(): void { - let workbench: Workbench; - let editorView: EditorView; - let settingsEditor: SettingsEditor; - before(async () => { - workbench = new Workbench(); - editorView = new EditorView(); - }); +let workbench: Workbench; +let editorView: EditorView; +let settingsEditor: SettingsEditor; - after(async function () { - if (editorView) { - await editorView.closeAllEditors(); - } - }); - describe("Check walkthroughs, elements and associated commands", async () => { - const walkthroughs = [ - [ - "Create an Ansible environment", - [ - "Create an Ansible playbook", - "tag in the status bar", - "Install the Ansible environment package", - ], - ], +before(async () => { + workbench = new Workbench(); + editorView = new EditorView(); +}); + +describe("Check walkthroughs, elements and associated commands", async () => { + const walkthroughs = [ + [ + "Create an Ansible environment", [ - "Discover Ansible Development Tools", - ["Create", "Test", "Deploy", "Where do I start"], + "Create an Ansible playbook", + "tag in the status bar", + "Install the Ansible environment package", ], + ], + [ + "Discover Ansible Development Tools", + ["Create", "Test", "Deploy", "Where do I start"], + ], + [ + "Start automating with your first Ansible playbook", [ - "Start automating with your first Ansible playbook", - [ - "Enable Ansible Lightspeed", - "Create an Ansible playbook project", - "Create an Ansible playbook", - "Save your playbook to a playbook project", - "Learn more about playbooks", - ], + "Enable Ansible Lightspeed", + "Create an Ansible playbook project", + "Create an Ansible playbook", + "Save your playbook to a playbook project", + "Learn more about playbooks", ], - ]; + ], + ]; - walkthroughs.forEach(([walkthroughName, steps]) => { - it(`Open the ${walkthroughName} walkthrough and check elements`, async function () { - const commandInput = await workbench.openCommandPrompt(); - await workbench.executeCommand("Welcome: Open Walkthrough"); - await commandInput.setText(`${walkthroughName}`); - await commandInput.confirm(); + walkthroughs.forEach(([walkthroughName, steps]) => { + it(`Open the ${walkthroughName} walkthrough and check elements`, async function () { + const commandInput = await workbench.openCommandPrompt(); + await workbench.executeCommand("Welcome: Open Walkthrough"); + await commandInput.setText(`${walkthroughName}`); + await commandInput.confirm(); - await sleep(1000); + await sleep(1000); - // Select the editor window - const welcomeTab = await editorView.getTabByTitle( - "Walkthrough: Ansible", - ); - expect(welcomeTab).is.not.undefined; + // Select the editor window + const welcomeTab = await editorView.getTabByTitle("Walkthrough: Ansible"); + expect(welcomeTab).is.not.undefined; - // Locate walkthrough title text - const titleText = await welcomeTab - .findElement( - By.xpath("//div[contains(@class, 'getting-started-category') ]"), - ) - .getText(); - expect( - titleText.includes(`${walkthroughName}`), - `${walkthroughName} title not found`, - ).to.be.true; + // Locate walkthrough title text + const titleText = await welcomeTab + .findElement( + By.xpath("//div[contains(@class, 'getting-started-category') ]"), + ) + .getText(); + expect( + titleText.includes(`${walkthroughName}`), + `${walkthroughName} title not found`, + ).to.be.true; - // Locate one of the steps - const fullStepText = await welcomeTab - .findElement( - By.xpath("//div[contains(@class, 'step-list-container') ]"), - ) - .getText(); + // Locate one of the steps + const fullStepText = await welcomeTab + .findElement( + By.xpath("//div[contains(@class, 'step-list-container') ]"), + ) + .getText(); - const stepText = fullStepText.split("\n")[0]; - expect(steps, "No walkthrough step").to.include(stepText); - }); + const stepText = fullStepText.split("\n")[0]; + expect(steps, "No walkthrough step").to.include(stepText); }); + }); - it("Check empty playbook command option", async function () { - settingsEditor = await workbench.openSettings(); - await updateSettings(settingsEditor, "ansible.lightspeed.enabled", false); - await workbench.executeCommand( - "Ansible: Create an empty playbook or with Lightspeed (if enabled)", - ); - await sleep(500); + it("Check empty playbook command option", async function () { + settingsEditor = await workbench.openSettings(); + await updateSettings(settingsEditor, "ansible.lightspeed.enabled", false); + await workbench.executeCommand( + "Ansible: Create an empty playbook or with Lightspeed (if enabled)", + ); + await sleep(500); - const newFileEditor = await new EditorView().openEditor("Untitled-1"); - const startingText = await newFileEditor.getText(); - expect( - startingText.startsWith("---"), - "The playbook file should start with ---", - ).to.be.true; - await workbench.executeCommand("View: Close All Editor Groups"); - const dialogBox = new ModalDialog(); - await dialogBox.pushButton(`Don't Save`); - }); + const newFileEditor = await new EditorView().openEditor("Untitled-1"); + const startingText = await newFileEditor.getText(); + expect( + startingText.startsWith("---"), + "The playbook file should start with ---", + ).to.be.true; + await workbench.executeCommand("View: Close All Editor Groups"); + const dialogBox = new ModalDialog(); + await dialogBox.pushButton(`Don't Save`); }); -} +}); diff --git a/test/ui-test/welcomePageUITest.ts b/test/ui-test/welcomePageUITest.ts index cd66b5309..7f8b277cb 100644 --- a/test/ui-test/welcomePageUITest.ts +++ b/test/ui-test/welcomePageUITest.ts @@ -14,118 +14,117 @@ import { } from "./uiTestHelper"; config.truncateThreshold = 0; -export function welcomePageUITest(): void { - describe("Verify welcome page is displayed as expected", async () => { - let view: ViewControl; - let sideBar: SideBarView; - let webviewView: InstanceType; - let adtSection: ViewSection; - - before(async () => { - // Open Ansible Development Tools by clicking the Getting started button on the side bar - view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; - sideBar = await view.openView(); - await sleep(2000); - - // to get the content part - adtSection = await sideBar - .getContent() - .getSection("Ansible Development Tools"); - adtSection.expand(); - }); - - it("check for title and get started button", async function () { - const title = await adtSection.getTitle(); - - await workbenchExecuteCommand( - "Ansible: Focus on Ansible Development Tools View", - ); - - webviewView = new WebviewView(); - expect(webviewView).not.undefined; - await webviewView.switchToFrame(1000); - - const body = await webviewView.findWebElement(By.xpath("//body")); - const welcomeMessage = await body.getText(); - expect(welcomeMessage).to.contain("LAUNCH"); - - const getStartedLink = await webviewView.findWebElement( - By.xpath( - "//a[contains(@title, 'Ansible Development Tools welcome page')]", - ), - ); - - expect(title).not.to.be.undefined; - expect(title).to.equals("Ansible Development Tools"); - expect(getStartedLink).not.to.be.undefined; - - if (getStartedLink) { - await getStartedLink.click(); - } - await sleep(3000); - webviewView.switchBack(); - }); - - it("check for header and subtitle", async function () { - const welcomePageWebView = await getWebviewByLocator( - By.xpath("//h1[text()='Ansible Development Tools']"), - ); - - const adtHeaderTitle = await welcomePageWebView.findWebElement( - By.xpath("//h1[text()='Ansible Development Tools']"), - ); - expect(adtHeaderTitle).not.to.be.undefined; - expect(await adtHeaderTitle.getText()).to.equals( - "Ansible Development Tools", - ); - - const adtSubheader = await welcomePageWebView.findWebElement( - By.className("subtitle description"), - ); - expect(adtSubheader).not.to.be.undefined; - expect(await adtSubheader.getText()).includes( - "Create, test and deploy Ansible content", - ); - - await welcomePageWebView.switchBack(); - }); - - it("Check if start and walkthrough list section is visible", async () => { - const welcomePageWebView = await getWebviewByLocator( - By.className("index-list start-container"), - ); - - const startSection = await welcomePageWebView.findWebElement( - By.className("index-list start-container"), - ); - - expect(startSection).not.to.be.undefined; - - const playbookWithLightspeedOption = await startSection.findElement( - By.css("h3"), - ); - - expect(await playbookWithLightspeedOption.getText()).to.equal( - "Playbook with Ansible Lightspeed", - ); - - const walkthroughSection = await getWebviewByLocator( - By.xpath("//button[@class='walkthrough-item']"), - ); - const walkthroughItems = await walkthroughSection.findWebElements( - By.xpath("//button[@class='walkthrough-item']"), - ); - expect(walkthroughItems.length).to.greaterThanOrEqual(2); - - const firstWalkthrough = await walkthroughSection.findWebElement( - By.xpath("//h3[contains(text(), 'Create an Ansible environment')]"), - ); - expect(await firstWalkthrough.getText()).to.equal( - "Create an Ansible environment", - ); - await firstWalkthrough.click(); - await sleep(2000); - await welcomePageWebView.switchBack(); - }); + +describe("Verify welcome page is displayed as expected", async () => { + let view: ViewControl; + let sideBar: SideBarView; + let webviewView: InstanceType; + let adtSection: ViewSection; + + before(async () => { + // Open Ansible Development Tools by clicking the Getting started button on the side bar + view = (await new ActivityBar().getViewControl("Ansible")) as ViewControl; + sideBar = await view.openView(); + await sleep(2000); + + // to get the content part + adtSection = await sideBar + .getContent() + .getSection("Ansible Development Tools"); + adtSection.expand(); + }); + + it("check for title and get started button", async function () { + const title = await adtSection.getTitle(); + + await workbenchExecuteCommand( + "Ansible: Focus on Ansible Development Tools View", + ); + + webviewView = new WebviewView(); + expect(webviewView).not.undefined; + await webviewView.switchToFrame(1000); + + const body = await webviewView.findWebElement(By.xpath("//body")); + const welcomeMessage = await body.getText(); + expect(welcomeMessage).to.contain("LAUNCH"); + + const getStartedLink = await webviewView.findWebElement( + By.xpath( + "//a[contains(@title, 'Ansible Development Tools welcome page')]", + ), + ); + + expect(title).not.to.be.undefined; + expect(title).to.equals("Ansible Development Tools"); + expect(getStartedLink).not.to.be.undefined; + + if (getStartedLink) { + await getStartedLink.click(); + } + await sleep(3000); + webviewView.switchBack(); + }); + + it("check for header and subtitle", async function () { + const welcomePageWebView = await getWebviewByLocator( + By.xpath("//h1[text()='Ansible Development Tools']"), + ); + + const adtHeaderTitle = await welcomePageWebView.findWebElement( + By.xpath("//h1[text()='Ansible Development Tools']"), + ); + expect(adtHeaderTitle).not.to.be.undefined; + expect(await adtHeaderTitle.getText()).to.equals( + "Ansible Development Tools", + ); + + const adtSubheader = await welcomePageWebView.findWebElement( + By.className("subtitle description"), + ); + expect(adtSubheader).not.to.be.undefined; + expect(await adtSubheader.getText()).includes( + "Create, test and deploy Ansible content", + ); + + await welcomePageWebView.switchBack(); + }); + + it("Check if start and walkthrough list section is visible", async () => { + const welcomePageWebView = await getWebviewByLocator( + By.className("index-list start-container"), + ); + + const startSection = await welcomePageWebView.findWebElement( + By.className("index-list start-container"), + ); + + expect(startSection).not.to.be.undefined; + + const playbookWithLightspeedOption = await startSection.findElement( + By.css("h3"), + ); + + expect(await playbookWithLightspeedOption.getText()).to.equal( + "Playbook with Ansible Lightspeed", + ); + + const walkthroughSection = await getWebviewByLocator( + By.xpath("//button[@class='walkthrough-item']"), + ); + const walkthroughItems = await walkthroughSection.findWebElements( + By.xpath("//button[@class='walkthrough-item']"), + ); + expect(walkthroughItems.length).to.greaterThanOrEqual(2); + + const firstWalkthrough = await walkthroughSection.findWebElement( + By.xpath("//h3[contains(text(), 'Create an Ansible environment')]"), + ); + expect(await firstWalkthrough.getText()).to.equal( + "Create an Ansible environment", + ); + await firstWalkthrough.click(); + await sleep(2000); + await welcomePageWebView.switchBack(); }); -} +}); diff --git a/tools/test-launcher.sh b/tools/test-launcher.sh index c5cc9876e..a4f2a1ede 100755 --- a/tools/test-launcher.sh +++ b/tools/test-launcher.sh @@ -5,8 +5,11 @@ set -o pipefail cleanup() { - pkill -P $$ - wait + echo "Final clean up" + if [ -s out/log/express.log ]; then + cat out/log/express.log + cat out/log/mock-server.log + fi } trap "cleanup" HUP INT ABRT BUS TERM EXIT @@ -20,9 +23,31 @@ MOCK_LIGHTSPEED_API="${MOCK_LIGHTSPEED_API:-}" TEST_TYPE="${TEST_TYPE:-ui}" # e2e or ui COVERAGE_ARG="" - OPTSTRING=":c" +function start_server() { + echo "🚀starting the mockLightspeedServer" + if [[ -n "${TEST_LIGHTSPEED_URL}" ]]; then + echo "MOCK_LIGHTSPEED_API is true, the existing TEST_LIGHTSPEED_URL envvar will be ignored!" + fi + mkdir -p out/log + export TEST_LIGHTSPEED_ACCESS_TOKEN=dummy + (DEBUG='express:*' node ./out/client/test/mockLightspeedServer/server.js >>out/log/express.log 2>&1 ) & + sleep 1 + grep 'Listening on port' out/log/express.log + export TEST_LIGHTSPEED_URL=$(sed -n 's,.*Listening on port \([0-9]*\) at \(.*\)".*,http://\2:\1,p' out/log/express.log|tail -n1) +} + +function stop_server() { + if [[ "$MOCK_LIGHTSPEED_API" == "1" ]]; then + curl "${TEST_LIGHTSPEED_URL}/__debug__/kill" || echo "ok" + echo "" > out/log/express.log + echo "" > out/log/mock-server.log + fi + +} + + while getopts ${OPTSTRING} opt; do case ${opt} in c) @@ -41,18 +66,6 @@ if [[ "${COVERAGE}" == "1" ]]; then fi -if [[ "$MOCK_LIGHTSPEED_API" == "1" ]]; then - if [[ -n "${TEST_LIGHTSPEED_URL}" ]]; then - echo "MOCK_LIGHTSPEED_API is true, the existing TEST_LIGHTSPEED_URL envvar will be ignored!" - fi - mkdir -p out/log - TEST_LIGHTSPEED_ACCESS_TOKEN=dummy - (DEBUG='express:*' node ./out/client/test/mockLightspeedServer/server.js >>out/log/express.log 2>&1 ) & - sleep 2 - grep 'Listening on port' out/log/express.log - TEST_LIGHTSPEED_URL=$(sed -n 's,.*Listening on port \([0-9]*\) at \(.*\)".*,http://\2:\1,p' out/log/express.log|tail -n1) -fi - # Start the mock Lightspeed server and run UI tests with the new VS Code @@ -76,16 +89,35 @@ if [[ "$COVERAGE" == "" ]]; then fi ${EXTEST} install-from-marketplace redhat.vscode-yaml ms-python.python -e out/ext -s out/test-resources - -export TEST_LIGHTSPEED_URL -export TEST_LIGHTSPEED_ACCESS_TOKEN export COVERAGE +cp test/testFixtures/settings.json out/settings.json +if [ "${TEST_LIGHTSPEED_URL}" != "" ]; then + sed -i.bak "s,https://c.ai.ansible.redhat.com,$TEST_LIGHTSPEED_URL," out/settings.json +fi + + if [[ "${TEST_TYPE}" == "ui" ]]; then - ${EXTEST} run-tests "${COVERAGE_ARG}" -s out/test-resources -e out/ext --code_settings test/testFixtures/settings.json out/client/test/ui-test/allTestsSuite.js - exit $? + # shellcheck disable=SC2044 + + + for test_file in $(find out/client/test/ui-test/ -name '*Test.js'); do + echo "🧐testing ${test_file}" + + if [[ "$MOCK_LIGHTSPEED_API" == "1" ]]; then + start_server + fi + + sed -i.bak 's/"ansible.lightspeed.enabled": .*/"ansible.lightspeed.enabled": false,/' out/settings.json + if grep "// BEFORE: ansible.lightspeed.enabled: true" "${test_file}"; then + sed -i.bak 's/"ansible.lightspeed.enabled": .*,/"ansible.lightspeed.enabled": true,/' out/settings.json + sed -i.bak 's/"ansible.lightspeed.suggestions.enabled": .*,/"ansible.lightspeed.suggestions.enabled": true,/' out/settings.json + fi + + ${EXTEST} run-tests "${COVERAGE_ARG}" -s out/test-resources -e out/ext --code_settings out/settings.json "${test_file}" + stop_server + done fi if [[ "${TEST_TYPE}" == "e2e" ]]; then node ./out/client/test/testRunner - exit $? fi