Browse Source

Finish active notebook tests

Steven Silvester 9 years ago
parent
commit
95bb660709
3 changed files with 263 additions and 13 deletions
  1. 1 0
      package.json
  2. 21 11
      src/notebook/notebook/widget.ts
  3. 241 2
      test/src/notebook/notebook/widget.spec.ts

+ 1 - 0
package.json

@@ -34,6 +34,7 @@
     "phosphor-tabs": "^1.0.0-rc.2",
     "phosphor-widget": "^1.0.0-rc.1",
     "sanitizer": "^0.1.3",
+    "simulate-event": "^1.2.0",
     "xterm": "^0.33.0"
   },
   "devDependencies": {

+ 21 - 11
src/notebook/notebook/widget.ts

@@ -425,6 +425,9 @@ class ActiveNotebook extends NotebookRenderer {
     case 'dblclick':
       this._evtDblClick(event as MouseEvent);
       break;
+    case 'focus':
+      this._evtFocus(event as FocusEvent);
+      break;
     default:
       break;
     }
@@ -437,6 +440,7 @@ class ActiveNotebook extends NotebookRenderer {
     super.onAfterAttach(msg);
     this.node.addEventListener('click', this);
     this.node.addEventListener('dblclick', this);
+    this.node.addEventListener('focus', this, true);
     this.update();
   }
 
@@ -446,6 +450,7 @@ class ActiveNotebook extends NotebookRenderer {
   protected onBeforeDetach(msg: Message): void {
     this.node.removeEventListener('click', this);
     this.node.removeEventListener('dblclick', this);
+    this.node.removeEventListener('focus', this, true);
   }
 
   /**
@@ -460,9 +465,9 @@ class ActiveNotebook extends NotebookRenderer {
       this.removeClass(COMMAND_CLASS);
       if (widget) {
         widget.focus();
-      }
-      if (widget instanceof MarkdownCellWidget) {
-        (widget as MarkdownCellWidget).rendered = false;
+        if (widget instanceof MarkdownCellWidget) {
+          (widget as MarkdownCellWidget).rendered = false;
+        }
       }
     } else {
       this.addClass(COMMAND_CLASS);
@@ -471,11 +476,8 @@ class ActiveNotebook extends NotebookRenderer {
     }
     if (widget) {
       widget.addClass(ACTIVE_CLASS);
-      Private.scrollIfNeeded(this.parent.node, widget.node);
-      if (widget instanceof MarkdownCellWidget) {
-        if (this.mode === 'edit') {
-          widget.rendered = false;
-        }
+      if (this.parent) {
+        Private.scrollIfNeeded(this.parent.node, widget.node);
       }
     }
 
@@ -556,7 +558,6 @@ class ActiveNotebook extends NotebookRenderer {
       return;
     }
     this.activeCellIndex = i;
-    this.mode = document.activeElement === this.node ? 'command' : 'edit';
   }
 
   /**
@@ -573,10 +574,19 @@ class ActiveNotebook extends NotebookRenderer {
     }
     let cell = model.cells.get(i) as MarkdownCellModel;
     let widget = (this.layout as PanelLayout).childAt(i) as MarkdownCellWidget;
-    if (cell.type !== 'markdown' || !widget.rendered) {
+    if (cell.type === 'markdown') {
+      widget.rendered = false;
       return;
     }
-    if (widget.node.contains(event.target as HTMLElement)) {
+  }
+
+  /**
+   * Handle `focus` events for the widget.
+   */
+  private _evtFocus(event: FocusEvent): void {
+    if (event.target === this.node) {
+      this.mode = 'command';
+    } else {
       this.mode = 'edit';
     }
   }

+ 241 - 2
test/src/notebook/notebook/widget.spec.ts

@@ -19,6 +19,10 @@ import {
   Widget
 } from 'phosphor-widget';
 
+import {
+  simulate
+} from 'simulate-event';
+
 import {
   BaseCellWidget, CodeCellWidget, ICellModel, MarkdownCellWidget, RawCellWidget
 } from '../../../../lib/notebook/cells';
@@ -61,7 +65,7 @@ class LogNotebookRenderer extends NotebookRenderer {
   methods: string[] = [];
 
   protected onUpdateRequest(msg: Message): void {
-    super.onAfterAttach(msg);
+    super.onUpdateRequest(msg);
     this.methods.push('onUpdateRequest');
   }
 
@@ -114,7 +118,7 @@ class LogActiveNotebook extends ActiveNotebook {
   }
 
   protected onUpdateRequest(msg: Message): void {
-    super.onAfterAttach(msg);
+    super.onUpdateRequest(msg);
     this.methods.push('onUpdateRequest');
   }
 
@@ -660,34 +664,269 @@ describe('notebook/notebook/widget', () => {
 
     describe('#handleEvent()', () => {
 
+      let widget: LogActiveNotebook;
+
+      beforeEach((done) => {
+        widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.attach(document.body);
+        requestAnimationFrame(() => { done(); });
+      });
+
+      afterEach(() => {
+        widget.dispose();
+      });
+
       context('click', () => {
 
+        it('should set the active cell index', () => {
+          let child = widget.childAt(1);
+          simulate(child.node, 'click');
+          expect(widget.events.indexOf('click')).to.not.be(-1);
+          expect(widget.activeCellIndex).to.be(1);
+        });
+
+        it('should be a no-op if the model is read only', () => {
+          let child = widget.childAt(1);
+          widget.model.readOnly = true;
+          simulate(child.node, 'click');
+          expect(widget.events.indexOf('click')).to.not.be(-1);
+          expect(widget.activeCellIndex).to.be(0);
+        });
+
+        it('should be a no-op if not not a cell', () => {
+          simulate(widget.node, 'click');
+          expect(widget.events.indexOf('click')).to.not.be(-1);
+          expect(widget.activeCellIndex).to.be(0);
+        });
+
       });
 
       context('dblclick', () => {
 
+        it('should unrender a markdown cell', () => {
+          let cell = widget.model.createMarkdownCell();
+          widget.model.cells.add(cell);
+          let child = widget.childAt(widget.childCount() - 1) as MarkdownCellWidget;
+          expect(child.rendered).to.be(true);
+          simulate(child.node, 'dblclick');
+          expect(child.rendered).to.be(false);
+        });
+
+        it('should be a no-op if the model is read only', () => {
+          let cell = widget.model.createMarkdownCell();
+          widget.model.cells.add(cell);
+          widget.model.readOnly = true;
+          let child = widget.childAt(widget.childCount() - 1) as MarkdownCellWidget;
+          expect(child.rendered).to.be(true);
+          simulate(child.node, 'dblclick');
+          expect(child.rendered).to.be(true);
+        });
+
+      });
+
+      context('focus', () => {
+
+        it('should change to edit mode if a child cell takes focus', () => {
+          let child = widget.childAt(0);
+          simulate(child.editor.node, 'focus');
+          expect(widget.events.indexOf('focus')).to.not.be(-1);
+          expect(widget.mode).to.be('edit');
+        });
+
+        it('should change to command mode if the widget takes focus', () => {
+          let child = widget.childAt(0);
+          simulate(child.editor.node, 'focus');
+          expect(widget.events.indexOf('focus')).to.not.be(-1);
+          expect(widget.mode).to.be('edit');
+          widget.events = [];
+          simulate(widget.node, 'focus');
+          expect(widget.events.indexOf('focus')).to.not.be(-1);
+          expect(widget.mode).to.be('command');
+        });
+
       });
 
     });
 
     describe('#onAfterAttach()', () => {
 
+      it('should add event listeners', (done) => {
+        let widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.attach(document.body);
+        let child = widget.childAt(0);
+        requestAnimationFrame(() => {
+          expect(widget.methods.indexOf('onAfterAttach')).to.not.be(-1);
+          simulate(widget.node, 'click');
+          expect(widget.events.indexOf('click')).to.not.be(-1);
+          simulate(widget.node, 'dblclick');
+          expect(widget.events.indexOf('dblclick')).to.not.be(-1);
+          simulate(child.node, 'focus');
+          expect(widget.events.indexOf('focus')).to.not.be(-1);
+          widget.dispose();
+          done();
+        });
+      });
+
+      it('should post an update request', (done) => {
+        let widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.attach(document.body);
+        requestAnimationFrame(() => {
+          expect(widget.methods.indexOf('onAfterAttach')).to.not.be(-1);
+          requestAnimationFrame(() => {
+            expect(widget.methods.indexOf('onUpdateRequest')).to.not.be(-1);
+            widget.dispose();
+            done();
+          });
+        });
+      });
+
     });
 
     describe('#onBeforeDetach()', () => {
 
+      it('should remove event listeners', (done) => {
+        let widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.attach(document.body);
+        let child = widget.childAt(0);
+        requestAnimationFrame(() => {
+          widget.detach();
+          expect(widget.methods.indexOf('onBeforeDetach')).to.not.be(-1);
+          simulate(widget.node, 'click');
+          expect(widget.events.indexOf('click')).to.be(-1);
+          simulate(widget.node, 'dblclick');
+          expect(widget.events.indexOf('dblclick')).to.be(-1);
+          simulate(child.node, 'focus');
+          expect(widget.events.indexOf('focus')).to.be(-1);
+          widget.dispose();
+          done();
+        });
+      });
+
     });
 
     describe('#onUpdateRequest()', () => {
 
+      let widget: LogActiveNotebook;
+
+      beforeEach((done) => {
+        widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.attach(document.body);
+        requestAnimationFrame(() => {  done(); });
+      });
+
+      afterEach(() => {
+        widget.dispose();
+      });
+
+      it('should apply the command class if in command mode', () => {
+        expect(widget.methods.indexOf('onUpdateRequest')).to.not.be(-1);
+        expect(widget.hasClass('jp-mod-commandMode')).to.be(true);
+      });
+
+      it('should focus the widget if in command mode', () => {
+        expect(widget.node).to.be(document.activeElement);
+      });
+
+      it('should apply the edit class if in edit mode', (done) => {
+        widget.mode = 'edit';
+        requestAnimationFrame(() => {
+          expect(widget.hasClass('jp-mod-editMode')).to.be(true);
+          done();
+        });
+      });
+
+      it('should focus the cell if in edit mode', (done) => {
+        widget.mode = 'edit';
+        let cell = widget.childAt(widget.activeCellIndex);
+        requestAnimationFrame(() => {
+          expect(cell.node.contains(document.activeElement)).to.be(true);
+          done();
+        });
+      });
+
+      it('should unrender a markdown cell in edit mode', (done) => {
+        let cell = widget.model.createMarkdownCell();
+        widget.model.cells.add(cell);
+        let child = widget.childAt(widget.childCount() - 1) as MarkdownCellWidget;
+        expect(child.rendered).to.be(true);
+        widget.activeCellIndex = widget.childCount() - 1;
+        widget.mode = 'edit';
+        requestAnimationFrame(() => {
+          expect(child.rendered).to.be(false);
+          done();
+        });
+      });
+
+      it('should add the active class to the active widget', () => {
+        let cell = widget.childAt(widget.activeCellIndex);
+        expect(cell.hasClass('jp-mod-active')).to.be(true);
+      });
+
+      it('should set the selected class on the selected widgets', (done) => {
+        widget.select(widget.childAt(1));
+        requestAnimationFrame(() => {
+          for (let i = 0; i < 2; i++) {
+            let cell = widget.childAt(i);
+            expect(cell.hasClass('jp-mod-selected')).to.be(true);
+            done();
+          }
+        });
+      });
+
+      it('should add the multi select class if there is more than one widget', (done) => {
+        widget.select(widget.childAt(1));
+        expect(widget.hasClass('jp-mod-multSelected')).to.be(false);
+        requestAnimationFrame(() => {
+          expect(widget.hasClass('jp-mod-multSelected')).to.be(false);
+          done();
+        });
+      });
+
     });
 
     describe('#initializeCellWidget()', () => {
 
+      it('should add the `jp-Notebook-cell` class', () => {
+        let widget = createActiveWidget();
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        expect(widget.methods.indexOf('initializeCellWidget')).to.not.be(-1);
+      });
+
+      it("should connect edge requested signals from the widget's editor", () => {
+        let widget = createActiveWidget();
+        let child = widget.childAt(widget.activeCellIndex);
+        child.editor.edgeRequested.emit('top');
+        expect(widget.methods.indexOf('onEdgeRequest')).to.not.be(-1);
+      });
+
     });
 
     describe('#onEdgeRequest()', () => {
 
+      it('should activate the previous cell if top is requested', () => {
+        let widget = createActiveWidget()
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        widget.activeCellIndex = 1;
+        let child = widget.childAt(widget.activeCellIndex);
+        child.editor.edgeRequested.emit('top');
+        expect(widget.methods.indexOf('onEdgeRequest')).to.not.be(-1);
+        expect(widget.activeCellIndex).to.be(0);
+      });
+
+      it('should activate the next cell if bottom is requested', ()  => {
+        let widget = createActiveWidget()
+        widget.model.fromJSON(DEFAULT_CONTENT);
+        let child = widget.childAt(widget.activeCellIndex);
+        child.editor.edgeRequested.emit('bottom');
+        expect(widget.methods.indexOf('onEdgeRequest')).to.not.be(-1);
+        expect(widget.activeCellIndex).to.be(1);
+      });
+
     });
 
   });