notebook.test.ts 54.2 KB
Newer Older
R
rebornix 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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';
R
rebornix 已提交
9
import { createRandomFile } from './utils';
R
rebornix 已提交
10

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 61 62 63 64 65 66 67 68 69 70 71 72
async function saveFileAndCloseAll(resource: vscode.Uri) {
	const documentClosed = new Promise((resolve, _reject) => {
		const d = vscode.notebook.onDidCloseNotebookDocument(e => {
			if (e.uri.toString() === resource.toString()) {
				d.dispose();
				resolve();
			}
		});
	});
	await vscode.commands.executeCommand('workbench.action.files.save');
	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	await documentClosed;
}

73
async function saveAllFilesAndCloseAll(resource: vscode.Uri | undefined) {
74
	const documentClosed = new Promise((resolve, _reject) => {
75 76 77
		if (!resource) {
			return resolve();
		}
78 79 80 81 82 83 84 85 86 87 88 89 90
		const d = vscode.notebook.onDidCloseNotebookDocument(e => {
			if (e.uri.toString() === resource.toString()) {
				d.dispose();
				resolve();
			}
		});
	});
	await vscode.commands.executeCommand('workbench.action.files.saveAll');
	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	await documentClosed;
}

function assertInitalState() {
R
rebornix 已提交
91
	// no-op unless we figure out why some documents are opened after the editor is closed
92

R
rebornix 已提交
93 94 95
	// assert.equal(vscode.notebook.activeNotebookEditor, undefined);
	// assert.equal(vscode.notebook.notebookDocuments.length, 0);
	// assert.equal(vscode.notebook.visibleNotebookEditors.length, 0);
96 97
}

R
rebornix 已提交
98
suite('Notebook API tests', () => {
R
rebornix 已提交
99 100 101 102 103 104 105 106 107 108 109 110
	// test.only('crash', async function () {
	// 	for (let i = 0; i < 200; i++) {
	// 		let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
	// 		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 		await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor');

	// 		resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
	// 		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 		await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor');
	// 	}
	// });

R
rebornix 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123
	// test.only('crash', async function () {
	// 	for (let i = 0; i < 200; i++) {
	// 		let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
	// 		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 		await vscode.commands.executeCommand('workbench.action.files.save');
	// 		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// 		resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
	// 		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 		await vscode.commands.executeCommand('workbench.action.files.save');
	// 		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// 	}
	// });

124
	test('document open/close event', async function () {
R
rebornix 已提交
125 126 127
		assertInitalState();

		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
128 129 130 131 132 133 134 135 136
		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;
	});

137
	test('notebook open/close, all cell-documents are ready', async function () {
J
Johannes Rieken 已提交
138
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
139

J
Johannes Rieken 已提交
140 141 142 143 144 145 146 147 148
		const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => {
			for (let cell of notebook.cells) {
				const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString());
				assert.ok(doc);
				assert.strictEqual(doc === cell.document, true);
				assert.strictEqual(doc?.languageId, cell.language);
				assert.strictEqual(doc?.isDirty, false);
				assert.strictEqual(doc?.isClosed, false);
			}
149 150 151 152 153 154 155
		});

		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await p;
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});

156
	test('notebook open/close, notebook ready when cell-document open event is fired', async function () {
J
Johannes Rieken 已提交
157
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
		let didHappen = false;
		const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => {
			if (doc.uri.scheme !== 'vscode-notebook-cell') {
				return;
			}
			const notebook = vscode.notebook.notebookDocuments.find(notebook => {
				const cell = notebook.cells.find(cell => cell.document === doc);
				return Boolean(cell);
			});
			assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`);
			didHappen = true;
		});

		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await p;
		assert.strictEqual(didHappen, true);
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});

177
	test('shared document in notebook editors', async function () {
R
rebornix 已提交
178 179 180
		assertInitalState();

		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
181 182 183 184 185 186 187 188 189 190 191
		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);

192
		await splitEditor();
193 194 195 196 197 198 199 200
		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 已提交
201 202 203
		assertInitalState();

		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
204 205 206 207 208 209 210 211 212
		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;
	});

213
	test('editor open/close event 2', async function () {
R
rebornix 已提交
214 215 216
		assertInitalState();

		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
217 218 219 220 221 222 223 224 225
		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 已提交
226
		await splitEditor();
227 228 229 230 231 232
		assert.equal(count, 2);

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

R
rebornix 已提交
233
	test('editor editing event 2', async function () {
R
rebornix 已提交
234 235 236
		assertInitalState();

		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
237 238 239 240 241 242 243 244 245 246
		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,
R
:guard:  
rebornix 已提交
247
			deletedItems: [],
248 249 250 251 252
			items: [
				vscode.notebook.activeNotebookEditor!.document.cells[1]
			]
		});

R
:guard:  
rebornix 已提交
253 254
		const secondCell = vscode.notebook.activeNotebookEditor!.document.cells[1];

R
rebornix 已提交
255
		const moveCellEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
256 257 258 259
		await vscode.commands.executeCommand('notebook.cell.moveUp');
		const moveCellEventRet = await moveCellEvent;
		assert.deepEqual(moveCellEventRet, {
			document: vscode.notebook.activeNotebookEditor!.document,
260 261 262 263
			changes: [
				{
					start: 1,
					deletedCount: 1,
R
:guard:  
rebornix 已提交
264
					deletedItems: [secondCell],
265 266 267 268 269
					items: []
				},
				{
					start: 0,
					deletedCount: 0,
R
:guard:  
rebornix 已提交
270
					deletedItems: [],
271 272 273
					items: [vscode.notebook.activeNotebookEditor?.document.cells[0]]
				}
			]
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
		});

		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 已提交
306

307
	test('editor move cell event', async function () {
R
rebornix 已提交
308 309
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
		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,
R
:guard:  
rebornix 已提交
326
					deletedItems: [activeCell],
327 328 329 330 331
					items: []
				},
				{
					start: 1,
					deletedCount: 0,
R
:guard:  
rebornix 已提交
332
					deletedItems: [],
333 334 335 336
					items: [activeCell]
				}
			]
		});
337 338 339

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
340 341 342 343 344 345 346

		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstEditor?.document.cells.length, 1);

		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
347 348
	});

349
	test('notebook editor active/visible', async function () {
R
rebornix 已提交
350 351
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
352 353 354
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstEditor?.active, true);
R
rebornix 已提交
355
		assert.equal(firstEditor?.visible, true);
R
rebornix 已提交
356

357
		await splitEditor();
R
rebornix 已提交
358 359
		const secondEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(secondEditor?.active, true);
R
rebornix 已提交
360
		assert.equal(secondEditor?.visible, true);
R
rebornix 已提交
361 362
		assert.equal(firstEditor?.active, false);

R
rebornix 已提交
363 364
		assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);

365
		const untitledEditorChange = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
R
rebornix 已提交
366
		await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
367
		await untitledEditorChange;
R
rebornix 已提交
368 369 370 371 372 373
		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);

374
		const activeEditorClose = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
R
rebornix 已提交
375
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
376
		await activeEditorClose;
R
rebornix 已提交
377 378 379 380
		assert.equal(secondEditor?.active, true);
		assert.equal(secondEditor?.visible, true);
		assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);

R
rebornix 已提交
381 382 383
		await vscode.commands.executeCommand('workbench.action.files.save');
		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});
R
rebornix 已提交
384 385

	test('notebook active editor change', async function () {
R
rebornix 已提交
386 387
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
388 389 390 391 392 393 394 395
		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;

396
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
397
	});
398

399
	test('edit API (replaceCells)', async function () {
R
rebornix 已提交
400 401
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
402 403 404 405
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
406
			editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]);
407 408 409
		});

		const cellChangeEventRet = await cellsChangeEvent;
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
		assert.strictEqual(cellChangeEventRet.document === vscode.notebook.activeNotebookEditor?.document, true);
		assert.strictEqual(cellChangeEventRet.document.isDirty, true);
		assert.strictEqual(cellChangeEventRet.changes.length, 1);
		assert.strictEqual(cellChangeEventRet.changes[0].start, 1);
		assert.strictEqual(cellChangeEventRet.changes[0].deletedCount, 0);
		assert.strictEqual(cellChangeEventRet.changes[0].items[0] === vscode.notebook.activeNotebookEditor!.document.cells[1], true);

		await saveAllFilesAndCloseAll(resource);
	});

	test('edit API (replaceOutput)', async function () {
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
			editBuilder.replaceOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]);
		});

		const document = vscode.notebook.activeNotebookEditor?.document!;
430
		assert.strictEqual(document.isDirty, true);
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
		assert.strictEqual(document.cells.length, 1);
		assert.strictEqual(document.cells[0].outputs.length, 1);
		assert.strictEqual(document.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich);

		await saveAllFilesAndCloseAll(undefined);
	});

	test('edit API (replaceOutput, event)', async function () {
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const outputChangeEvent = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
			editBuilder.replaceOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]);
		});

		const value = await outputChangeEvent;
		assert.strictEqual(value.document === vscode.notebook.activeNotebookEditor?.document, true);
450
		assert.strictEqual(value.document.isDirty, true);
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
		assert.strictEqual(value.cells.length, 1);
		assert.strictEqual(value.cells[0].outputs.length, 1);
		assert.strictEqual(value.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich);

		await saveAllFilesAndCloseAll(undefined);
	});

	test('edit API (replaceMetadata)', async function () {

		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
			editBuilder.replaceMetadata(0, { inputCollapsed: true, executionOrder: 17 });
		});

		const document = vscode.notebook.activeNotebookEditor?.document!;
		assert.strictEqual(document.cells.length, 1);
		assert.strictEqual(document.cells[0].metadata.executionOrder, 17);
		assert.strictEqual(document.cells[0].metadata.inputCollapsed, true);

		assert.strictEqual(document.isDirty, true);
		await saveFileAndCloseAll(resource);
	});

	test('edit API (replaceMetadata, event)', async function () {

		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		const event = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);

		await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
			editBuilder.replaceMetadata(0, { inputCollapsed: true, executionOrder: 17 });
		});

		const data = await event;
		assert.strictEqual(data.document, vscode.notebook.activeNotebookEditor?.document);
		assert.strictEqual(data.cell.metadata.executionOrder, 17);
		assert.strictEqual(data.cell.metadata.inputCollapsed, true);
493

494
		assert.strictEqual(data.document.isDirty, true);
R
rebornix 已提交
495
		await saveFileAndCloseAll(resource);
496
	});
497 498

	test('initialzation should not emit cell change events.', async function () {
R
rebornix 已提交
499 500
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
501 502 503 504 505 506 507 508 509 510 511

		let count = 0;
		const disposables: vscode.Disposable[] = [];
		disposables.push(vscode.notebook.onDidChangeNotebookCells(() => {
			count++;
		}));

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

		disposables.forEach(d => d.dispose());
R
rebornix 已提交
512 513

		await saveFileAndCloseAll(resource);
514
	});
515 516
});

R
rebornix 已提交
517 518
suite('notebook workflow', () => {
	test('notebook open', async function () {
R
rebornix 已提交
519 520
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
521
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
R
rebornix 已提交
522
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
523
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
524 525
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

R
Rob Lourens 已提交
526
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
527
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
528

R
Rob Lourens 已提交
529
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
530 531
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
J
Johannes Rieken 已提交
532
		assert.equal(activeCell!.document.getText(), '');
R
rebornix 已提交
533 534 535 536 537 538 539 540
		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 已提交
541 542
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
543
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
R
rebornix 已提交
544
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
545
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
546 547 548
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		// ---- insert cell below and focus ---- //
R
Rob Lourens 已提交
549
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
550
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
551 552

		// ---- insert cell above and focus ---- //
R
Rob Lourens 已提交
553
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
554 555
		let activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
J
Johannes Rieken 已提交
556
		assert.equal(activeCell!.document.getText(), '');
R
rebornix 已提交
557 558 559 560
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);

		// ---- focus bottom ---- //
R
Rob Lourens 已提交
561
		await vscode.commands.executeCommand('notebook.focusBottom');
R
rebornix 已提交
562 563 564 565
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2);

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

R
Rob Lourens 已提交
570
		await vscode.commands.executeCommand('notebook.cell.copyDown');
R
rebornix 已提交
571 572
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
J
Johannes Rieken 已提交
573
		assert.equal(activeCell?.document.getText(), 'test');
R
rebornix 已提交
574

R
Rob Lourens 已提交
575
		await vscode.commands.executeCommand('notebook.cell.delete');
R
rebornix 已提交
576 577
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
J
Johannes Rieken 已提交
578
		assert.equal(activeCell?.document.getText(), '');
R
rebornix 已提交
579 580

		// ---- focus top and then copy up ---- //
R
Rob Lourens 已提交
581 582
		await vscode.commands.executeCommand('notebook.focusTop');
		await vscode.commands.executeCommand('notebook.cell.copyUp');
R
rebornix 已提交
583
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 4);
J
Johannes Rieken 已提交
584 585 586 587
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].document.getText(), 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].document.getText(), '');
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].document.getText(), '');
R
rebornix 已提交
588 589 590 591 592 593
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);


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

R
Rob Lourens 已提交
594
		await vscode.commands.executeCommand('notebook.cell.moveDown');
595
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1,
J
Johannes Rieken 已提交
596
			`first move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.document.getText()}`);
597

R
rebornix 已提交
598 599 600 601
		// await vscode.commands.executeCommand('notebook.cell.moveDown');
		// activeCell = vscode.notebook.activeNotebookEditor!.selection;

		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2,
J
Johannes Rieken 已提交
602 603 604 605 606
		// 	`second move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.document.getText()}`);
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), 'test');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].document.getText(), '');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].document.getText(), 'test');
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].document.getText(), '');
R
rebornix 已提交
607 608 609

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

610
		await vscode.commands.executeCommand('workbench.action.files.save');
R
rebornix 已提交
611 612
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
R
Rob Lourens 已提交
613

R
rebornix 已提交
614
	test('notebook join cells', async function () {
R
rebornix 已提交
615 616
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
617 618 619 620 621 622 623
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
624 625 626
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
R
rebornix 已提交
627 628 629 630 631

		const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
		await vscode.commands.executeCommand('notebook.cell.joinAbove');
		await cellsChangeEvent;

R
rebornix 已提交
632
		assert.deepEqual(vscode.notebook.activeNotebookEditor!.selection?.document.getText().split(/\r\n|\r|\n/), ['test', 'var abc = 0;']);
R
rebornix 已提交
633 634 635 636 637

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

638
	test('move cells will not recreate cells in ExtHost', async function () {
R
rebornix 已提交
639 640
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
641 642 643 644 645 646 647 648 649 650 651 652
		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 已提交
653

R
rebornix 已提交
654
		await saveFileAndCloseAll(resource);
655 656
		// TODO@rebornix, there are still some events order issue.
		// assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(newActiveCell!), 2);
657 658
	});

R
Rob Lourens 已提交
659
	// test.only('document metadata is respected', async function () {
R
rebornix 已提交
660
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
Rob Lourens 已提交
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
	// 	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 已提交
684 685
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
Rob Lourens 已提交
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
		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

701
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
702 703 704 705
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('document runnable metadata is respected', async () => {
R
rebornix 已提交
706 707
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
Rob Lourens 已提交
708 709 710 711 712 713 714 715 716 717 718 719 720 721
		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

722
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
723 724
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
R
rebornix 已提交
725
});
R
rebornix 已提交
726 727 728

suite('notebook dirty state', () => {
	test('notebook open', async function () {
R
rebornix 已提交
729 730
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
731 732
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
733
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
734 735 736
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
737
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
738 739 740 741

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
J
Johannes Rieken 已提交
742
		assert.equal(activeCell!.document.getText(), '');
R
rebornix 已提交
743 744 745
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);

R
rebornix 已提交
746 747 748
		const edit = new vscode.WorkspaceEdit();
		edit.insert(activeCell!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
R
rebornix 已提交
749 750 751
		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);
J
Johannes Rieken 已提交
752
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
R
rebornix 已提交
753

754
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
755 756 757 758
	});
});

suite('notebook undo redo', () => {
759
	test('notebook open', async function () {
R
rebornix 已提交
760 761
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
762 763
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
764
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
765 766 767
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
768
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
769 770 771 772

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
J
Johannes Rieken 已提交
773
		assert.equal(activeCell!.document.getText(), '');
R
rebornix 已提交
774 775 776 777 778
		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
R
rebornix 已提交
779 780 781
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
R
rebornix 已提交
782 783 784 785 786 787
		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
R
rebornix 已提交
788
		await vscode.commands.executeCommand('undo');
R
rebornix 已提交
789 790
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3);
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1);
J
Johannes Rieken 已提交
791
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
R
rebornix 已提交
792 793 794 795 796

		// 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);
J
Johannes Rieken 已提交
797
		// assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'test');
R
rebornix 已提交
798

799
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
800
	});
801

R
rebornix 已提交
802
	test.skip('execute and then undo redo', async function () {
R
rebornix 已提交
803 804
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
		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,
			deletedItems: [],
			items: [
				vscode.notebook.activeNotebookEditor!.document.cells[1]
			]
		});

		const secondCell = vscode.notebook.activeNotebookEditor!.document.cells[1];

		const moveCellEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
		await vscode.commands.executeCommand('notebook.cell.moveUp');
		const moveCellEventRet = await moveCellEvent;
		assert.deepEqual(moveCellEventRet, {
			document: vscode.notebook.activeNotebookEditor!.document,
			changes: [
				{
					start: 1,
					deletedCount: 1,
					deletedItems: [secondCell],
					items: []
				},
				{
					start: 0,
					deletedCount: 0,
					deletedItems: [],
					items: [vscode.notebook.activeNotebookEditor?.document.cells[0]]
				}
			]
		});

		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);
R
rebornix 已提交
854
		await vscode.commands.executeCommand('undo');
855 856 857 858 859 860 861
		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);

862
		await saveFileAndCloseAll(resource);
863 864
	});

R
rebornix 已提交
865
});
866 867

suite('notebook working copy', () => {
R
rebornix 已提交
868
	// test('notebook revert on close', async function () {
R
rebornix 已提交
869
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
870 871 872
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 	await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
873

R
rebornix 已提交
874 875
	// 	await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
	// 	await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
876

R
rebornix 已提交
877 878 879 880 881 882 883
	// 	// 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?.document.getText(), 'test');
884

R
rebornix 已提交
885 886 887
	// 	await vscode.commands.executeCommand('workbench.action.files.save');
	// 	await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	// });
888

R
rebornix 已提交
889
	// test('notebook revert', async function () {
R
rebornix 已提交
890
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
891 892 893
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 	await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
894

R
rebornix 已提交
895 896 897
	// 	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');
898

R
rebornix 已提交
899 900 901 902 903
	// 	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?.document.getText(), 'test');
904

R
rebornix 已提交
905 906 907
	// 	await vscode.commands.executeCommand('workbench.action.files.saveAll');
	// 	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// });
908 909

	test('multiple tabs: dirty + clean', async function () {
R
rebornix 已提交
910 911
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
912 913
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
914
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
915 916

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
917 918 919
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
920

R
rebornix 已提交
921
		const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
922 923 924 925 926 927 928 929
		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);
J
Johannes Rieken 已提交
930
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
931

932
		await saveFileAndCloseAll(resource);
933 934 935
	});

	test('multiple tabs: two dirty tabs and switching', async function () {
R
rebornix 已提交
936 937
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
938 939
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
940
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
941 942

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
943 944 945
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
946

R
rebornix 已提交
947
		const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
948 949
		await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
950
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
951 952 953 954 955 956 957

		// 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);
J
Johannes Rieken 已提交
958
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
959 960 961 962 963 964 965

		// 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);
J
Johannes Rieken 已提交
966
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), '');
967

968 969 970
		await saveAllFilesAndCloseAll(secondResource);
		// await vscode.commands.executeCommand('workbench.action.files.saveAll');
		// await vscode.commands.executeCommand('workbench.action.closeAllEditors');
971 972 973
	});

	test('multiple tabs: different editors with same document', async function () {
R
rebornix 已提交
974
		assertInitalState();
975

R
rebornix 已提交
976
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
977 978 979
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
980
		assert.equal(firstNotebookEditor!.selection?.document.getText(), 'test');
981 982
		assert.equal(firstNotebookEditor!.selection?.language, 'typescript');

983
		await splitEditor();
984 985
		const secondNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(secondNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
986
		assert.equal(secondNotebookEditor!.selection?.document.getText(), 'test');
987 988 989 990
		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 已提交
991
		assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
992

993 994 995 996
		await saveAllFilesAndCloseAll(resource);

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

R
rebornix 已提交
1000 1001
suite('metadata', () => {
	test('custom metadata should be supported', async function () {
R
rebornix 已提交
1002 1003
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
1004 1005
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
1006
		assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
R
rebornix 已提交
1007
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
R
rebornix 已提交
1008
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
1009

1010
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
1011
	});
1012

1013

R
rebornix 已提交
1014
	// TODO@rebornix skip as it crashes the process all the time
1015
	test.skip('custom metadata should be supported 2', async function () {
R
rebornix 已提交
1016 1017
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
1018 1019 1020 1021 1022 1023
		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');

1024 1025 1026 1027 1028
		// TODO see #101462
		// 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!['testCellMetadata'] as number, 123);
1029

1030
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
1031
	});
R
rebornix 已提交
1032 1033
});

1034 1035
suite('regression', () => {
	test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () {
R
rebornix 已提交
1036 1037
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
1038 1039
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
1040
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
1041
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
1042
		await saveFileAndCloseAll(resource);
1043
	});
1044 1045

	test('#97830, #97764. Support switch to other editor types', async function () {
R
rebornix 已提交
1046 1047
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
1048 1049
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
R
rebornix 已提交
1050 1051 1052
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
1053 1054

		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
1055
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;');
1056 1057 1058 1059 1060 1061 1062
		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.closeAllEditors');
	});
1063 1064 1065

	// open text editor, pin, and then open a notebook
	test('#96105 - dirty editors', async function () {
R
rebornix 已提交
1066 1067
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
1068
		await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
R
rebornix 已提交
1069
		const edit = new vscode.WorkspaceEdit();
R
rebornix 已提交
1070
		edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;');
R
rebornix 已提交
1071
		await vscode.workspace.applyEdit(edit);
1072 1073 1074 1075 1076 1077 1078 1079 1080

		// 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.closeAllEditors');
	});

R
rebornix 已提交
1081
	test('#102411 - untitled notebook creation failed', async function () {
R
rebornix 已提交
1082
		assertInitalState();
J
Johannes Rieken 已提交
1083
		await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { viewType: 'notebookCoreTest' });
R
rebornix 已提交
1084
		assert.notEqual(vscode.notebook.activeNotebookEditor, undefined, 'untitled notebook editor is not undefined');
1085 1086

		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
R
rebornix 已提交
1087
	});
R
rebornix 已提交
1088 1089

	test('#102423 - copy/paste shares the same text buffer', async function () {
R
rebornix 已提交
1090 1091
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');

		let activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(activeCell?.document.getText(), 'test');

		await vscode.commands.executeCommand('notebook.cell.copyDown');
		await vscode.commands.executeCommand('notebook.cell.edit');
		activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
		assert.equal(activeCell?.document.getText(), 'test');

R
rebornix 已提交
1103 1104 1105
		const edit = new vscode.WorkspaceEdit();
		edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
R
rebornix 已提交
1106 1107

		assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 2);
J
Johannes Rieken 已提交
1108
		assert.notEqual(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), vscode.notebook.activeNotebookEditor!.document.cells[1].document.getText());
1109 1110

		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
R
rebornix 已提交
1111
	});
1112
});
R
rebornix 已提交
1113

R
rebornix 已提交
1114
suite('webview', () => {
R
rebornix 已提交
1115
	// for web, `asWebUri` gets `https`?
1116 1117 1118 1119
	// test('asWebviewUri', async function () {
	// 	if (vscode.env.uiKind === vscode.UIKind.Web) {
	// 		return;
	// 	}
R
rebornix 已提交
1120

R
rebornix 已提交
1121
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
1122 1123 1124
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 	assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
	// 	const uri = vscode.notebook.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));
1125
	// 	assert.equal(uri.scheme, 'vscode-webview-resource');
1126 1127
	// 	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// });
R
rebornix 已提交
1128

R
rebornix 已提交
1129 1130

	// 404 on web
1131 1132 1133 1134
	// test('custom renderer message', async function () {
	// 	if (vscode.env.uiKind === vscode.UIKind.Web) {
	// 		return;
	// 	}
R
rebornix 已提交
1135

1136 1137
	// 	const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './customRenderer.vsctestnb'));
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
R
rebornix 已提交
1138

1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
	// 	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 已提交
1153
});