notebook.test.ts 38.9 KB
Newer Older
R
rebornix 已提交
1 2 3 4 5 6 7 8 9 10
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'mocha';
import * as assert from 'assert';
import * as vscode from 'vscode';
import { join } from 'path';

R
rebornix 已提交
11 12 13 14 15 16 17 18
export function timeoutAsync(n: number): Promise<void> {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve();
		}, n);
	});
}

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
export function once<T>(event: vscode.Event<T>): vscode.Event<T> {
	return (listener: any, thisArgs = null, disposables?: any) => {
		// we need this, in case the event fires during the listener call
		let didFire = false;
		let result: vscode.Disposable;
		result = event(e => {
			if (didFire) {
				return;
			} else if (result) {
				result.dispose();
			} else {
				didFire = true;
			}

			return listener.call(thisArgs, e);
		}, null, disposables);

		if (didFire) {
			result.dispose();
		}

		return result;
	};
}
R
rebornix 已提交
43

44 45 46
async function getEventOncePromise<T>(event: vscode.Event<T>): Promise<T> {
	return new Promise<T>((resolve, _reject) => {
		once(event)((result: T) => resolve(result));
R
rebornix 已提交
47 48 49
	});
}

50 51 52 53 54 55 56 57 58
// Since `workbench.action.splitEditor` command does await properly
// Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves
// The workaround here is waiting for the first visible notebook editor change event.
async function splitEditor() {
	const once = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
	await vscode.commands.executeCommand('workbench.action.splitEditor');
	await once;
}

59 60
suite('API tests', () => {
	test('document open/close event', async function () {
R
rebornix 已提交
61
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
62 63 64 65 66 67 68 69 70 71
		const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument);
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await firstDocumentOpen;

		const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument);
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
		await firstDocumentClose;
	});

	test('shared document in notebook editors', async function () {
R
rebornix 已提交
72
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
73 74 75 76 77 78 79 80 81 82 83
		let counter = 0;
		const disposables: vscode.Disposable[] = [];
		disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => {
			counter++;
		}));
		disposables.push(vscode.notebook.onDidCloseNotebookDocument(() => {
			counter--;
		}));
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(counter, 1);

84
		await splitEditor();
85 86 87 88 89 90 91 92
		assert.equal(counter, 1);
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
		assert.equal(counter, 0);

		disposables.forEach(d => d.dispose());
	});

	test('editor open/close event', async function () {
R
rebornix 已提交
93
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
94 95 96 97 98 99 100 101 102 103
		const firstEditorOpen = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await firstEditorOpen;

		const firstEditorClose = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
		await firstEditorClose;
	});

	test('editor open/close event', async function () {
R
rebornix 已提交
104
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
105 106 107 108 109 110 111 112 113
		let count = 0;
		const disposables: vscode.Disposable[] = [];
		disposables.push(vscode.notebook.onDidChangeVisibleNotebookEditors(() => {
			count = vscode.notebook.visibleNotebookEditors.length;
		}));

		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(count, 1);

R
rebornix 已提交
114
		await splitEditor();
115 116 117 118 119 120
		assert.equal(count, 2);

		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
		assert.equal(count, 0);
	});

R
rebornix 已提交
121
	test('editor editing event 2', async function () {
R
rebornix 已提交
122
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		const cellChangeEventRet = await cellsChangeEvent;
		assert.equal(cellChangeEventRet.document, vscode.notebook.activeNotebookEditor?.document);
		assert.equal(cellChangeEventRet.changes.length, 1);
		assert.deepEqual(cellChangeEventRet.changes[0], {
			start: 1,
			deletedCount: 0,
			items: [
				vscode.notebook.activeNotebookEditor!.document.cells[1]
			]
		});

R
rebornix 已提交
138
		const moveCellEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
139 140 141 142
		await vscode.commands.executeCommand('notebook.cell.moveUp');
		const moveCellEventRet = await moveCellEvent;
		assert.deepEqual(moveCellEventRet, {
			document: vscode.notebook.activeNotebookEditor!.document,
143 144 145 146 147 148 149 150 151 152 153 154
			changes: [
				{
					start: 1,
					deletedCount: 1,
					items: []
				},
				{
					start: 0,
					deletedCount: 0,
					items: [vscode.notebook.activeNotebookEditor?.document.cells[0]]
				}
			]
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
		});

		const cellOutputChange = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
		await vscode.commands.executeCommand('notebook.cell.execute');
		const cellOutputsAddedRet = await cellOutputChange;
		assert.deepEqual(cellOutputsAddedRet, {
			document: vscode.notebook.activeNotebookEditor!.document,
			cells: [vscode.notebook.activeNotebookEditor!.document.cells[0]]
		});
		assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 1);

		const cellOutputClear = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
		await vscode.commands.executeCommand('notebook.cell.clearOutputs');
		const cellOutputsCleardRet = await cellOutputClear;
		assert.deepEqual(cellOutputsCleardRet, {
			document: vscode.notebook.activeNotebookEditor!.document,
			cells: [vscode.notebook.activeNotebookEditor!.document.cells[0]]
		});
		assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 0);

		// const cellChangeLanguage = getEventOncePromise<vscode.NotebookCellLanguageChangeEvent>(vscode.notebook.onDidChangeCellLanguage);
		// await vscode.commands.executeCommand('notebook.cell.changeToMarkdown');
		// const cellChangeLanguageRet = await cellChangeLanguage;
		// assert.deepEqual(cellChangeLanguageRet, {
		// 	document: vscode.notebook.activeNotebookEditor!.document,
		// 	cells: vscode.notebook.activeNotebookEditor!.document.cells[0],
		// 	language: 'markdown'
		// });

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
R
rebornix 已提交
187

188
	test('editor move cell event', async function () {
R
rebornix 已提交
189
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('notebook.focusTop');

		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);
		const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
		await vscode.commands.executeCommand('notebook.cell.moveDown');
		const ret = await moveChange;
		assert.deepEqual(ret, {
			document: vscode.notebook.activeNotebookEditor?.document,
			changes: [
				{
					start: 0,
					deletedCount: 1,
					items: []
				},
				{
					start: 1,
					deletedCount: 0,
					items: [activeCell]
				}
			]
		});
	});

R
rebornix 已提交
217
	test('notebook editor active/visible', async function () {
R
rebornix 已提交
218
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
219 220 221
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstEditor?.active, true);
R
rebornix 已提交
222
		assert.equal(firstEditor?.visible, true);
R
rebornix 已提交
223

224
		await splitEditor();
R
rebornix 已提交
225 226
		const secondEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(secondEditor?.active, true);
R
rebornix 已提交
227
		assert.equal(secondEditor?.visible, true);
R
rebornix 已提交
228 229
		assert.equal(firstEditor?.active, false);

R
rebornix 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243
		assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);

		await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
		assert.equal(firstEditor?.visible, true);
		assert.equal(firstEditor?.active, false);
		assert.equal(secondEditor?.visible, false);
		assert.equal(secondEditor?.active, false);
		assert.equal(vscode.notebook.visibleNotebookEditors.length, 1);

		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
		assert.equal(secondEditor?.active, true);
		assert.equal(secondEditor?.visible, true);
		assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);

R
rebornix 已提交
244 245 246
		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
R
rebornix 已提交
247 248

	test('notebook active editor change', async function () {
R
rebornix 已提交
249
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
250 251 252 253 254 255 256 257
		const firstEditorOpen = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await firstEditorOpen;

		const firstEditorDeactivate = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
		await vscode.commands.executeCommand('workbench.action.splitEditor');
		await firstEditorDeactivate;

258
		await vscode.commands.executeCommand('workbench.action.files.save');
R
rebornix 已提交
259 260
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
261 262

	test('edit API', async function () {
R
rebornix 已提交
263
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
			editBuilder.insert(1, 'test 2', 'javascript', vscode.CellKind.Code, [], undefined);
		});

		const cellChangeEventRet = await cellsChangeEvent;
		assert.equal(cellChangeEventRet.document, vscode.notebook.activeNotebookEditor?.document);
		assert.equal(cellChangeEventRet.changes.length, 1);
		assert.deepEqual(cellChangeEventRet.changes[0].start, 1);
		assert.deepEqual(cellChangeEventRet.changes[0].deletedCount, 0);
		assert.equal(cellChangeEventRet.changes[0].items[0], vscode.notebook.activeNotebookEditor!.document.cells[1]);

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
281 282
});

R
rebornix 已提交
283 284
suite('notebook workflow', () => {
	test('notebook open', async function () {
R
rebornix 已提交
285
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
286
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
R
rebornix 已提交
287 288 289 290
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

R
Rob Lourens 已提交
291
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
R
rebornix 已提交
292 293
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

R
Rob Lourens 已提交
294
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
295 296 297 298 299 300 301 302 303 304 305
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
		assert.equal(activeCell!.source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('notebook cell actions', async function () {
R
rebornix 已提交
306
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
307
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
R
rebornix 已提交
308 309 310 311 312
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		// ---- insert cell below and focus ---- //
R
Rob Lourens 已提交
313
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
R
rebornix 已提交
314 315 316
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		// ---- insert cell above and focus ---- //
R
Rob Lourens 已提交
317
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
318 319 320 321 322 323 324
		let activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
		assert.equal(activeCell!.source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);

		// ---- focus bottom ---- //
R
Rob Lourens 已提交
325
		await vscode.commands.executeCommand('notebook.focusBottom');
R
rebornix 已提交
326 327 328 329
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2);

		// ---- focus top and then copy down ---- //
R
Rob Lourens 已提交
330
		await vscode.commands.executeCommand('notebook.focusTop');
R
rebornix 已提交
331 332 333
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);

R
Rob Lourens 已提交
334
		await vscode.commands.executeCommand('notebook.cell.copyDown');
R
rebornix 已提交
335 336 337 338
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
		assert.equal(activeCell?.source, 'test');

R
Rob Lourens 已提交
339
		await vscode.commands.executeCommand('notebook.cell.delete');
R
rebornix 已提交
340 341 342 343 344
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
		assert.equal(activeCell?.source, '');

		// ---- focus top and then copy up ---- //
R
Rob Lourens 已提交
345 346
		await vscode.commands.executeCommand('notebook.focusTop');
		await vscode.commands.executeCommand('notebook.cell.copyUp');
R
rebornix 已提交
347 348 349 350 351 352 353 354 355 356 357
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 4);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, '');
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);


		// ---- move up and down ---- //

R
Rob Lourens 已提交
358
		await vscode.commands.executeCommand('notebook.cell.moveDown');
359 360 361
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1,
			`first move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.source}`);

R
rebornix 已提交
362 363 364 365 366 367 368 369 370
		// await vscode.commands.executeCommand('notebook.cell.moveDown');
		// activeCell = vscode.notebook.activeNotebookEditor!.selection;

		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2,
		// 	`second move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.source}`);
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].source, 'test');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, '');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, 'test');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, '');
R
rebornix 已提交
371 372 373

		// ---- ---- //

374
		await vscode.commands.executeCommand('workbench.action.files.save');
R
rebornix 已提交
375 376
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
R
Rob Lourens 已提交
377

378
	test('move cells will not recreate cells in ExtHost', async function () {
R
rebornix 已提交
379
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
380 381 382 383 384 385 386 387 388 389 390 391
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('notebook.focusTop');

		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);
		await vscode.commands.executeCommand('notebook.cell.moveDown');
		await vscode.commands.executeCommand('notebook.cell.moveDown');

		const newActiveCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.deepEqual(activeCell, newActiveCell);
R
rebornix 已提交
392 393 394
		await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');

395 396
		// TODO@rebornix, there are still some events order issue.
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(newActiveCell!), 2);
397 398
	});

R
Rob Lourens 已提交
399
	// test.only('document metadata is respected', async function () {
R
rebornix 已提交
400
	// 	const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
Rob Lourens 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

	// 	assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
	// 	const editor = vscode.notebook.activeNotebookEditor!;

	// 	assert.equal(editor.document.cells.length, 1);
	// 	editor.document.metadata.editable = false;
	// 	await editor.edit(builder => builder.delete(0));
	// 	assert.equal(editor.document.cells.length, 1, 'should not delete cell'); // Not editable, no effect
	// 	await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined));
	// 	assert.equal(editor.document.cells.length, 1, 'should not insert cell'); // Not editable, no effect

	// 	editor.document.metadata.editable = true;
	// 	await editor.edit(builder => builder.delete(0));
	// 	assert.equal(editor.document.cells.length, 0, 'should delete cell'); // Editable, it worked
	// 	await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined));
	// 	assert.equal(editor.document.cells.length, 1, 'should insert cell'); // Editable, it worked

	// 	// await vscode.commands.executeCommand('workbench.action.files.save');
	// 	await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	// });

	test('cell runnable metadata is respected', async () => {
R
rebornix 已提交
424
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
Rob Lourens 已提交
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		const editor = vscode.notebook.activeNotebookEditor!;

		await vscode.commands.executeCommand('notebook.focusTop');
		const cell = editor.document.cells[0];
		assert.equal(cell.outputs.length, 0);
		cell.metadata.runnable = false;
		await vscode.commands.executeCommand('notebook.cell.execute');
		assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work

		cell.metadata.runnable = true;
		await vscode.commands.executeCommand('notebook.cell.execute');
		assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked

440
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
441 442 443 444
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('document runnable metadata is respected', async () => {
R
rebornix 已提交
445
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
Rob Lourens 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		const editor = vscode.notebook.activeNotebookEditor!;

		const cell = editor.document.cells[0];
		assert.equal(cell.outputs.length, 0);
		editor.document.metadata.runnable = false;
		await vscode.commands.executeCommand('notebook.execute');
		assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work

		editor.document.metadata.runnable = true;
		await vscode.commands.executeCommand('notebook.execute');
		assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked

460
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
461 462
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
R
rebornix 已提交
463
});
R
rebornix 已提交
464 465 466

suite('notebook dirty state', () => {
	test('notebook open', async function () {
R
rebornix 已提交
467
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
		assert.equal(activeCell!.source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);


		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
R
rebornix 已提交
485
		await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
R
rebornix 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498 499
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');

		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
});

suite('notebook undo redo', () => {
500
	test('notebook open', async function () {
R
rebornix 已提交
501
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
		assert.equal(activeCell!.source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);


		// modify the second cell, delete it
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
		await vscode.commands.executeCommand('notebook.cell.delete');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 2);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1);


		// undo should bring back the deleted cell, and revert to previous content and selection
		await vscode.commands.executeCommand('notebook.undo');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');

		// redo
		// await vscode.commands.executeCommand('notebook.redo');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 2);
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1);
		// assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test');

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
});
541 542 543

suite('notebook working copy', () => {
	test('notebook revert on close', async function () {
R
rebornix 已提交
544
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });

		// close active editor from command will revert the file
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test');

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('notebook revert', async function () {
R
rebornix 已提交
565
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
		await vscode.commands.executeCommand('workbench.action.files.revert');

		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 1);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test');

		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
582 583

	test('multiple tabs: dirty + clean', async function () {
R
rebornix 已提交
584
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
585 586 587 588 589 590 591
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });

R
rebornix 已提交
592
		const secondResource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './second.vsctestnb'));
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
		await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');

		// make sure that the previous dirty editor is still restored in the extension host and no data loss
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('multiple tabs: two dirty tabs and switching', async function () {
R
rebornix 已提交
608
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
609 610 611 612 613 614 615
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });

R
rebornix 已提交
616
		const secondResource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './second.vsctestnb'));
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
		await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');

		// switch to the first editor
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');

		// switch to the second editor
		await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
		assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 2);
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, '');

637 638 639 640 641 642
		await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});

	test('multiple tabs: different editors with same document', async function () {

R
rebornix 已提交
643
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
644 645 646 647 648 649
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(firstNotebookEditor!.selection?.source, 'test');
		assert.equal(firstNotebookEditor!.selection?.language, 'typescript');

650
		await splitEditor();
651 652 653 654 655 656 657
		const secondNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(secondNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(secondNotebookEditor!.selection?.source, 'test');
		assert.equal(secondNotebookEditor!.selection?.language, 'typescript');

		assert.notEqual(firstNotebookEditor, secondNotebookEditor);
		assert.equal(firstNotebookEditor?.document, secondNotebookEditor?.document, 'split notebook editors share the same document');
R
rebornix 已提交
658
		assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
659

660 661 662
		await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
663
});
664

R
rebornix 已提交
665 666
suite('metadata', () => {
	test('custom metadata should be supported', async function () {
R
rebornix 已提交
667
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
668 669
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
670
		assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
R
rebornix 已提交
671
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
R
rebornix 已提交
672 673
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
	});
674 675

	// TODO copy cell should not copy metadata
676 677

	test('custom metadata should be supported', async function () {
R
rebornix 已提交
678
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
679 680 681 682 683 684 685 686 687 688 689
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.copyDown');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
		assert.equal(activeCell?.metadata.custom, undefined);
	});
R
rebornix 已提交
690 691
});

692 693
suite('regression', () => {
	test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () {
R
rebornix 已提交
694
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
695 696 697 698
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
699 700
		await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
701
	});
702 703

	test('#97830, #97764. Support switch to other editor types', async function () {
R
rebornix 已提交
704
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
705 706 707 708 709 710 711 712 713 714 715 716 717 718
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });

		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'var abc = 0;');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
		assert.equal(vscode.window.activeTextEditor?.document.uri.path, resource.path);

		await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
719 720 721

	// open text editor, pin, and then open a notebook
	test('#96105 - dirty editors', async function () {
R
rebornix 已提交
722
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
723 724 725 726 727 728 729 730 731 732 733 734 735
		await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });

		// now it's dirty, open the resource with notebook editor should open a new one
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.notEqual(vscode.notebook.activeNotebookEditor, undefined, 'notebook first');
		assert.notEqual(vscode.window.activeTextEditor, undefined);

		// await vscode.commands.executeCommand('workbench.action.files.saveAll');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});

736
});
R
rebornix 已提交
737

R
rebornix 已提交
738
suite('webview', () => {
R
rebornix 已提交
739
	// for web, `asWebUri` gets `https`?
R
rebornix 已提交
740
	test('asWebviewUri', async function () {
R
rebornix 已提交
741 742 743 744
		if (vscode.env.uiKind === vscode.UIKind.Web) {
			return;
		}

R
rebornix 已提交
745
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
R
rebornix 已提交
746 747
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
R
rebornix 已提交
748
		const uri = vscode.notebook.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));
M
Matt Bierner 已提交
749
		assert.equal(uri.scheme, 'vscode-resource');
750
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
R
rebornix 已提交
751
	});
R
rebornix 已提交
752

R
rebornix 已提交
753 754

	// 404 on web
R
rebornix 已提交
755
	test('custom renderer message', async function () {
R
rebornix 已提交
756 757 758 759
		if (vscode.env.uiKind === vscode.UIKind.Web) {
			return;
		}

R
rebornix 已提交
760
		const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './customRenderer.vsctestnb'));
R
rebornix 已提交
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const editor = vscode.notebook.activeNotebookEditor;
		const promise = new Promise(resolve => {
			const messageEmitter = editor?.onDidReceiveMessage(e => {
				if (e.type === 'custom_renderer_initialize') {
					resolve();
					messageEmitter?.dispose();
				}
			});
		});

		await vscode.commands.executeCommand('notebook.cell.execute');
		await promise;
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
R
rebornix 已提交
777
});