|
@@ -3,103 +3,87 @@
|
|
|
|
|
|
import expect = require('expect.js');
|
|
|
|
|
|
-import {
|
|
|
- INotebookSession
|
|
|
-} from 'jupyter-js-services';
|
|
|
-
|
|
|
import {
|
|
|
ObservableList, IListChangedArgs
|
|
|
} from 'phosphor-observablelist';
|
|
|
|
|
|
import {
|
|
|
- IChangedArgs
|
|
|
-} from 'phosphor-properties';
|
|
|
-
|
|
|
-import {
|
|
|
- EditorModel, IEditorModel
|
|
|
-} from '../../../../lib/notebook/editor/model';
|
|
|
+ ICellModel
|
|
|
+} from '../../../../lib/notebook/cells/model';
|
|
|
|
|
|
import {
|
|
|
- InputAreaModel
|
|
|
-} from '../../../../lib/notebook/input-area/model';
|
|
|
+ JSONObject, deepEqual
|
|
|
+} from '../../../../lib/notebook/common/json';
|
|
|
|
|
|
import {
|
|
|
- OutputAreaModel
|
|
|
-} from '../../../../lib/notebook/output-area/model';
|
|
|
+ ObservableUndoableList
|
|
|
+} from '../../../../lib/notebook/common/undo';
|
|
|
|
|
|
import {
|
|
|
- BaseCellModel, CodeCellModel, MarkdownCellModel, MetadataCursor,
|
|
|
- RawCellModel, ICellModel
|
|
|
-} from '../../../../lib/notebook/cells/model';
|
|
|
+ nbformat
|
|
|
+} from '../../../../lib/notebook/notebook/nbformat';
|
|
|
|
|
|
import {
|
|
|
NotebookModel
|
|
|
} from '../../../../lib/notebook/notebook/model';
|
|
|
|
|
|
-import {
|
|
|
- MockSession
|
|
|
-} from 'jupyter-js-services/lib/mocksession';
|
|
|
|
|
|
-import {
|
|
|
- MockKernel
|
|
|
-} from 'jupyter-js-services/lib/mockkernel';
|
|
|
+const DEFAULT_CONTENT: nbformat.INotebookContent = require('../../../../examples/notebook/test.ipynb') as nbformat.INotebookContent;
|
|
|
|
|
|
|
|
|
/**
|
|
|
* A notebook model which tests protected methods.
|
|
|
*/
|
|
|
-class MyNotebookModel extends NotebookModel {
|
|
|
+class LogNotebookModel extends NotebookModel {
|
|
|
methods: string[] = [];
|
|
|
|
|
|
- protected onEditorChanged(editor: IEditorModel, args: IChangedArgs<any>): void {
|
|
|
- super.onEditorChanged(editor, args);
|
|
|
- this.methods.push('onEditorChanged');
|
|
|
+ protected onCellChanged(cell: ICellModel, change: any): void {
|
|
|
+ super.onCellChanged(cell, change);
|
|
|
+ this.methods.push('onCellsChanged');
|
|
|
}
|
|
|
|
|
|
protected onCellsChanged(list: ObservableList<ICellModel>, change: IListChangedArgs<ICellModel>): void {
|
|
|
super.onCellsChanged(list, change);
|
|
|
this.methods.push('onCellsChanged');
|
|
|
}
|
|
|
+
|
|
|
+ protected setCursorData(name: string, newValue: any): void {
|
|
|
+ super.setCursorData(name, newValue);
|
|
|
+ this.methods.push('setCursorData');
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
-describe('jupyter-js-notebook', () => {
|
|
|
+describe('notebook/notebook', () => {
|
|
|
|
|
|
describe('NotebookModel', () => {
|
|
|
|
|
|
describe('#constructor()', () => {
|
|
|
|
|
|
- it('should create an notebook model', () => {
|
|
|
+ it('should create a notebook model', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model instanceof NotebookModel).to.be(true);
|
|
|
+ expect(model).to.be.a(NotebookModel);
|
|
|
});
|
|
|
|
|
|
- });
|
|
|
-
|
|
|
- describe('#stateChanged', () => {
|
|
|
-
|
|
|
- it('should be emitted when the state changes', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('readOnly');
|
|
|
- expect(change.oldValue).to.be(false);
|
|
|
- expect(change.newValue).to.be(true);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.readOnly = true;
|
|
|
- expect(called).to.be(true);
|
|
|
+ it('should accept an optional language preference', () => {
|
|
|
+ let model = new NotebookModel('python');
|
|
|
+ let cursor = model.getMetadata('language_info');
|
|
|
+ let lang = cursor.getValue() as nbformat.ILanguageInfoMetadata;
|
|
|
+ expect(lang.name).to.be('python');
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
describe('#metadataChanged', () => {
|
|
|
|
|
|
- it ('should be emitted when metadata changes', () => {
|
|
|
+ it('should be emitted when a metadata field changes', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let called = false;
|
|
|
- model.metadataChanged.connect((cell, name) => {
|
|
|
- expect(name).to.be('foo');
|
|
|
+ model.metadataChanged.connect((sender, args) => {
|
|
|
+ expect(sender).to.be(model);
|
|
|
+ expect(args.name).to.be('foo');
|
|
|
+ expect(args.oldValue).to.be(void 0);
|
|
|
+ expect(args.newValue).to.be(1);
|
|
|
called = true;
|
|
|
});
|
|
|
let foo = model.getMetadata('foo');
|
|
@@ -107,349 +91,212 @@ describe('jupyter-js-notebook', () => {
|
|
|
expect(called).to.be(true);
|
|
|
});
|
|
|
|
|
|
- });
|
|
|
-
|
|
|
- describe('#cells', () => {
|
|
|
-
|
|
|
- it('should be an observable list', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- expect(model.cells instanceof ObservableList).to.be(true);
|
|
|
- });
|
|
|
-
|
|
|
- it('should be read-only', () => {
|
|
|
+ it('should not be emitted when the value does not change', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(() => { model.cells = null; }).to.throwError();
|
|
|
+ let called = false;
|
|
|
+ let foo = model.getMetadata('foo');
|
|
|
+ foo.setValue(1);
|
|
|
+ model.metadataChanged.connect(() => { called = true; });
|
|
|
+ foo.setValue(1);
|
|
|
+ expect(called).to.be(false);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#defaultMimetype', () => {
|
|
|
+ describe('#cells', () => {
|
|
|
|
|
|
- it('should default to `text/x-ipython`', () => {
|
|
|
+ it('should be an observable undoable list', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.defaultMimetype).to.be('text/x-ipython');
|
|
|
+ expect(model.cells).to.be.an(ObservableUndoableList);
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should default to an empty list', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('defaultMimetype');
|
|
|
- expect(change.oldValue).to.be('text/x-ipython');
|
|
|
- expect(change.newValue).to.be('text/python');
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.defaultMimetype = 'text/python';
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(model.cells.length).to.be(0);
|
|
|
});
|
|
|
|
|
|
- });
|
|
|
-
|
|
|
- describe('#readOnly', () => {
|
|
|
-
|
|
|
- it('should default to false', () => {
|
|
|
+ it('should be reset when loading from disk', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.readOnly).to.be(false);
|
|
|
+ let cell = model.createCodeCell();
|
|
|
+ model.cells.add(cell);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.cells.indexOf(cell)).to.be(-1);
|
|
|
+ expect(model.cells.length).to.be(6);
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should be read-only', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('readOnly');
|
|
|
- expect(change.oldValue).to.be(false);;
|
|
|
- expect(change.newValue).to.be(true);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.readOnly = true;
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(() => { model.cells = null; }).to.throwError();
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#session', () => {
|
|
|
+ describe('#nbformat', () => {
|
|
|
|
|
|
- it('should default to null', () => {
|
|
|
+ it('should get the major version number of the nbformat', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.session).to.be(null);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.nbformat).to.be(DEFAULT_CONTENT.nbformat);
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should be read-only', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- let session = new MockSession('test.ipynb');
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('session');
|
|
|
- expect(change.oldValue).to.be(null);
|
|
|
- expect(change.newValue).to.be(session);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.session = session;
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(() => { model.nbformat = 0; }).to.throwError();
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#kernelspec', () => {
|
|
|
+ describe('#nbformatMinor', () => {
|
|
|
|
|
|
- it('should default to an unknown kernel', () => {
|
|
|
+ it('should get the minor version number of the nbformat', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.kernelspec.name).to.be('unknown');
|
|
|
- expect(model.kernelspec.display_name).to.be('No Kernel!');
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.nbformatMinor).to.be(DEFAULT_CONTENT.nbformat_minor);
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should be read-only', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('kernelspec');
|
|
|
- expect(change.oldValue.name).to.be('unknown');;
|
|
|
- expect(change.newValue.name).to.be('python');
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.kernelspec = { name: 'python', display_name: 'Python' };
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(() => { model.nbformatMinor = 0; }).to.throwError();
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#languageInfo', () => {
|
|
|
+ describe('#defaultKernelName()', () => {
|
|
|
|
|
|
- it('should default to an unknown language', () => {
|
|
|
+ it('should get the default kernel name of the document', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.languageInfo.name).to.be('unknown');
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.defaultKernelName).to.be('python3');
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should default to an empty string', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('languageInfo');
|
|
|
- expect(change.oldValue.name).to.be('unknown');;
|
|
|
- expect(change.newValue.name).to.be('python');
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.languageInfo = { name: 'python' };
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(model.defaultKernelName).to.be('');
|
|
|
});
|
|
|
|
|
|
- });
|
|
|
-
|
|
|
- describe('#origNbformat', () => {
|
|
|
-
|
|
|
- it('should default to null', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- expect(model.origNbformat).to.be(null);
|
|
|
- });
|
|
|
-
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should be read-only', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('origNbformat');
|
|
|
- expect(change.oldValue).to.be(null);
|
|
|
- expect(change.newValue).to.be(4);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.origNbformat = 4;
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(() => { model.defaultKernelName = ''; }).to.throwError();
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#activeCellIndex', () => {
|
|
|
+ describe('#defaultKernelLanguage', () => {
|
|
|
|
|
|
- it('should default to null', () => {
|
|
|
+ it('should get the default kernel language of the document', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.activeCellIndex).to.be(null);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.defaultKernelLanguage).to.be('python');
|
|
|
});
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should default to an empty string', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('activeCellIndex');
|
|
|
- expect(change.oldValue).to.be(null);
|
|
|
- expect(change.newValue).to.be(0);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.cells.add(model.createMarkdownCell());
|
|
|
- model.activeCellIndex = 0;
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(model.defaultKernelLanguage).to.be('');
|
|
|
});
|
|
|
|
|
|
- it('should be clamped to the length of the cells list', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- model.cells.add(model.createMarkdownCell());
|
|
|
- model.cells.add(model.createMarkdownCell());
|
|
|
- model.activeCellIndex = -1;
|
|
|
- expect(model.activeCellIndex).to.be(0);
|
|
|
- model.activeCellIndex = 2;
|
|
|
- expect(model.activeCellIndex).to.be(1);
|
|
|
+ it('should be set from the constructor arg', () => {
|
|
|
+ let model = new NotebookModel('foo');
|
|
|
+ expect(model.defaultKernelLanguage).to.be('foo');
|
|
|
});
|
|
|
|
|
|
-
|
|
|
- it('should unrender a markdown cell if in edit mode', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let cell0 = model.createMarkdownCell();
|
|
|
- let cell1 = model.createMarkdownCell();
|
|
|
- model.cells.add(cell0);
|
|
|
- model.cells.add(cell1);
|
|
|
- model.mode = 'edit';
|
|
|
- debugger;
|
|
|
- expect(cell0.rendered).to.be(true);
|
|
|
- expect(cell1.rendered).to.be(false);
|
|
|
- model.activeCellIndex = 0;
|
|
|
- expect(cell0.rendered).to.be(false);
|
|
|
- });
|
|
|
-
|
|
|
- });
|
|
|
-
|
|
|
- describe('#mode', () => {
|
|
|
-
|
|
|
- it('should default to command', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- expect(model.mode).to.be('command');
|
|
|
- });
|
|
|
-
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should be read-only', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('mode');
|
|
|
- expect(change.oldValue).to.be('command');
|
|
|
- expect(change.newValue).to.be('edit');
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.mode = 'edit';
|
|
|
- expect(called).to.be(true);
|
|
|
+ expect(() => { model.defaultKernelLanguage = ''; }).to.throwError();
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#dirty', () => {
|
|
|
-
|
|
|
- it('should default to false', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- expect(model.dirty).to.be(false);
|
|
|
- });
|
|
|
+ describe('#dispose()', () => {
|
|
|
|
|
|
- it('should emit a stateChanged signal when changed', () => {
|
|
|
+ it('should dispose of the resources held by the model', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect((nb, change) => {
|
|
|
- expect(change.name).to.be('dirty');
|
|
|
- expect(change.oldValue).to.be(false);
|
|
|
- expect(change.newValue).to.be(true);
|
|
|
- called = true;
|
|
|
- });
|
|
|
- model.dirty = true;
|
|
|
- expect(called).to.be(true);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ model.dispose();
|
|
|
+ expect(model.cells).to.be(null);
|
|
|
+ expect(model.isDisposed).to.be(true);
|
|
|
});
|
|
|
|
|
|
- });
|
|
|
-
|
|
|
- describe('#isDisposed', () => {
|
|
|
-
|
|
|
- it('should indicate whether the model is disposed', () => {
|
|
|
+ it('should be safe to call multiple times', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- expect(model.isDisposed).to.be(false);
|
|
|
+ model.dispose();
|
|
|
model.dispose();
|
|
|
expect(model.isDisposed).to.be(true);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#dispose()', () => {
|
|
|
+ describe('#toString()', () => {
|
|
|
|
|
|
- it('should dispose of the resource held by the model', () => {
|
|
|
+ it('should serialize the model to a string', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.stateChanged.connect(() => { called = true; });
|
|
|
- model.dispose();
|
|
|
- model.dirty = true;
|
|
|
- expect(called).to.be(false);
|
|
|
- expect(model.cells).to.be(null);
|
|
|
- });
|
|
|
-
|
|
|
- it('should be safe to call multiple times', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- model.dispose();
|
|
|
- model.dispose();
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ let text = model.toString();
|
|
|
+ let data = JSON.parse(text);
|
|
|
+ // TODO: use JSON types in services then deepEqual here.
|
|
|
+ expect(data.cells[0]).to.eql(DEFAULT_CONTENT.cells[0]);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#metadataChanged', () => {
|
|
|
+ describe('#fromString()', () => {
|
|
|
|
|
|
- it('should be emitted when metadata changes', () => {
|
|
|
+ it('should deserialize the model from a string', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let called = false;
|
|
|
- model.metadataChanged.connect((cell, name) => {
|
|
|
- expect(name).to.be('foo');
|
|
|
- called = true;
|
|
|
- });
|
|
|
- let foo = model.getMetadata('foo');
|
|
|
- foo.setValue(1);
|
|
|
- expect(called).to.be(true);
|
|
|
+ model.fromString(JSON.stringify(DEFAULT_CONTENT));
|
|
|
+ expect(model.cells.length).to.be(6);
|
|
|
});
|
|
|
|
|
|
- it('should throw an error on blacklisted names', () => {
|
|
|
+ it('should set the dirty flag', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let invalid = ['kernelspec', 'languageInfo', 'origNbformat'];
|
|
|
- for (let key of invalid) {
|
|
|
- expect(() => { model.getMetadata(key); }).to.throwError();
|
|
|
- }
|
|
|
+ model.dirty = false;
|
|
|
+ model.fromString(JSON.stringify(DEFAULT_CONTENT));
|
|
|
+ expect(model.dirty).to.be(true);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#select()', () => {
|
|
|
+ describe('#toJSON()', () => {
|
|
|
|
|
|
- it('should select a cell', () => {
|
|
|
+ it('should serialize the model to JSON', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createMarkdownCell();
|
|
|
- model.cells.add(cell);
|
|
|
- model.cells.add(model.createCodeCell())
|
|
|
- expect(model.isSelected(cell)).to.be(false);
|
|
|
- model.select(cell);
|
|
|
- expect(model.isSelected(cell)).to.be(true);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ let data = model.toJSON();
|
|
|
+ // TODO: use JSON types in services then deepEqual here.
|
|
|
+ expect(data.cells[0]).to.eql(DEFAULT_CONTENT.cells[0]);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#deselect()', () => {
|
|
|
+ describe('#fromJSON()', () => {
|
|
|
|
|
|
- it('should deselect a cell', () => {
|
|
|
+ it('should serialize the model from JSON', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- model.cells.add(cell);
|
|
|
- model.cells.add(model.createCodeCell());
|
|
|
- model.select(cell);
|
|
|
- expect(model.isSelected(cell)).to.be(true);
|
|
|
- model.deselect(cell);
|
|
|
- expect(model.isSelected(cell)).to.be(false);
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.cells.length).to.be(6);
|
|
|
+ expect(model.nbformat).to.be(DEFAULT_CONTENT.nbformat);
|
|
|
+ expect(model.nbformatMinor).to.be(DEFAULT_CONTENT.nbformat_minor);
|
|
|
});
|
|
|
|
|
|
- it('should have no effect on the active cell', () => {
|
|
|
+ it('should set the dirty flag', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- model.cells.add(cell);
|
|
|
- model.deselect(cell);
|
|
|
- expect(model.isSelected(cell)).to.be(true);
|
|
|
+ model.dirty = false;
|
|
|
+ model.fromJSON(DEFAULT_CONTENT);
|
|
|
+ expect(model.dirty).to.be(true);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#isSelected()', () => {
|
|
|
+ describe('#initialize()', () => {
|
|
|
|
|
|
- it('should indicate whether a cell is selected', () => {
|
|
|
+ it('should initialize the model state', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createCodeCell();
|
|
|
model.cells.add(cell);
|
|
|
- expect(model.isSelected(cell)).to.be(true);
|
|
|
- model.cells.add(model.createMarkdownCell());
|
|
|
- expect(model.isSelected(cell)).to.be(false);
|
|
|
+ expect(model.dirty).to.be(true);
|
|
|
+ expect(model.cells.canUndo).to.be(true);
|
|
|
+ model.initialize();
|
|
|
+ expect(model.dirty).to.be(false);
|
|
|
+ expect(model.cells.canUndo).to.be(false);
|
|
|
});
|
|
|
|
|
|
});
|
|
@@ -459,78 +306,23 @@ describe('jupyter-js-notebook', () => {
|
|
|
it('should create a new code cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createCodeCell();
|
|
|
- expect(cell instanceof CodeCellModel).to.be(true);
|
|
|
+ expect(cell.type).to.be('code');
|
|
|
});
|
|
|
|
|
|
- it('should clone a code cell model', () => {
|
|
|
+ it('should clone an existing code cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createCodeCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- cell.collapsed = true;
|
|
|
- cell.scrolled = true;
|
|
|
- cell.output.outputs.add({
|
|
|
- output_type: 'error',
|
|
|
- ename: 'foo',
|
|
|
- evalue: '',
|
|
|
- traceback: ['']
|
|
|
- });
|
|
|
- let newCell = model.createCodeCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
- expect(newCell.collapsed).to.be(true);
|
|
|
- expect(newCell.scrolled).to.be(true);
|
|
|
- expect(newCell.output.outputs.length).to.be(1);
|
|
|
- });
|
|
|
-
|
|
|
- it('should clone from a markdown cell model', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let cell = model.createMarkdownCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- let newCell = model.createCodeCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
- });
|
|
|
-
|
|
|
- });
|
|
|
-
|
|
|
- describe('#createCodeCell()', () => {
|
|
|
-
|
|
|
- it('should create a new markdown cell', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let cell = model.createMarkdownCell();
|
|
|
- expect(cell instanceof MarkdownCellModel).to.be(true);
|
|
|
- });
|
|
|
-
|
|
|
- it('should clone a markdown cell model', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let cell = model.createMarkdownCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- cell.rendered = false;
|
|
|
- let newCell = model.createMarkdownCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
- expect(newCell.rendered).to.be(false);
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createCodeCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
- it('should clone from a code cell model', () => {
|
|
|
+ it('should clone an existing raw cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- let newCell = model.createMarkdownCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
+ let cell = model.createRawCell();
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createCodeCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
});
|
|
@@ -540,182 +332,95 @@ describe('jupyter-js-notebook', () => {
|
|
|
it('should create a new raw cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createRawCell();
|
|
|
- expect(cell instanceof RawCellModel).to.be(true);
|
|
|
+ expect(cell.type).to.be('raw');
|
|
|
});
|
|
|
|
|
|
- it('should clone a raw cell model', () => {
|
|
|
+ it('should clone an existing raw cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createRawCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- cell.format = 'foo';
|
|
|
- let newCell = model.createRawCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
- expect(newCell.format).to.be('foo');
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createRawCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
- it('should clone from a code cell model', () => {
|
|
|
+ it('should clone an existing code cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
let cell = model.createCodeCell();
|
|
|
- cell.trusted = true;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- cell.tags = ['foo', 'bar'];
|
|
|
- let newCell = model.createRawCell(cell);
|
|
|
- expect(newCell.trusted).to.be(true);
|
|
|
- expect(newCell.input.textEditor.text).to.be('foo');
|
|
|
- expect(newCell.tags).to.eql(['foo', 'bar']);
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createRawCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#runActiveCell()', () => {
|
|
|
+ describe('#createMarkdownCell()', () => {
|
|
|
|
|
|
- it('should mark the active cell as trusted ', () => {
|
|
|
+ it('should create a new markdown cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createRawCell();
|
|
|
- cell.trusted = false;
|
|
|
- model.cells.add(cell);
|
|
|
- model.activeCellIndex = 0;
|
|
|
- model.runActiveCell();
|
|
|
- expect(cell.trusted).to.be(true);
|
|
|
+ let cell = model.createMarkdownCell();
|
|
|
+ expect(cell.type).to.be('markdown');
|
|
|
});
|
|
|
|
|
|
- it('should have no effect on a readonly notebook', () => {
|
|
|
+ it('should clone an existing markdown cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- cell.trusted = false;
|
|
|
- model.cells.add(cell);
|
|
|
- model.activeCellIndex = 0;
|
|
|
- model.readOnly = true;
|
|
|
- model.runActiveCell();
|
|
|
- expect(cell.trusted).to.be(false);
|
|
|
+ let cell = model.createMarkdownCell();
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createMarkdownCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
- it('should have no effect if there is no active cell', () => {
|
|
|
+ it('should clone an existing raw cell', () => {
|
|
|
let model = new NotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- cell.trusted = false;
|
|
|
- model.runActiveCell();
|
|
|
- expect(cell.trusted).to.be(false);
|
|
|
+ let cell = model.createRawCell();
|
|
|
+ cell.source = 'foo';
|
|
|
+ let newCell = model.createMarkdownCell(cell.toJSON());
|
|
|
+ expect(newCell.source).to.be('foo');
|
|
|
});
|
|
|
|
|
|
- it('should render a markdown cell', () => {
|
|
|
- let model = new NotebookModel();
|
|
|
- let cell = model.createMarkdownCell();
|
|
|
- cell.rendered = false;
|
|
|
- model.cells.add(cell);
|
|
|
- model.runActiveCell();
|
|
|
- expect(cell.rendered).to.be(true);
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- it('should clear the prompt on a code cell if there is no session', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- cell.input.textEditor.text = 'a = 1';
|
|
|
- cell.input.prompt = '';
|
|
|
- model.cells.add(cell);
|
|
|
- model.runActiveCell();
|
|
|
- expect(cell.input.prompt).to.be('');
|
|
|
+ describe('#getMetadata()', () => {
|
|
|
+
|
|
|
+ it('should get a metadata cursor for the notebook', () => {
|
|
|
+ let model = new NotebookModel();
|
|
|
+ let cursor = model.getMetadata('foo');
|
|
|
+ expect(cursor.getValue()).to.be(void 0);
|
|
|
});
|
|
|
|
|
|
- it('should execute on a code cell when there is a session', (done) => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- model.session = new MockSession('test.ipynb');
|
|
|
- let cell = model.createCodeCell();
|
|
|
- cell.input.textEditor.text = 'a = 1';
|
|
|
- model.cells.add(cell);
|
|
|
- let called = false;
|
|
|
- model.session.statusChanged.connect(() => {
|
|
|
- if (called) {
|
|
|
- return;
|
|
|
- }
|
|
|
- called = true;
|
|
|
- model.runActiveCell();
|
|
|
- let kernel = model.session.kernel as MockKernel;
|
|
|
- kernel.sendShellReply({
|
|
|
- execution_count: 1,
|
|
|
- data: {},
|
|
|
- metadata: {}
|
|
|
- });
|
|
|
- });
|
|
|
- cell.input.stateChanged.connect(() => {
|
|
|
- if (cell.input.prompt === '1') {
|
|
|
- done();
|
|
|
- }
|
|
|
- });
|
|
|
+ it('should get the value for all cursors', () => {
|
|
|
+ let model = new NotebookModel();
|
|
|
+ let cursor0 = model.getMetadata('foo');
|
|
|
+ let cursor1 = model.getMetadata('foo');
|
|
|
+ cursor0.setValue(1);
|
|
|
+ expect(cursor1.getValue()).to.be(1);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
- describe('#onEditorChanged()', () => {
|
|
|
+ describe('#listMetadata()', () => {
|
|
|
|
|
|
- it('should set the dirty flag when a text editor text changes', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- model.cells.add(cell);
|
|
|
- model.dirty = false;
|
|
|
- cell.input.textEditor.text = 'foo';
|
|
|
- expect(model.dirty).to.be(true);
|
|
|
- expect(model.methods.indexOf('onEditorChanged')).to.not.be(-1);
|
|
|
+ it('should list the metadata namespace keys for the notebook', () => {
|
|
|
+ let model = new NotebookModel();
|
|
|
+ let keys = ['kernelspec', 'language_info', 'orig_nbformat'];
|
|
|
+ expect(model.listMetadata()).to.eql(keys);
|
|
|
+ let cursor = model.getMetadata('foo');
|
|
|
+ expect(model.listMetadata()).to.eql(keys);
|
|
|
+ cursor.setValue(1);
|
|
|
+ keys.push('foo');
|
|
|
+ expect(model.listMetadata()).to.eql(keys);
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
describe('#onCellsChanged()', () => {
|
|
|
|
|
|
- it('should set the dirty flag', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- expect(model.dirty).to.be(false);
|
|
|
- model.cells.add(cell);
|
|
|
- expect(model.methods.indexOf('onCellsChanged')).to.not.be(-1);
|
|
|
- expect(model.dirty).to.be(true);
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- it('should set the activeCellIndex on an add', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- let cell = model.createCodeCell();
|
|
|
- expect(model.activeCellIndex).to.be(null);
|
|
|
- model.cells.add(cell);
|
|
|
- expect(model.activeCellIndex).to.be(0);
|
|
|
- expect(model.methods.indexOf('onCellsChanged')).to.not.be(-1);
|
|
|
- expect(model.dirty).to.be(true);
|
|
|
- });
|
|
|
+ describe('#onCellChanged()', () => {
|
|
|
|
|
|
- it('should adjust the activeCellIndex on a remove', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- model.cells.add(model.createCodeCell());
|
|
|
- let cell = model.createCodeCell();
|
|
|
- model.cells.add(cell);
|
|
|
- expect(model.activeCellIndex).to.be(1);
|
|
|
- model.dirty = false;
|
|
|
- model.cells.remove(cell);
|
|
|
- expect(model.activeCellIndex).to.be(0);
|
|
|
- expect(model.methods.indexOf('onCellsChanged')).to.not.be(-1);
|
|
|
- expect(model.dirty).to.be(true);
|
|
|
- expect(cell.isDisposed).to.be(true);
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- it('should dispose of all old cells on a replace', () => {
|
|
|
- let model = new MyNotebookModel();
|
|
|
- let cells: ICellModel[] = [];
|
|
|
- for (let i = 0; i < 5; i++) {
|
|
|
- let cell = model.createMarkdownCell()
|
|
|
- cells.push(cell);
|
|
|
- model.cells.add(cell);
|
|
|
- }
|
|
|
- model.dirty = false;
|
|
|
- model.cells.clear();
|
|
|
- expect(model.methods.indexOf('onCellsChanged')).to.not.be(-1);
|
|
|
- expect(model.dirty).to.be(true);
|
|
|
- for (let i = 0; i < 5; i++) {
|
|
|
- let cell = cells[i];
|
|
|
- expect(cell.isDisposed).to.be(true);
|
|
|
- }
|
|
|
- });
|
|
|
+ describe('#setCursorData()', () => {
|
|
|
|
|
|
});
|
|
|
|