notebook.test.ts 54.8 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
		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);
R
rebornix 已提交
693 694

		let metadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
R
Rob Lourens 已提交
695
		cell.metadata.runnable = false;
R
rebornix 已提交
696 697
		await metadataChangeEvent;

R
Rob Lourens 已提交
698 699 700
		await vscode.commands.executeCommand('notebook.cell.execute');
		assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work

R
rebornix 已提交
701
		metadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
R
Rob Lourens 已提交
702
		cell.metadata.runnable = true;
R
rebornix 已提交
703 704
		await metadataChangeEvent;

R
Rob Lourens 已提交
705 706 707
		await vscode.commands.executeCommand('notebook.cell.execute');
		assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked

708
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
709 710 711 712
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});

	test('document runnable metadata is respected', async () => {
R
rebornix 已提交
713 714
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
Rob Lourens 已提交
715 716 717 718 719 720
		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);
721
		let metadataChangeEvent = getEventOncePromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
R
Rob Lourens 已提交
722
		editor.document.metadata.runnable = false;
723 724
		await metadataChangeEvent;

R
Rob Lourens 已提交
725 726 727
		await vscode.commands.executeCommand('notebook.execute');
		assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work

728
		metadataChangeEvent = getEventOncePromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
R
Rob Lourens 已提交
729
		editor.document.metadata.runnable = true;
730 731
		await metadataChangeEvent;

R
Rob Lourens 已提交
732 733 734
		await vscode.commands.executeCommand('notebook.execute');
		assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked

735
		await vscode.commands.executeCommand('workbench.action.files.save');
R
Rob Lourens 已提交
736 737
		await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	});
R
rebornix 已提交
738
});
R
rebornix 已提交
739 740 741

suite('notebook dirty state', () => {
	test('notebook open', async function () {
R
rebornix 已提交
742 743
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
744 745
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
746
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
747 748 749
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
750
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
751 752 753 754

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

R
rebornix 已提交
759 760 761
		const edit = new vscode.WorkspaceEdit();
		edit.insert(activeCell!.uri, new vscode.Position(0, 0), 'var abc = 0;');
		await vscode.workspace.applyEdit(edit);
R
rebornix 已提交
762 763 764
		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 已提交
765
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
R
rebornix 已提交
766

767
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
768 769 770 771
	});
});

suite('notebook undo redo', () => {
772
	test('notebook open', async function () {
R
rebornix 已提交
773 774
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
775 776
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
777
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test');
R
rebornix 已提交
778 779 780
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
781
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
R
rebornix 已提交
782 783 784 785

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
		const activeCell = vscode.notebook.activeNotebookEditor!.selection;
		assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined);
J
Johannes Rieken 已提交
786
		assert.equal(activeCell!.document.getText(), '');
R
rebornix 已提交
787 788 789 790 791
		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 已提交
792 793 794
		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 已提交
795 796 797 798 799 800
		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 已提交
801
		await vscode.commands.executeCommand('undo');
R
rebornix 已提交
802 803
		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 已提交
804
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
R
rebornix 已提交
805 806 807 808 809

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

812
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
813
	});
814

R
rebornix 已提交
815
	test.skip('execute and then undo redo', async function () {
R
rebornix 已提交
816 817
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
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 854 855 856 857 858 859 860 861 862 863 864 865 866
		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 已提交
867
		await vscode.commands.executeCommand('undo');
868 869 870 871 872 873 874
		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);

875
		await saveFileAndCloseAll(resource);
876 877
	});

R
rebornix 已提交
878
});
879 880

suite('notebook working copy', () => {
R
rebornix 已提交
881
	// test('notebook revert on close', async function () {
R
rebornix 已提交
882
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
883 884 885
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 	await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
886

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

R
rebornix 已提交
890 891 892 893 894 895 896
	// 	// 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');
897

R
rebornix 已提交
898 899 900
	// 	await vscode.commands.executeCommand('workbench.action.files.save');
	// 	await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
	// });
901

R
rebornix 已提交
902
	// test('notebook revert', async function () {
R
rebornix 已提交
903
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
904 905 906
	// 	await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
	// 	await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
907

R
rebornix 已提交
908 909 910
	// 	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');
911

R
rebornix 已提交
912 913 914 915 916
	// 	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');
917

R
rebornix 已提交
918 919 920
	// 	await vscode.commands.executeCommand('workbench.action.files.saveAll');
	// 	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// });
921 922

	test('multiple tabs: dirty + clean', async function () {
R
rebornix 已提交
923 924
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
925 926
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
927
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
928 929

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
930 931 932
		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);
933

R
rebornix 已提交
934
		const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
935 936 937 938 939 940 941 942
		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 已提交
943
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
944

945
		await saveFileAndCloseAll(resource);
946 947 948
	});

	test('multiple tabs: two dirty tabs and switching', async function () {
R
rebornix 已提交
949 950
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
951 952
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
953
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
954 955

		await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
R
rebornix 已提交
956 957 958
		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);
959

R
rebornix 已提交
960
		const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
961 962
		await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
J
Johannes Rieken 已提交
963
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
964 965 966 967 968 969 970

		// 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 已提交
971
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;');
972 973 974 975 976 977 978

		// 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 已提交
979
		assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), '');
980

981 982 983
		await saveAllFilesAndCloseAll(secondResource);
		// await vscode.commands.executeCommand('workbench.action.files.saveAll');
		// await vscode.commands.executeCommand('workbench.action.closeAllEditors');
984 985 986
	});

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

R
rebornix 已提交
989
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
990 991 992
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		const firstNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(firstNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
993
		assert.equal(firstNotebookEditor!.selection?.document.getText(), 'test');
994 995
		assert.equal(firstNotebookEditor!.selection?.language, 'typescript');

996
		await splitEditor();
997 998
		const secondNotebookEditor = vscode.notebook.activeNotebookEditor;
		assert.equal(secondNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
999
		assert.equal(secondNotebookEditor!.selection?.document.getText(), 'test');
1000 1001 1002 1003
		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 已提交
1004
		assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
1005

1006 1007 1008 1009
		await saveAllFilesAndCloseAll(resource);

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

R
rebornix 已提交
1013 1014
suite('metadata', () => {
	test('custom metadata should be supported', async function () {
R
rebornix 已提交
1015 1016
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
1017 1018
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
1019
		assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
R
rebornix 已提交
1020
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
R
rebornix 已提交
1021
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
1022

1023
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
1024
	});
1025

1026

R
rebornix 已提交
1027
	// TODO@rebornix skip as it crashes the process all the time
1028
	test.skip('custom metadata should be supported 2', async function () {
R
rebornix 已提交
1029 1030
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
1031 1032 1033 1034 1035 1036
		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');

1037 1038 1039 1040 1041
		// 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);
1042

1043
		await saveFileAndCloseAll(resource);
R
rebornix 已提交
1044
	});
R
rebornix 已提交
1045 1046
});

1047
suite('regression', () => {
R
rebornix 已提交
1048 1049 1050 1051 1052 1053 1054 1055
	// test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () {
	// 	assertInitalState();
	// 	await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" });
	// 	assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), '');
	// 	assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
	// 	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// });
1056 1057

	test('#97830, #97764. Support switch to other editor types', async function () {
R
rebornix 已提交
1058 1059
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
1060 1061
		await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
		await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
R
rebornix 已提交
1062 1063 1064
		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);
1065 1066

		assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
J
Johannes Rieken 已提交
1067
		assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;');
1068 1069 1070 1071 1072 1073 1074
		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');
	});
1075 1076 1077

	// open text editor, pin, and then open a notebook
	test('#96105 - dirty editors', async function () {
R
rebornix 已提交
1078 1079
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
1080
		await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
R
rebornix 已提交
1081
		const edit = new vscode.WorkspaceEdit();
R
rebornix 已提交
1082
		edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;');
R
rebornix 已提交
1083
		await vscode.workspace.applyEdit(edit);
1084 1085 1086 1087

		// 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');
R
rebornix 已提交
1088
		// assert.notEqual(vscode.window.activeTextEditor, undefined);
1089 1090 1091 1092

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

R
rebornix 已提交
1093
	test('#102411 - untitled notebook creation failed', async function () {
R
rebornix 已提交
1094
		assertInitalState();
J
Johannes Rieken 已提交
1095
		await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { viewType: 'notebookCoreTest' });
R
rebornix 已提交
1096
		assert.notEqual(vscode.notebook.activeNotebookEditor, undefined, 'untitled notebook editor is not undefined');
1097 1098

		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
R
rebornix 已提交
1099
	});
R
rebornix 已提交
1100 1101

	test('#102423 - copy/paste shares the same text buffer', async function () {
R
rebornix 已提交
1102 1103
		assertInitalState();
		const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
R
rebornix 已提交
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
		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 已提交
1115 1116 1117
		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 已提交
1118 1119

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

		await vscode.commands.executeCommand('workbench.action.closeAllEditors');
R
rebornix 已提交
1123
	});
1124
});
R
rebornix 已提交
1125

R
rebornix 已提交
1126
suite('webview', () => {
R
rebornix 已提交
1127
	// for web, `asWebUri` gets `https`?
1128 1129 1130 1131
	// test('asWebviewUri', async function () {
	// 	if (vscode.env.uiKind === vscode.UIKind.Web) {
	// 		return;
	// 	}
R
rebornix 已提交
1132

R
rebornix 已提交
1133
	// 	const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
1134 1135 1136
	// 	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'));
1137
	// 	assert.equal(uri.scheme, 'vscode-webview-resource');
1138 1139
	// 	await vscode.commands.executeCommand('workbench.action.closeAllEditors');
	// });
R
rebornix 已提交
1140

R
rebornix 已提交
1141 1142

	// 404 on web
1143 1144 1145 1146
	// test('custom renderer message', async function () {
	// 	if (vscode.env.uiKind === vscode.UIKind.Web) {
	// 		return;
	// 	}
R
rebornix 已提交
1147

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

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
	// 	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 已提交
1165
});