123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import expect = require('expect.js');
- import {
- CodeMirrorEditorFactory
- } from '@jupyterlab/codemirror';
- import {
- ObservableJSON
- } from '@jupyterlab/coreutils';
- import {
- JSONEditor
- } from '@jupyterlab/codeeditor';
- import {
- Message
- } from '@phosphor/messaging';
- import {
- Widget
- } from '@phosphor/widgets';
- import {
- simulate
- } from 'simulate-event';
- class LogEditor extends JSONEditor {
- methods: string[] = [];
- events: string[] = [];
- handleEvent(event: Event): void {
- super.handleEvent(event);
- this.events.push(event.type);
- }
- protected onAfterAttach(msg: Message): void {
- super.onAfterAttach(msg);
- this.methods.push('onAfterAttach');
- }
- protected onAfterShow(msg: Message): void {
- super.onAfterShow(msg);
- this.methods.push('onAfterShow');
- }
- protected onUpdateRequest(msg: Message): void {
- super.onUpdateRequest(msg);
- this.methods.push('onUpdateRequest');
- }
- protected onBeforeDetach(msg: Message): void {
- super.onBeforeDetach(msg);
- this.methods.push('onBeforeDetach');
- }
- }
- describe('apputils', () => {
- describe('JSONEditor', () => {
- let editor: LogEditor;
- let editorServices = new CodeMirrorEditorFactory();
- const editorFactory = editorServices.newInlineEditor.bind(editorServices);
- beforeEach(() => {
- editor = new LogEditor({ editorFactory });
- });
- afterEach(() => {
- editor.dispose();
- });
- describe('#constructor', () => {
- it('should create a new metadata editor', () => {
- let newEditor = new JSONEditor({ editorFactory });
- expect(newEditor).to.be.a(JSONEditor);
- });
- });
- describe('#collapsible', () => {
- it('should default to false', () => {
- expect(editor.collapsible).to.be(false);
- });
- it ('should be settable in the constructor', () => {
- let newEditor = new JSONEditor({ editorFactory, collapsible: true });
- expect(newEditor.collapsible).to.be(true);
- });
- });
- describe('#editorTitle', () => {
- it('should default to empty string', () => {
- expect(editor.editorTitle).to.be('');
- });
- it ('should be settable in the constructor', () => {
- let newEditor = new JSONEditor({ editorFactory, title: 'foo' });
- expect(newEditor.editorTitle).to.be('foo');
- });
- it('should be settable', () => {
- editor.editorTitle = 'foo';
- expect(editor.editorTitle).to.be('foo');
- });
- });
- describe('#headerNode', () => {
- it('should be the header node used by the editor', () => {
- expect(editor.headerNode.classList).to.contain('jp-JSONEditor-header');
- });
- });
- describe('#titleNode', () => {
- it('should be the title node used by the editor', () => {
- expect(editor.titleNode.classList).to.contain('jp-JSONEditor-title');
- });
- });
- describe('#collapserNode', () => {
- it('should be the collapser node used by the editor', () => {
- expect(editor.collapserNode.classList)
- .to.contain('jp-JSONEditor-collapser');
- });
- });
- describe('#editorHostNode', () => {
- it('should be the editor host node used by the editor', () => {
- expect(editor.editorHostNode.classList)
- .to.contain('jp-JSONEditor-host');
- });
- });
- describe('#revertButtonNode', () => {
- it('should be the revert button node used by the editor', () => {
- expect(editor.revertButtonNode.classList)
- .to.contain('jp-JSONEditor-revertButton');
- });
- });
- describe('#commitButtonNode', () => {
- it('should be the commit button node used by the editor', () => {
- expect(editor.commitButtonNode.classList)
- .to.contain('jp-JSONEditor-commitButton');
- });
- });
- describe('#source', () => {
- it('should be the source of the metadata', () => {
- expect(editor.source).to.be(null);
- });
- it('should be settable', () => {
- let source = new ObservableJSON();
- editor.source = source;
- expect(editor.source).to.be(source);
- });
- it('should update the text area value', () => {
- let model = editor.model;
- expect(model.value.text).to.be('No data!');
- editor.source = new ObservableJSON();
- expect(model.value.text).to.be('{}');
- });
- });
- describe('#isDirty', () => {
- it('should test whether the editor value is dirty', () => {
- expect(editor.isDirty).to.be(false);
- Widget.attach(editor, document.body);
- editor.model.value.text = 'a';
- expect(editor.isDirty).to.be(true);
- });
- it('should be dirty if the value changes while focused', () => {
- editor.source = new ObservableJSON();
- Widget.attach(editor, document.body);
- editor.editor.focus();
- expect(editor.isDirty).to.be(false);
- editor.source.set('foo', 1);
- expect(editor.isDirty).to.be(true);
- });
- it('should not be set if not focused', () => {
- editor.source = new ObservableJSON();
- Widget.attach(editor, document.body);
- expect(editor.isDirty).to.be(false);
- editor.source.set('foo', 1);
- expect(editor.isDirty).to.be(false);
- });
- });
- context('model.value.changed', () => {
- it('should add the error flag if invalid JSON', () => {
- editor.model.value.text = 'foo';
- expect(editor.hasClass('jp-mod-error')).to.be(true);
- });
- it('should show the commit button if the value has changed', () => {
- editor.model.value.text = '{"foo": 2}';
- editor.model.value.text = '{"foo": 1}';
- expect(editor.commitButtonNode.hidden).to.be(false);
- });
- it('should not show the commit button if the value is invalid', () => {
- editor.model.value.text = 'foo';
- expect(editor.commitButtonNode.hidden).to.be(true);
- });
- it('should show the revert button if the value has changed', () => {
- editor.model.value.text = 'foo';
- expect(editor.revertButtonNode.hidden).to.be(false);
- });
- });
- describe('#handleEvent()', () => {
- beforeEach(() => {
- Widget.attach(editor, document.body);
- });
- context('blur', () => {
- it('should handle blur events on the host node', () => {
- editor.editor.focus();
- simulate(editor.editorHostNode, 'blur');
- expect(editor.events).to.contain('blur');
- });
- it('should revert to current data if there was no change', () => {
- editor.source = new ObservableJSON();
- editor.editor.focus();
- editor.source.set('foo', 1);
- let model = editor.model;
- expect(model.value.text).to.be('{}');
- simulate(editor.editorHostNode, 'blur');
- expect(model.value.text).to.be('{\n "foo": 1\n}');
- });
- it('should not revert to current data if there was a change', () => {
- editor.source = new ObservableJSON();
- editor.model.value.text = 'foo';
- editor.source.set('foo', 1);
- let model = editor.model;
- expect(model.value.text).to.be('foo');
- simulate(editor.editorHostNode, 'blur');
- expect(model.value.text).to.be('foo');
- expect(editor.commitButtonNode.hidden).to.be(true);
- expect(editor.revertButtonNode.hidden).to.be(false);
- });
- });
- context('click', () => {
- it('should handle click events on the revert button', () => {
- simulate(editor.revertButtonNode, 'click');
- expect(editor.events).to.contain('click');
- });
- it('should revert the current data', () => {
- editor.source = new ObservableJSON();
- editor.model.value.text = 'foo';
- simulate(editor.revertButtonNode, 'click');
- expect(editor.model.value.text).to.be('{}');
- });
- it('should handle programmatic changes', () => {
- editor.source = new ObservableJSON();
- editor.model.value.text = 'foo';
- editor.source.set('foo', 1);
- simulate(editor.revertButtonNode, 'click');
- expect(editor.model.value.text).to.be('{\n "foo": 1\n}');
- });
- it('should handle click events on the commit button', () => {
- simulate(editor.commitButtonNode, 'click');
- expect(editor.events).to.contain('click');
- });
- it('should bail if it is not valid JSON', () => {
- editor.source = new ObservableJSON();
- editor.model.value.text = 'foo';
- editor.source.set('foo', 1);
- simulate(editor.commitButtonNode, 'click');
- expect(editor.model.value.text).to.be('foo');
- });
- it('should override a key that was set programmatically', () => {
- editor.source = new ObservableJSON();
- editor.model.value.text = '{"foo": 2}';
- editor.source.set('foo', 1);
- simulate(editor.commitButtonNode, 'click');
- expect(editor.model.value.text).to.be('{\n "foo": 2\n}');
- });
- it('should allow a programmatic key to update', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- editor.source.set('bar', 1);
- editor.model.value.text = '{"foo":1, "bar": 2}';
- editor.source.set('foo', 2);
- simulate(editor.commitButtonNode, 'click');
- let expected = '{\n "foo": 2,\n "bar": 2\n}';
- expect(editor.model.value.text).to.be(expected);
- });
- it('should allow a key to be added by the user', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- editor.source.set('bar', 1);
- editor.model.value.text = '{"foo":1, "bar": 2, "baz": 3}';
- editor.source.set('foo', 2);
- simulate(editor.commitButtonNode, 'click');
- let value = '{\n "foo": 2,\n "bar": 2,\n "baz": 3\n}';
- expect(editor.model.value.text).to.be(value);
- });
- it('should allow a key to be removed by the user', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- editor.source.set('bar', 1);
- editor.model.value.text = '{"foo": 1}';
- simulate(editor.commitButtonNode, 'click');
- expect(editor.model.value.text).to.be('{\n "foo": 1\n}');
- });
- it('should allow a key to be removed programmatically that was not set by the user', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- editor.source.set('bar', 1);
- editor.model.value.text = '{"foo": 1, "bar": 3}';
- editor.source.delete('foo');
- simulate(editor.commitButtonNode, 'click');
- expect(editor.model.value.text).to.be('{\n "bar": 3\n}');
- });
- it('should keep a key that was removed programmatically that was changed by the user', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- editor.source.set('bar', 1);
- editor.model.value.text = '{"foo": 2, "bar": 3}';
- editor.source.set('foo', null);
- simulate(editor.commitButtonNode, 'click');
- let expected = '{\n "foo": 2,\n "bar": 3\n}';
- expect(editor.model.value.text).to.be(expected);
- });
- it('should collapse the editor', () => {
- editor.dispose();
- editor = new LogEditor({ editorFactory, collapsible: true });
- Widget.attach(editor, document.body);
- simulate(editor.titleNode, 'click');
- expect(editor.editorHostNode.classList).to.contain('jp-mod-collapsed');
- });
- it('should have no effect if the editor is not collapsible', () => {
- simulate(editor.titleNode, 'click');
- expect(editor.editorHostNode.classList).to.not.contain('jp-mod-collapsed');
- });
- });
- });
- describe('#onAfterAttach()', () => {
- it('should add event listeners', () => {
- Widget.attach(editor, document.body);
- expect(editor.methods).to.contain('onAfterAttach');
- editor.editor.focus();
- simulate(editor.editorHostNode, 'blur');
- simulate(editor.revertButtonNode, 'click');
- simulate(editor.commitButtonNode, 'click');
- expect(editor.events).to.eql(['blur', 'click', 'click']);
- });
- });
- describe('#onAfterShow()', () => {
- it('should update the editor', done => {
- editor.hide();
- Widget.attach(editor, document.body);
- editor.show();
- requestAnimationFrame(() => {
- expect(editor.methods).to.contain('onUpdateRequest');
- done();
- });
- });
- });
- describe('#onBeforeDetach()', () => {
- it('should remove event listeners', () => {
- Widget.attach(editor, document.body);
- Widget.detach(editor);
- expect(editor.methods).to.contain('onBeforeDetach');
- editor.editor.focus();
- simulate(editor.editorHostNode, 'blur');
- simulate(editor.revertButtonNode, 'click');
- simulate(editor.commitButtonNode, 'click');
- expect(editor.events).to.eql([]);
- });
- });
- context('#source.changed', () => {
- it('should update the value', () => {
- editor.source = new ObservableJSON();
- editor.source.set('foo', 1);
- expect(editor.model.value.text).to.be('{\n "foo": 1\n}');
- });
- it('should bail if the input is dirty', () => {
- Widget.attach(editor, document.body);
- editor.source = new ObservableJSON();
- editor.model.value.text = 'ha';
- editor.source.set('foo', 2);
- expect(editor.model.value.text).to.be('ha');
- });
- it('should bail if the input is focused', () => {
- Widget.attach(editor, document.body);
- editor.model.value.text = '{}';
- editor.source = new ObservableJSON();
- editor.editor.focus();
- editor.source.set('foo', 2);
- expect(editor.model.value.text).to.be('{}');
- });
- });
- });
- });
|