Add simple notebook smoke test (#12898)

* add simple notebook smoke test

* add id for notebook dropdown elements
This commit is contained in:
Lucy Zhang
2020-10-14 05:58:32 -07:00
committed by GitHub
parent a0c03784f2
commit 7bc26cc493
6 changed files with 131 additions and 7 deletions

View File

@@ -350,6 +350,7 @@ export class SelectBox extends vsSelectBox {
super.render(selectContainer);
this.selectElement.classList.add('action-item-label');
this.selectElement.id = selectOptions.id;
}
else {
super.render(container);
@@ -364,4 +365,5 @@ export class SelectBox extends vsSelectBox {
export interface ISelectBoxOptionsWithLabel extends ISelectBoxOptions {
labelText?: string;
labelOnTop?: boolean;
id?: string;
}

View File

@@ -275,12 +275,13 @@ export class CollapseCellsAction extends ToggleableAction {
const showAllKernelsConfigName = 'notebook.showAllKernels';
const workbenchPreviewConfigName = 'workbench.enablePreviewFeatures';
export const noKernelName = localize('noKernel', "No Kernel");
const kernelDropdownElementId = 'kernel-dropdown';
export class KernelsDropdown extends SelectBox {
private model: NotebookModel;
private _showAllKernels: boolean = false;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise<INotebookModel>, @IConfigurationService private _configurationService: IConfigurationService) {
super([msgLoading], msgLoading, contextViewProvider, container, { labelText: kernelLabel, labelOnTop: false, ariaLabel: kernelLabel } as ISelectBoxOptionsWithLabel);
super([msgLoading], msgLoading, contextViewProvider, container, { labelText: kernelLabel, labelOnTop: false, ariaLabel: kernelLabel, id: kernelDropdownElementId } as ISelectBoxOptionsWithLabel);
if (modelReady) {
modelReady
@@ -353,6 +354,8 @@ export class KernelsDropdown extends SelectBox {
}
}
const attachToDropdownElementId = 'attach-to-dropdown';
export class AttachToDropdown extends SelectBox {
private model: NotebookModel;
@@ -363,7 +366,7 @@ export class AttachToDropdown extends SelectBox {
@INotificationService private _notificationService: INotificationService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
) {
super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false, ariaLabel: attachToLabel } as ISelectBoxOptionsWithLabel);
super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false, ariaLabel: attachToLabel, id: attachToDropdownElementId } as ISelectBoxOptionsWithLabel);
if (modelReady) {
modelReady
.then(model => {

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Code } from '../code';
import { Dialog } from './dialog';
const CONFIGURE_PYTHON_DIALOG_TITLE = 'Configure Python to run Python 3 kernel';
export class ConfigurePythonDialog extends Dialog {
constructor(code: Code) {
super(CONFIGURE_PYTHON_DIALOG_TITLE, code);
}
async waitForConfigurePythonDialog(): Promise<void> {
await this.waitForNewDialog();
}
async installPython(): Promise<void> {
const dialog = '.modal .modal-dialog';
await this.code.waitAndClick(dialog);
const newPythonInstallation = '.modal .modal-body input[aria-label="New Python installation"]';
await this.code.waitAndClick(newPythonInstallation);
const nextButton = '.modal-dialog .modal-content .modal-footer .right-footer .footer-button a[aria-label="Next"][aria-disabled="false"]';
await this.code.waitForElement(nextButton);
await this.code.dispatchKeybinding('enter');
const installButton = '.modal-dialog .modal-content .modal-footer .right-footer .footer-button a[aria-label="Install"][aria-disabled="false"]';
await this.code.waitForElement(installButton);
await this.code.dispatchKeybinding('enter');
return this.waitForDialogGone();
}
}

View File

@@ -8,17 +8,82 @@ import { QuickAccess } from '../quickaccess';
import { QuickInput } from '../quickinput';
import { Editors } from '../editors';
const winOrCtrl = process.platform === 'darwin' ? 'ctrl' : 'win';
export class Notebook {
public readonly toolbar: NotebookToolbar;
constructor(private code: Code, private quickAccess: QuickAccess, private quickInput: QuickInput, private editors: Editors) {
this.toolbar = new NotebookToolbar(code);
}
async openFile(fileName: string): Promise<void> {
await this.quickAccess.openQuickAccess(fileName);
await this.quickInput.waitForQuickInputElements(names => names[0] === fileName);
await this.code.dispatchKeybinding('enter');
await this.editors.waitForActiveTab(fileName);
await this.code.waitForElement('.notebookEditor');
}
async newUntitledNotebook(): Promise<void> {
await this.code.dispatchKeybinding(winOrCtrl + '+alt+n');
await this.editors.waitForActiveTab('Notebook-0');
await this.code.waitForElement('.notebookEditor');
}
async addCell(cellType: 'markdown' | 'code'): Promise<void> {
if (cellType === 'markdown') {
await this.code.dispatchKeybinding(winOrCtrl + '+shift+t');
} else {
await this.code.dispatchKeybinding(winOrCtrl + '+shift+c');
}
await this.code.waitForElement('.notebook-cell.active');
}
async changeKernel(kernel: string): Promise<void> {
await this.toolbar.changeKernel(kernel);
}
async runActiveCell(): Promise<void> {
await this.code.dispatchKeybinding('F5');
}
async runAllCells(): Promise<void> {
await this.code.dispatchKeybinding(winOrCtrl + '+shift+F5');
}
async waitForTypeInEditor(text: string) {
const editor = '.notebook-cell.active .monaco-editor';
await this.code.waitAndClick(editor);
const textarea = `${editor} textarea`;
await this.code.waitForActiveElement(textarea);
await this.code.waitForTypeInEditor(textarea, text);
await this._waitForActiveCellEditorContents(c => c.indexOf(text) > -1);
}
private async _waitForActiveCellEditorContents(accept: (contents: string) => boolean): Promise<any> {
const selector = '.notebook-cell.active .monaco-editor .view-lines';
return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' ')));
}
async waitForResults(): Promise<void> {
const outputComponent = '.notebook-cell.active .notebook-output';
await this.code.waitForElement(outputComponent);
}
}
export class NotebookToolbar {
private static readonly toolbarSelector = '.notebookEditor .editor-toolbar .actions-container';
constructor(private code: Code) { }
async changeKernel(kernel: string): Promise<void> {
const kernelDropdown = `${NotebookToolbar.toolbarSelector} select[id="kernel-dropdown"]`;
await this.code.waitForSetValue(kernelDropdown, kernel);
await this.code.dispatchKeybinding('enter');
}
}

View File

@@ -27,6 +27,7 @@ import { Profiler } from './sql/profiler';
import { QueryEditors } from './sql/queryEditors';
import { QueryEditor } from './sql/queryEditor';
import { Notebook as SqlNotebook } from './sql/notebook';
import { ConfigurePythonDialog } from './sql/configurePythonDialog';
// {{END}}
export interface Commands {
@@ -57,7 +58,8 @@ export class Workbench {
readonly profiler: Profiler;
readonly queryEditors: QueryEditors;
readonly queryEditor: QueryEditor;
readonly sqlNotebbok: SqlNotebook;
readonly sqlNotebook: SqlNotebook;
readonly configurePythonDialog: ConfigurePythonDialog;
// {{END}}
constructor(code: Code, userDataPath: string) {
@@ -81,7 +83,8 @@ export class Workbench {
this.profiler = new Profiler(code, this.quickaccess);
this.queryEditors = new QueryEditors(code, this.editors);
this.queryEditor = new QueryEditor(code);
this.sqlNotebbok = new SqlNotebook(code, this.quickaccess, this.quickinput, this.editors);
this.sqlNotebook = new SqlNotebook(code, this.quickaccess, this.quickinput, this.editors);
this.configurePythonDialog = new ConfigurePythonDialog(code);
// {{END}}
this.notebook = new Notebook(this.quickaccess, code);
}

View File

@@ -7,9 +7,21 @@ import { Application } from '../../../../../automation';
export function setup() {
describe('Notebook', () => {
it('open ', async function () {
it('can open new notebook, configure Python, and execute one cell', async function () {
const app = this.app as Application;
await app.workbench.sqlNotebbok.openFile('hello.ipynb');
await app.workbench.sqlNotebook.newUntitledNotebook();
await app.workbench.sqlNotebook.addCell('code');
await app.workbench.sqlNotebook.waitForTypeInEditor('print("Hello world!")');
await app.workbench.sqlNotebook.changeKernel('Python 3');
await app.workbench.configurePythonDialog.waitForConfigurePythonDialog();
await app.workbench.configurePythonDialog.installPython();
await app.workbench.sqlNotebook.runActiveCell();
await app.workbench.sqlNotebook.waitForResults();
await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor');
});
});
}