From 24fc6dce9f14fa8df6f6900d0d53bd1b5f3e5b31 Mon Sep 17 00:00:00 2001 From: Cory Rivera Date: Wed, 29 Jun 2022 10:23:29 -0700 Subject: [PATCH] Add uninstall step to manage packages smoke test to clean up installed packages. (#19854) --- .../src/sql/managePackagesDialog.ts | 43 ++++++++++++++++++- test/automation/src/workbench.ts | 2 +- .../src/sql/areas/notebook/notebook.test.ts | 33 ++++++++++++-- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/test/automation/src/sql/managePackagesDialog.ts b/test/automation/src/sql/managePackagesDialog.ts index 0512782262..e0ed9f43cc 100644 --- a/test/automation/src/sql/managePackagesDialog.ts +++ b/test/automation/src/sql/managePackagesDialog.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Code } from '../code'; +import { QuickInput } from '../quickinput'; import { Dialog } from './dialog'; const MANAGE_PACKAGES_DIALOG_TITLE = 'Manage Packages'; @@ -11,7 +12,7 @@ const MANAGE_PACKAGES_DIALOG_TITLE = 'Manage Packages'; export class ManagePackagesDialog extends Dialog { private static readonly dialogPage = '.modal .modal-body .dialogModal-pane'; - constructor(code: Code) { + constructor(code: Code, private readonly quickInput: QuickInput) { super(MANAGE_PACKAGES_DIALOG_TITLE, code); } @@ -60,4 +61,44 @@ export class ManagePackagesDialog extends Dialog { return packageVersion; } + + async removePackage(packageName: string, clickOnTab: boolean = false): Promise { + // When the dialog is first opened, the Installed Packages tab is already open, which causes + // clicking on its page tab to fail. So we skip clicking on the page tab by default, but can + // re-enable it if mixing install and uninstall operations in the same test. + if (clickOnTab) { + const installedPkgTab = `${ManagePackagesDialog.dialogPage} div[class="tab-header"][aria-controls="dialogPane.Manage Packages.0"]`; + await this.code.waitAndClick(installedPkgTab); + } + + // Wait for initial loading spinner to disappear + const loadingSpinner = `${ManagePackagesDialog.dialogPage} div.modelview-loadingComponent-spinner`; + await this.code.waitForElement(loadingSpinner); + await this.code.waitForElementGone(loadingSpinner); + + // Click on package row in installed packages list to select it for uninstall + const packageRow = `${ManagePackagesDialog.dialogPage} div[role="gridcell"][aria-label="${packageName}"]`; + await this.code.waitAndClick(packageRow); + + // Tab over to uninstall button on the right side of the row. Can't select the uninstall button + // directly since it doesn't have any package name info associated with it. + await this.code.dispatchKeybinding('tab'); + await this.code.dispatchKeybinding('tab'); + await this.code.dispatchKeybinding('enter'); + + // Click Yes on quick select + const quickInputAccept = 'Yes'; + await this.quickInput.waitForQuickInputOpened(); + await this.quickInput.waitForQuickInputElements(names => names[0] === quickInputAccept); + await this.quickInput.submit(quickInputAccept); + + // Wait for uninstall loading spinner to disappear + await this.code.waitForElement(loadingSpinner); + await this.code.waitForElementGone(loadingSpinner); + + // Close dialog + const closeButton = '.modal .modal-footer a[class="monaco-button monaco-text-button"][aria-label="Close"][aria-disabled="false"]'; + await this.code.waitAndClick(closeButton); + await this.waitForDialogGone(); + } } diff --git a/test/automation/src/workbench.ts b/test/automation/src/workbench.ts index 6365c0b56d..0fd2b61be8 100644 --- a/test/automation/src/workbench.ts +++ b/test/automation/src/workbench.ts @@ -99,7 +99,7 @@ export class Workbench { this.sqlNotebook = new SqlNotebook(code, this.quickaccess, this.quickinput, this.editors); this.createBookDialog = new CreateBookDialog(code); this.configurePythonDialog = new ConfigurePythonDialog(code); - this.managePackagesDialog = new ManagePackagesDialog(code); + this.managePackagesDialog = new ManagePackagesDialog(code, this.quickinput); this.addRemoteBookDialog = new AddRemoteBookDialog(code); this.taskPanel = new TaskPanel(code, this.quickaccess); // {{END}} diff --git a/test/smoke/src/sql/areas/notebook/notebook.test.ts b/test/smoke/src/sql/areas/notebook/notebook.test.ts index c164faffa2..c1bc77b684 100644 --- a/test/smoke/src/sql/areas/notebook/notebook.test.ts +++ b/test/smoke/src/sql/areas/notebook/notebook.test.ts @@ -114,7 +114,10 @@ export function setup(opts: minimist.ParsedArgs) { await app.workbench.sqlNotebook.waitForActiveCellResults(); }); - it('can add a new package from the Manage Packages wizard', async function () { + it('can add and remove new package from the Manage Packages wizard', async function () { + // Use arrow package so that it's at the top of the packages list when uninstalling later + const testPackageName = 'arrow'; + const app = this.app as Application; await app.workbench.sqlNotebook.newUntitledNotebook(); await app.workbench.sqlNotebook.notebookToolbar.waitForKernel('SQL'); @@ -122,20 +125,42 @@ export function setup(opts: minimist.ParsedArgs) { await configurePython(app); await app.workbench.sqlNotebook.notebookToolbar.waitForKernel('Python 3'); + const importTestCode = `import ${testPackageName}`; await app.workbench.sqlNotebook.addCell('code'); - await app.workbench.sqlNotebook.waitForTypeInEditor('import pyarrow'); + await app.workbench.sqlNotebook.waitForTypeInEditor(importTestCode); await app.workbench.sqlNotebook.runActiveCell(); await app.workbench.sqlNotebook.waitForJupyterErrorOutput(); await app.workbench.sqlNotebook.notebookToolbar.managePackages(); await app.workbench.managePackagesDialog.waitForManagePackagesDialog(); - let packageVersion = await app.workbench.managePackagesDialog.addNewPackage('pyarrow'); + let packageVersion = await app.workbench.managePackagesDialog.addNewPackage(testPackageName); await app.workbench.taskPanel.showTaskPanel(); - await app.workbench.taskPanel.waitForTaskComplete(`Installing pyarrow ${packageVersion} succeeded`); + await app.workbench.taskPanel.waitForTaskComplete(`Installing ${testPackageName} ${packageVersion} succeeded`); // There should be no error output when running the cell after pyarrow has been installed await app.workbench.sqlNotebook.runActiveCell(); await app.workbench.sqlNotebook.waitForActiveCellResultsGone(); + + // Uninstall package and check if it throws the expected import error. + // This also functions as cleanup for subsequent test runs, since the test + // assumes the package isn't installed by default. + await app.workbench.sqlNotebook.notebookToolbar.managePackages(); + await app.workbench.managePackagesDialog.waitForManagePackagesDialog(); + await app.workbench.managePackagesDialog.removePackage(testPackageName); + await app.workbench.taskPanel.showTaskPanel(); + await app.workbench.taskPanel.waitForTaskComplete(`Uninstalling ${testPackageName} ${packageVersion} succeeded`); + + // Open a new notebook to verify that the package is uninstalled, since the old notebook's + // python instance retains a cached copy of the successfully imported module. + await app.workbench.quickaccess.runCommand('workbench.action.revertAndCloseActiveEditor'); + await app.workbench.sqlNotebook.newUntitledNotebook(); + await app.workbench.sqlNotebook.notebookToolbar.waitForKernel('SQL'); + await app.workbench.sqlNotebook.notebookToolbar.changeKernel('Python 3'); + await app.workbench.sqlNotebook.notebookToolbar.waitForKernel('Python 3'); + await app.workbench.sqlNotebook.addCell('code'); + await app.workbench.sqlNotebook.waitForTypeInEditor(importTestCode); + await app.workbench.sqlNotebook.runActiveCell(); + await app.workbench.sqlNotebook.waitForJupyterErrorOutput(); }); it('can open ipynb file, run all, and save notebook with outputs', async function () {