mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Add simple notebook smoke test (#12898)
* add simple notebook smoke test * add id for notebook dropdown elements
This commit is contained in:
@@ -350,6 +350,7 @@ export class SelectBox extends vsSelectBox {
|
|||||||
|
|
||||||
super.render(selectContainer);
|
super.render(selectContainer);
|
||||||
this.selectElement.classList.add('action-item-label');
|
this.selectElement.classList.add('action-item-label');
|
||||||
|
this.selectElement.id = selectOptions.id;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
super.render(container);
|
super.render(container);
|
||||||
@@ -364,4 +365,5 @@ export class SelectBox extends vsSelectBox {
|
|||||||
export interface ISelectBoxOptionsWithLabel extends ISelectBoxOptions {
|
export interface ISelectBoxOptionsWithLabel extends ISelectBoxOptions {
|
||||||
labelText?: string;
|
labelText?: string;
|
||||||
labelOnTop?: boolean;
|
labelOnTop?: boolean;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,12 +275,13 @@ export class CollapseCellsAction extends ToggleableAction {
|
|||||||
const showAllKernelsConfigName = 'notebook.showAllKernels';
|
const showAllKernelsConfigName = 'notebook.showAllKernels';
|
||||||
const workbenchPreviewConfigName = 'workbench.enablePreviewFeatures';
|
const workbenchPreviewConfigName = 'workbench.enablePreviewFeatures';
|
||||||
export const noKernelName = localize('noKernel', "No Kernel");
|
export const noKernelName = localize('noKernel', "No Kernel");
|
||||||
|
const kernelDropdownElementId = 'kernel-dropdown';
|
||||||
|
|
||||||
export class KernelsDropdown extends SelectBox {
|
export class KernelsDropdown extends SelectBox {
|
||||||
private model: NotebookModel;
|
private model: NotebookModel;
|
||||||
private _showAllKernels: boolean = false;
|
private _showAllKernels: boolean = false;
|
||||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise<INotebookModel>, @IConfigurationService private _configurationService: IConfigurationService) {
|
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) {
|
if (modelReady) {
|
||||||
modelReady
|
modelReady
|
||||||
@@ -353,6 +354,8 @@ export class KernelsDropdown extends SelectBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const attachToDropdownElementId = 'attach-to-dropdown';
|
||||||
|
|
||||||
export class AttachToDropdown extends SelectBox {
|
export class AttachToDropdown extends SelectBox {
|
||||||
private model: NotebookModel;
|
private model: NotebookModel;
|
||||||
|
|
||||||
@@ -363,7 +366,7 @@ export class AttachToDropdown extends SelectBox {
|
|||||||
@INotificationService private _notificationService: INotificationService,
|
@INotificationService private _notificationService: INotificationService,
|
||||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
|
@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) {
|
if (modelReady) {
|
||||||
modelReady
|
modelReady
|
||||||
.then(model => {
|
.then(model => {
|
||||||
|
|||||||
39
test/automation/src/sql/configurePythonDialog.ts
Normal file
39
test/automation/src/sql/configurePythonDialog.ts
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,17 +8,82 @@ import { QuickAccess } from '../quickaccess';
|
|||||||
import { QuickInput } from '../quickinput';
|
import { QuickInput } from '../quickinput';
|
||||||
import { Editors } from '../editors';
|
import { Editors } from '../editors';
|
||||||
|
|
||||||
|
const winOrCtrl = process.platform === 'darwin' ? 'ctrl' : 'win';
|
||||||
|
|
||||||
export class Notebook {
|
export class Notebook {
|
||||||
|
|
||||||
|
public readonly toolbar: NotebookToolbar;
|
||||||
|
|
||||||
constructor(private code: Code, private quickAccess: QuickAccess, private quickInput: QuickInput, private editors: Editors) {
|
constructor(private code: Code, private quickAccess: QuickAccess, private quickInput: QuickInput, private editors: Editors) {
|
||||||
|
this.toolbar = new NotebookToolbar(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openFile(fileName: string): Promise<void> {
|
async openFile(fileName: string): Promise<void> {
|
||||||
await this.quickAccess.openQuickAccess(fileName);
|
await this.quickAccess.openQuickAccess(fileName);
|
||||||
|
|
||||||
await this.quickInput.waitForQuickInputElements(names => names[0] === fileName);
|
await this.quickInput.waitForQuickInputElements(names => names[0] === fileName);
|
||||||
await this.code.dispatchKeybinding('enter');
|
await this.code.dispatchKeybinding('enter');
|
||||||
await this.editors.waitForActiveTab(fileName);
|
await this.editors.waitForActiveTab(fileName);
|
||||||
await this.code.waitForElement('.notebookEditor');
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { Profiler } from './sql/profiler';
|
|||||||
import { QueryEditors } from './sql/queryEditors';
|
import { QueryEditors } from './sql/queryEditors';
|
||||||
import { QueryEditor } from './sql/queryEditor';
|
import { QueryEditor } from './sql/queryEditor';
|
||||||
import { Notebook as SqlNotebook } from './sql/notebook';
|
import { Notebook as SqlNotebook } from './sql/notebook';
|
||||||
|
import { ConfigurePythonDialog } from './sql/configurePythonDialog';
|
||||||
// {{END}}
|
// {{END}}
|
||||||
|
|
||||||
export interface Commands {
|
export interface Commands {
|
||||||
@@ -57,7 +58,8 @@ export class Workbench {
|
|||||||
readonly profiler: Profiler;
|
readonly profiler: Profiler;
|
||||||
readonly queryEditors: QueryEditors;
|
readonly queryEditors: QueryEditors;
|
||||||
readonly queryEditor: QueryEditor;
|
readonly queryEditor: QueryEditor;
|
||||||
readonly sqlNotebbok: SqlNotebook;
|
readonly sqlNotebook: SqlNotebook;
|
||||||
|
readonly configurePythonDialog: ConfigurePythonDialog;
|
||||||
// {{END}}
|
// {{END}}
|
||||||
|
|
||||||
constructor(code: Code, userDataPath: string) {
|
constructor(code: Code, userDataPath: string) {
|
||||||
@@ -81,7 +83,8 @@ export class Workbench {
|
|||||||
this.profiler = new Profiler(code, this.quickaccess);
|
this.profiler = new Profiler(code, this.quickaccess);
|
||||||
this.queryEditors = new QueryEditors(code, this.editors);
|
this.queryEditors = new QueryEditors(code, this.editors);
|
||||||
this.queryEditor = new QueryEditor(code);
|
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}}
|
// {{END}}
|
||||||
this.notebook = new Notebook(this.quickaccess, code);
|
this.notebook = new Notebook(this.quickaccess, code);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,21 @@ import { Application } from '../../../../../automation';
|
|||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
describe('Notebook', () => {
|
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;
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user