widget.spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import expect = require('expect.js');
  4. import {
  5. Kernel
  6. } from 'jupyter-js-services';
  7. import {
  8. Message, sendMessage
  9. } from 'phosphor/lib/core/messaging';
  10. import {
  11. Widget, WidgetMessage
  12. } from 'phosphor/lib/ui/widget';
  13. import {
  14. RenderMime
  15. } from '../../../../lib/rendermime';
  16. import {
  17. BaseCellWidget, CellModel, InputAreaWidget, ICellModel,
  18. CodeCellWidget, CodeCellModel, MarkdownCellWidget,
  19. RawCellWidget
  20. } from '../../../../lib/notebook/cells';
  21. import {
  22. CodeMirrorCellEditorWidget
  23. } from '../../../../lib/notebook/codemirror/cells/editor';
  24. import {
  25. CodeMirrorCodeCellWidgetRenderer
  26. } from '../../../../lib/notebook/codemirror/cells/widget';
  27. import {
  28. CodeMirrorNotebookRenderer
  29. } from '../../../../lib/notebook/codemirror/notebook/widget';
  30. import {
  31. OutputAreaWidget
  32. } from '../../../../lib/notebook/output-area';
  33. import {
  34. ICellEditorWidget
  35. } from '../../../../lib/notebook/cells/editor';
  36. import {
  37. defaultRenderMime
  38. } from '../../utils';
  39. const INPUT_CLASS = 'jp-InputArea';
  40. const RENDERED_CLASS = 'jp-mod-rendered';
  41. const PROMPT_CLASS = 'jp-Cell-prompt';
  42. const rendermime = defaultRenderMime();
  43. class LogBaseCell extends BaseCellWidget {
  44. methods: string[] = [];
  45. constructor() {
  46. super({
  47. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  48. });
  49. }
  50. protected onAfterAttach(msg: Message): void {
  51. super.onAfterAttach(msg);
  52. this.methods.push('onAfterAttach');
  53. }
  54. protected onActivateRequest(msg: Message): void {
  55. super.onActivateRequest(msg);
  56. this.methods.push('onActivateRequest');
  57. }
  58. protected onUpdateRequest(msg: Message): void {
  59. super.onUpdateRequest(msg);
  60. this.methods.push('onUpdateRequest');
  61. }
  62. protected onMetadataChanged(model: ICellModel, args: any): void {
  63. super.onMetadataChanged(model, args);
  64. this.methods.push('onMetadataChanged');
  65. }
  66. protected onModelChanged(oldValue: ICellModel, newValue: ICellModel): void {
  67. super.onModelChanged(oldValue, newValue);
  68. this.methods.push('onModelChanged');
  69. }
  70. protected onModelStateChanged(model: ICellModel, args: any): void {
  71. super.onModelStateChanged(model, args);
  72. this.methods.push('onModelStateChanged');
  73. }
  74. }
  75. class LogCodeCell extends CodeCellWidget {
  76. methods: string[] = [];
  77. protected onUpdateRequest(msg: Message): void {
  78. super.onAfterAttach(msg);
  79. this.methods.push('onUpdateRequest');
  80. }
  81. protected onMetadataChanged(model: ICellModel, args: any): void {
  82. super.onMetadataChanged(model, args);
  83. this.methods.push('onMetadataChanged');
  84. }
  85. protected onModelChanged(oldValue: ICellModel, newValue: ICellModel): void {
  86. super.onModelChanged(oldValue, newValue);
  87. this.methods.push('onModelChanged');
  88. }
  89. protected onModelStateChanged(model: ICellModel, args: any): void {
  90. super.onModelStateChanged(model, args);
  91. this.methods.push('onModelStateChanged');
  92. }
  93. }
  94. class LogMarkdownCell extends MarkdownCellWidget {
  95. methods: string[] = [];
  96. protected onUpdateRequest(msg: Message): void {
  97. super.onAfterAttach(msg);
  98. this.methods.push('onUpdateRequest');
  99. }
  100. }
  101. class LogRenderer extends CodeMirrorCodeCellWidgetRenderer {
  102. methods: string[] = [];
  103. createCellEditor(): ICellEditorWidget {
  104. this.methods.push('createCellEditor');
  105. return super.createCellEditor();
  106. }
  107. createInputArea(editor: ICellEditorWidget): InputAreaWidget {
  108. this.methods.push('createInputArea');
  109. return super.createInputArea(editor);
  110. }
  111. createOutputArea(rendermime: RenderMime): OutputAreaWidget {
  112. this.methods.push('createOutputArea');
  113. return super.createOutputArea(rendermime);
  114. }
  115. }
  116. describe('notebook/cells/widget', () => {
  117. describe('BaseCellWidget', () => {
  118. describe('#constructor()', () => {
  119. it('should create a base cell widget', () => {
  120. let widget = new BaseCellWidget({
  121. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  122. });
  123. expect(widget).to.be.a(BaseCellWidget);
  124. });
  125. it('should accept a custom renderer', () => {
  126. let renderer = new LogRenderer();
  127. expect(renderer.methods).to.not.contain('createCellEditor');
  128. expect(renderer.methods).to.not.contain('createInputArea');
  129. let widget = new BaseCellWidget({ renderer });
  130. expect(widget).to.be.a(BaseCellWidget);
  131. expect(renderer.methods).to.contain('createCellEditor');
  132. expect(renderer.methods).to.contain('createInputArea');
  133. });
  134. });
  135. describe('#model', () => {
  136. it('should be settable', () => {
  137. let model = new CellModel();
  138. let widget = new BaseCellWidget({
  139. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  140. });
  141. expect(widget.model).to.be(null);
  142. widget.model = model;
  143. expect(widget.model).to.be(model);
  144. widget.model = new CellModel();
  145. expect(widget.model).not.to.be(model);
  146. });
  147. });
  148. describe('#modelChanged', () => {
  149. it('should emit a signal when the model changes', () => {
  150. let widget = new BaseCellWidget({
  151. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  152. });
  153. let called = false;
  154. widget.modelChanged.connect(() => { called = true; });
  155. expect(called).to.be(false);
  156. widget.model = new CellModel();
  157. expect(called).to.be(true);
  158. });
  159. it('should not emit a signal when the model has not changed', () => {
  160. let widget = new BaseCellWidget({
  161. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  162. });
  163. let model = new CellModel();
  164. let called = 0;
  165. widget.modelChanged.connect(() => { called++; });
  166. expect(called).to.be(0);
  167. widget.model = model;
  168. expect(called).to.be(1);
  169. widget.model = model;
  170. expect(called).to.be(1);
  171. });
  172. });
  173. describe('#editor', () => {
  174. it('should be a cell editor widget', () => {
  175. let widget = new BaseCellWidget({
  176. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  177. });
  178. expect(widget.editor).to.be.a(CodeMirrorCellEditorWidget);
  179. });
  180. });
  181. describe('#mimetype', () => {
  182. it('should be a string', () => {
  183. let widget = new BaseCellWidget({
  184. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  185. });
  186. expect(typeof widget.mimetype).to.be('string');
  187. });
  188. it('should default to text/plain', () => {
  189. let widget = new BaseCellWidget({
  190. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  191. });
  192. expect(widget.mimetype).to.be('text/plain');
  193. });
  194. it('should supporting being set to other types', () => {
  195. let widget = new BaseCellWidget({
  196. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  197. });
  198. widget.mimetype = 'test/test';
  199. expect(widget.mimetype).to.be('test/test');
  200. });
  201. it('should be safe to set multiple times', () => {
  202. let widget = new BaseCellWidget({
  203. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  204. });
  205. widget.mimetype = 'test/test';
  206. widget.mimetype = 'test/test';
  207. expect(widget.mimetype).to.be('test/test');
  208. });
  209. it('should not allow being set to empty or null strings', () => {
  210. let widget = new BaseCellWidget({
  211. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  212. });
  213. widget.mimetype = null;
  214. expect(widget.mimetype).to.be('text/plain');
  215. widget.mimetype = '';
  216. expect(widget.mimetype).to.be('text/plain');
  217. });
  218. });
  219. describe('#readOnly', () => {
  220. it('should be a boolean', () => {
  221. let widget = new BaseCellWidget({
  222. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  223. });
  224. expect(typeof widget.readOnly).to.be('boolean');
  225. });
  226. it('should default to false', () => {
  227. let widget = new BaseCellWidget({
  228. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  229. });
  230. expect(widget.readOnly).to.be(false);
  231. });
  232. it('should be settable', () => {
  233. let widget = new BaseCellWidget({
  234. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  235. });
  236. widget.readOnly = true;
  237. expect(widget.readOnly).to.be(true);
  238. });
  239. it('should ignore being set to the same value', (done) => {
  240. let widget = new LogBaseCell();
  241. widget.readOnly = true;
  242. widget.readOnly = true;
  243. requestAnimationFrame(() => {
  244. expect(widget.methods).to.eql(['onUpdateRequest']);
  245. done();
  246. });
  247. });
  248. });
  249. describe('#trusted', () => {
  250. it('should be a boolean', () => {
  251. let widget = new BaseCellWidget({
  252. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  253. });
  254. expect(typeof widget.trusted).to.be('boolean');
  255. });
  256. it('should default to false', () => {
  257. let widget = new BaseCellWidget({
  258. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  259. });
  260. expect(widget.trusted).to.be(false);
  261. });
  262. it('should be settable', () => {
  263. let widget = new BaseCellWidget({
  264. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  265. });
  266. widget.model = new CellModel();
  267. widget.trusted = true;
  268. expect(widget.trusted).to.be(true);
  269. });
  270. it('should do nothing if there is no model', () => {
  271. let widget = new BaseCellWidget({
  272. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  273. });
  274. expect(widget.trusted).to.be(false);
  275. widget.trusted = true;
  276. expect(widget.trusted).to.be(false);
  277. });
  278. });
  279. describe('#onActivateRequest()', () => {
  280. it('should focus the cell editor', (done) => {
  281. let widget = new LogBaseCell();
  282. Widget.attach(widget, document.body);
  283. expect(widget.editor.hasFocus()).to.be(false);
  284. widget.activate();
  285. requestAnimationFrame(() => {
  286. expect(widget.methods).to.contain('onActivateRequest');
  287. requestAnimationFrame(() => {
  288. expect(widget.editor.hasFocus()).to.be(true);
  289. widget.dispose();
  290. done();
  291. });
  292. });
  293. });
  294. });
  295. describe('#setPrompt()', () => {
  296. it('should not throw an error (full test in input area)', () => {
  297. let widget = new BaseCellWidget({
  298. renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer
  299. });
  300. expect(() => { widget.setPrompt(void 0); }).to.not.throwError();
  301. expect(() => { widget.setPrompt(null); }).to.not.throwError();
  302. expect(() => { widget.setPrompt(''); }).to.not.throwError();
  303. expect(() => { widget.setPrompt('null'); }).to.not.throwError();
  304. expect(() => { widget.setPrompt('test'); }).to.not.throwError();
  305. });
  306. });
  307. describe('#toggleInput()', () => {
  308. it('should toggle whether the input is shown', () => {
  309. let widget = new BaseCellWidget({renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer});
  310. let input = widget.node.getElementsByClassName(INPUT_CLASS)[0];
  311. Widget.attach(widget, document.body);
  312. expect(window.getComputedStyle(input).display).to.not.be('none');
  313. widget.toggleInput(false);
  314. expect(window.getComputedStyle(input).display).to.be('none');
  315. widget.toggleInput(true);
  316. expect(window.getComputedStyle(input).display).to.not.be('none');
  317. });
  318. });
  319. describe('#dispose()', () => {
  320. it('should dispose of the resources held by the widget', () => {
  321. let widget = new BaseCellWidget({renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer});
  322. widget.dispose();
  323. expect(widget.isDisposed).to.be(true);
  324. });
  325. it('should be safe to call multiple times', () => {
  326. let widget = new BaseCellWidget({renderer: CodeMirrorCodeCellWidgetRenderer.defaultRenderer});
  327. widget.dispose();
  328. widget.dispose();
  329. expect(widget.isDisposed).to.be(true);
  330. });
  331. });
  332. describe('#onAfterAttach()', () => {
  333. it('should run when widget is attached', () => {
  334. let widget = new LogBaseCell();
  335. expect(widget.methods).to.not.contain('onAfterAttach');
  336. Widget.attach(widget, document.body);
  337. expect(widget.methods).to.contain('onAfterAttach');
  338. widget.dispose();
  339. });
  340. });
  341. describe('#onUpdateRequest()', () => {
  342. it('should update the widget', () => {
  343. let widget = new LogBaseCell();
  344. expect(widget.methods).to.not.contain('onUpdateRequest');
  345. sendMessage(widget, WidgetMessage.UpdateRequest);
  346. expect(widget.methods).to.contain('onUpdateRequest');
  347. });
  348. });
  349. describe('#onModelStateChanged()', () => {
  350. it('should fire when model state changes', () => {
  351. let method = 'onModelStateChanged';
  352. let widget = new LogBaseCell();
  353. widget.model = new CellModel();
  354. expect(widget.methods).to.not.contain(method);
  355. widget.model.source = 'foo';
  356. expect(widget.methods).to.contain(method);
  357. });
  358. });
  359. describe('#onMetadataChanged()', () => {
  360. it('should fire when model metadata changes', () => {
  361. let method = 'onMetadataChanged';
  362. let widget = new LogBaseCell();
  363. widget.model = new CellModel();
  364. expect(widget.methods).to.not.contain(method);
  365. widget.model.metadataChanged.emit({
  366. name: 'foo',
  367. oldValue: 'bar',
  368. newValue: 'baz'
  369. });
  370. expect(widget.methods).to.contain(method);
  371. });
  372. });
  373. describe('#onModelChanged()', () => {
  374. it('should fire when the model changes', () => {
  375. let method = 'onModelChanged';
  376. let widget = new LogBaseCell();
  377. expect(widget.methods).to.not.contain(method);
  378. widget.model = new CellModel();
  379. expect(widget.methods).to.contain(method);
  380. });
  381. });
  382. describe('.Renderer', () => {
  383. describe('#constructor()', () => {
  384. it('should create a renderer', () => {
  385. let renderer = new CodeMirrorCodeCellWidgetRenderer();
  386. expect(renderer).to.be.a(BaseCellWidget.Renderer);
  387. });
  388. });
  389. describe('#createCellEditor()', () => {
  390. it('should create a cell editor widget', () => {
  391. let renderer = new CodeMirrorCodeCellWidgetRenderer();
  392. let editor = renderer.createCellEditor();
  393. expect(editor).to.be.a(CodeMirrorCellEditorWidget);
  394. });
  395. });
  396. describe('#createInputArea()', () => {
  397. it('should create an input area widget', () => {
  398. let renderer = new CodeMirrorCodeCellWidgetRenderer();
  399. let editor = renderer.createCellEditor();
  400. let input = renderer.createInputArea(editor);
  401. expect(input).to.be.an(InputAreaWidget);
  402. });
  403. });
  404. describe('#defaultRenderer', () => {
  405. it('should be a renderer', () => {
  406. let defaultRenderer = CodeMirrorCodeCellWidgetRenderer.defaultRenderer;
  407. expect(defaultRenderer).to.be.a(BaseCellWidget.Renderer);
  408. });
  409. });
  410. });
  411. });
  412. describe('CodeCellWidget', () => {
  413. describe('#constructor()', () => {
  414. it('should create a code cell widget', () => {
  415. let widget = new CodeCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  416. expect(widget).to.be.a(CodeCellWidget);
  417. });
  418. it('should accept a custom renderer', () => {
  419. let renderer = new LogRenderer();
  420. expect(renderer.methods).to.not.contain('createCellEditor');
  421. expect(renderer.methods).to.not.contain('createInputArea');
  422. expect(renderer.methods).to.not.contain('createOutputArea');
  423. let widget = new CodeCellWidget({ renderer, rendermime });
  424. widget.model = new CodeCellModel();
  425. expect(widget).to.be.a(CodeCellWidget);
  426. expect(renderer.methods).to.contain('createCellEditor');
  427. expect(renderer.methods).to.contain('createInputArea');
  428. expect(renderer.methods).to.contain('createOutputArea');
  429. });
  430. });
  431. describe('#dispose()', () => {
  432. it('should dispose of the resources held by the widget', () => {
  433. let widget = new CodeCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  434. widget.dispose();
  435. expect(widget.isDisposed).to.be(true);
  436. });
  437. it('should be safe to call multiple times', () => {
  438. let widget = new CodeCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  439. widget.dispose();
  440. widget.dispose();
  441. expect(widget.isDisposed).to.be(true);
  442. });
  443. });
  444. describe('#execute()', () => {
  445. it('should fulfill a promise if there is no code to execute', (done) => {
  446. let widget = new CodeCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  447. Kernel.startNew().then(kernel => {
  448. widget.model = new CodeCellModel();
  449. return widget.execute(kernel).then(() => {
  450. kernel.shutdown();
  451. done();
  452. });
  453. }).catch(done);
  454. });
  455. it('should fulfill a promise if there is code to execute', (done) => {
  456. let widget = new CodeCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  457. Kernel.startNew().then(kernel => {
  458. widget.model = new CodeCellModel();
  459. widget.model.source = 'foo';
  460. let originalCount = (widget.model).executionCount;
  461. return widget.execute(kernel).then(() => {
  462. let executionCount = (widget.model).executionCount;
  463. expect(executionCount).to.not.equal(originalCount);
  464. kernel.shutdown();
  465. done();
  466. });
  467. }).catch(done);
  468. });
  469. });
  470. describe('#onUpdateRequest()', () => {
  471. it('should update the widget', () => {
  472. let widget = new LogCodeCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  473. expect(widget.methods).to.not.contain('onUpdateRequest');
  474. sendMessage(widget, WidgetMessage.UpdateRequest);
  475. expect(widget.methods).to.contain('onUpdateRequest');
  476. });
  477. });
  478. describe('#onModelChanged()', () => {
  479. it('should fire when the model changes', () => {
  480. let method = 'onModelChanged';
  481. let widget = new LogCodeCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  482. expect(widget.methods).to.not.contain(method);
  483. widget.model = new CodeCellModel();
  484. expect(widget.methods).to.contain(method);
  485. });
  486. });
  487. describe('#onModelStateChanged()', () => {
  488. it('should fire when model state changes', () => {
  489. let method = 'onModelStateChanged';
  490. let widget = new LogCodeCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  491. widget.model = new CodeCellModel();
  492. expect(widget.methods).to.not.contain(method);
  493. widget.model.source = 'foo';
  494. expect(widget.methods).to.contain(method);
  495. });
  496. });
  497. describe('#onMetadataChanged()', () => {
  498. it('should fire when model metadata changes', () => {
  499. let method = 'onMetadataChanged';
  500. let widget = new LogCodeCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultCodeCellRenderer });
  501. widget.model = new CodeCellModel();
  502. expect(widget.methods).to.not.contain(method);
  503. widget.model.metadataChanged.emit({
  504. name: 'foo',
  505. oldValue: 'bar',
  506. newValue: 'baz'
  507. });
  508. expect(widget.methods).to.contain(method);
  509. });
  510. });
  511. describe('.Renderer', () => {
  512. describe('#constructor()', () => {
  513. it('should create a renderer', () => {
  514. let renderer = new CodeMirrorCodeCellWidgetRenderer();
  515. expect(renderer).to.be.a(CodeCellWidget.Renderer);
  516. });
  517. });
  518. describe('#createOutputArea()', () => {
  519. it('should create an output area widget', () => {
  520. let renderer = new CodeMirrorCodeCellWidgetRenderer();
  521. let output = renderer.createOutputArea(rendermime);
  522. expect(output).to.be.an(OutputAreaWidget);
  523. });
  524. });
  525. describe('#defaultRenderer', () => {
  526. it('should be a renderer', () => {
  527. let defaultRenderer = CodeMirrorNotebookRenderer.defaultCodeCellRenderer;
  528. expect(defaultRenderer).to.be.a(CodeCellWidget.Renderer);
  529. });
  530. });
  531. });
  532. });
  533. describe('MarkdownCellWidget', () => {
  534. describe('#constructor()', () => {
  535. it('should create a markdown cell widget', () => {
  536. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  537. expect(widget).to.be.a(MarkdownCellWidget);
  538. });
  539. it('should accept a custom renderer', () => {
  540. let renderer = new LogRenderer();
  541. expect(renderer.methods).to.not.contain('createCellEditor');
  542. expect(renderer.methods).to.not.contain('createInputArea');
  543. let widget = new MarkdownCellWidget({ renderer, rendermime });
  544. expect(widget).to.be.a(MarkdownCellWidget);
  545. expect(renderer.methods).to.contain('createCellEditor');
  546. expect(renderer.methods).to.contain('createInputArea');
  547. });
  548. it('should set the default mimetype to text/x-ipythongfm', () => {
  549. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  550. expect(widget.mimetype).to.be('text/x-ipythongfm');
  551. });
  552. });
  553. describe('#rendered', () => {
  554. it('should default to true', (done) => {
  555. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  556. Widget.attach(widget, document.body);
  557. expect(widget.rendered).to.be(true);
  558. requestAnimationFrame(() => {
  559. expect(widget.node.classList.contains(RENDERED_CLASS)).to.be(true);
  560. widget.dispose();
  561. done();
  562. });
  563. });
  564. it('should unrender the widget', (done) => {
  565. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  566. Widget.attach(widget, document.body);
  567. widget.rendered = false;
  568. requestAnimationFrame(() => {
  569. expect(widget.node.classList.contains(RENDERED_CLASS)).to.be(false);
  570. widget.dispose();
  571. done();
  572. });
  573. });
  574. it('should ignore being set to the same value', (done) => {
  575. let widget = new LogMarkdownCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  576. Widget.attach(widget, document.body);
  577. widget.rendered = false;
  578. widget.rendered = false;
  579. requestAnimationFrame(() => {
  580. let updates = widget.methods.filter((method) => {
  581. return method === 'onUpdateRequest';
  582. });
  583. expect(updates).to.have.length(1);
  584. done();
  585. });
  586. });
  587. });
  588. describe('#dispose()', () => {
  589. it('should dispose of the resources held by the widget', () => {
  590. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  591. widget.dispose();
  592. expect(widget.isDisposed).to.be(true);
  593. });
  594. it('should be safe to call multiple times', () => {
  595. let widget = new MarkdownCellWidget({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  596. widget.dispose();
  597. widget.dispose();
  598. expect(widget.isDisposed).to.be(true);
  599. });
  600. });
  601. describe('#onUpdateRequest()', () => {
  602. it('should update the widget', () => {
  603. let widget = new LogMarkdownCell({ rendermime, renderer: CodeMirrorNotebookRenderer.defaultMarkdownCellRenderer });
  604. expect(widget.methods).to.not.contain('onUpdateRequest');
  605. sendMessage(widget, WidgetMessage.UpdateRequest);
  606. expect(widget.methods).to.contain('onUpdateRequest');
  607. });
  608. });
  609. });
  610. describe('RawCellWidget', () => {
  611. describe('#constructor()', () => {
  612. it('should create a raw cell widget', () => {
  613. let widget = new RawCellWidget({renderer: CodeMirrorNotebookRenderer.defaultRawCellRenderer});
  614. expect(widget).to.be.a(RawCellWidget);
  615. });
  616. });
  617. });
  618. describe('InputAreaWidget', () => {
  619. describe('#constructor()', () => {
  620. it('should create an input area widget', () => {
  621. let editor = new CodeMirrorCellEditorWidget(new CellModel());
  622. let widget = new InputAreaWidget(editor);
  623. expect(widget).to.be.an(InputAreaWidget);
  624. });
  625. });
  626. describe('#setPrompt()', () => {
  627. it('should change the value of the input prompt', () => {
  628. let editor = new CodeMirrorCellEditorWidget(new CellModel());
  629. let widget = new InputAreaWidget(editor);
  630. let prompt = widget.node.querySelector(`.${PROMPT_CLASS}`);
  631. expect(prompt.textContent).to.be.empty();
  632. widget.setPrompt('foo');
  633. expect(prompt.textContent).to.contain('foo');
  634. });
  635. it('should treat the string value "null" as special', () => {
  636. let editor = new CodeMirrorCellEditorWidget(new CellModel());
  637. let widget = new InputAreaWidget(editor);
  638. let prompt = widget.node.querySelector(`.${PROMPT_CLASS}`);
  639. expect(prompt.textContent).to.be.empty();
  640. widget.setPrompt('null');
  641. expect(prompt.textContent).to.not.contain('null');
  642. });
  643. });
  644. });
  645. });