model.spec.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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, nbformat
  6. } from '@jupyterlab/services';
  7. import {
  8. deepEqual
  9. } from 'phosphor/lib/algorithm/json';
  10. import {
  11. OutputAreaModel
  12. } from '../../../../lib/notebook/output-area/model';
  13. /**
  14. * The default outputs used for testing.
  15. */
  16. export
  17. const DEFAULT_OUTPUTS: nbformat.IOutput[] = [
  18. {
  19. name: 'stdout',
  20. output_type: 'stream',
  21. text: [
  22. 'hello world\n',
  23. '0\n',
  24. '1\n',
  25. '2\n'
  26. ]
  27. },
  28. {
  29. name: 'stderr',
  30. output_type: 'stream',
  31. text: [
  32. 'output to stderr\n'
  33. ]
  34. },
  35. {
  36. name: 'stderr',
  37. output_type: 'stream',
  38. text: [
  39. 'output to stderr2\n'
  40. ]
  41. },
  42. {
  43. output_type: 'execute_result',
  44. execution_count: 1,
  45. data: { 'text/plain': 'foo' },
  46. metadata: {}
  47. },
  48. {
  49. output_type: 'display_data',
  50. data: { 'text/plain': 'hello, world' },
  51. metadata: {}
  52. },
  53. {
  54. output_type: 'error',
  55. ename: 'foo',
  56. evalue: 'bar',
  57. traceback: ['fizz', 'buzz']
  58. }
  59. ];
  60. describe('notebook/output-area/model', () => {
  61. describe('OutputAreaModel', () => {
  62. describe('#constructor()', () => {
  63. it('should create an output area model', () => {
  64. let model = new OutputAreaModel();
  65. expect(model).to.be.an(OutputAreaModel);
  66. });
  67. });
  68. describe('#changed', () => {
  69. it('should be emitted when the model changes', () => {
  70. let model = new OutputAreaModel();
  71. let called = false;
  72. model.changed.connect((sender, args) => {
  73. expect(sender).to.be(model);
  74. expect(args.type).to.be('add');
  75. expect(args.oldIndex).to.be(-1);
  76. expect(args.newIndex).to.be(0);
  77. expect(args.oldValues.length).to.be(0);
  78. expect(deepEqual(args.newValues[0] as nbformat.IOutput, DEFAULT_OUTPUTS[0]));
  79. called = true;
  80. });
  81. model.add(DEFAULT_OUTPUTS[0]);
  82. expect(called).to.be(true);
  83. });
  84. });
  85. describe('#length', () => {
  86. it('should get the length of the items in the model', () => {
  87. let model = new OutputAreaModel();
  88. expect(model.length).to.be(0);
  89. model.add(DEFAULT_OUTPUTS[0]);
  90. expect(model.length).to.be(1);
  91. });
  92. });
  93. describe('#isDisposed', () => {
  94. it('should test whether the model is disposed', () => {
  95. let model = new OutputAreaModel();
  96. expect(model.isDisposed).to.be(false);
  97. model.dispose();
  98. expect(model.isDisposed).to.be(true);
  99. });
  100. });
  101. describe('#dispose()', () => {
  102. it('should dispose of the resources used by the model', () => {
  103. let model = new OutputAreaModel();
  104. model.add(DEFAULT_OUTPUTS[0]);
  105. model.dispose();
  106. expect(model.isDisposed).to.be(true);
  107. expect(model.length).to.be(0);
  108. });
  109. it('should be safe to call more than once', () => {
  110. let model = new OutputAreaModel();
  111. model.dispose();
  112. model.dispose();
  113. expect(model.isDisposed).to.be(true);
  114. });
  115. });
  116. describe('#get()', () => {
  117. it('should get the item at the specified index', () => {
  118. let model = new OutputAreaModel();
  119. model.add(DEFAULT_OUTPUTS[0]);
  120. let output = model.get(0);
  121. expect(output).to.not.be(DEFAULT_OUTPUTS[0]);
  122. expect(output.output_type).to.be(DEFAULT_OUTPUTS[0].output_type);
  123. });
  124. it('should return `undefined` if out of range', () => {
  125. let model = new OutputAreaModel();
  126. model.add(DEFAULT_OUTPUTS[0]);
  127. expect(model.get(1)).to.be(void 0);
  128. });
  129. });
  130. describe('#add()', () => {
  131. it('should add an output', () => {
  132. let model = new OutputAreaModel();
  133. model.add(DEFAULT_OUTPUTS[0]);
  134. expect(model.length).to.be(1);
  135. });
  136. it('should consolidate consecutive stream outputs of the same kind', () => {
  137. let model = new OutputAreaModel();
  138. model.add(DEFAULT_OUTPUTS[0]);
  139. model.add(DEFAULT_OUTPUTS[1]);
  140. expect(model.length).to.be(2);
  141. model.add(DEFAULT_OUTPUTS[2]);
  142. expect(model.length).to.be(2);
  143. });
  144. });
  145. describe('#clear()', () => {
  146. it('should clear all of the output', () => {
  147. let model = new OutputAreaModel();
  148. for (let output of DEFAULT_OUTPUTS) {
  149. model.add(output);
  150. }
  151. model.clear();
  152. expect(model.length).to.be(0);
  153. });
  154. it('should wait for next add if requested', () => {
  155. let model = new OutputAreaModel();
  156. model.add(DEFAULT_OUTPUTS[0]);
  157. model.clear(true);
  158. expect(model.length).to.be(1);
  159. model.add(DEFAULT_OUTPUTS[1]);
  160. expect(model.length).to.be(1);
  161. });
  162. });
  163. describe('#execute()', () => {
  164. let kernel: Kernel.IKernel;
  165. beforeEach((done) => {
  166. Kernel.startNew().then(k => {
  167. kernel = k;
  168. return kernel.ready();
  169. }).then(() => {
  170. done();
  171. }).catch(done);
  172. });
  173. it('should execute code on a kernel and send outputs to the model', (done) => {
  174. let model = new OutputAreaModel();
  175. expect(model.length).to.be(0);
  176. model.execute('print("hello")', kernel).then(reply => {
  177. expect(reply.content.execution_count).to.be(1);
  178. expect(reply.content.status).to.be('ok');
  179. expect(model.length).to.be(1);
  180. kernel.shutdown();
  181. done();
  182. }).catch(done);
  183. });
  184. it('should clear existing outputs', (done) => {
  185. let model = new OutputAreaModel();
  186. for (let output of DEFAULT_OUTPUTS) {
  187. model.add(output);
  188. }
  189. return model.execute('print("hello")', kernel).then(reply => {
  190. expect(reply.content.execution_count).to.be(1);
  191. expect(model.length).to.be(1);
  192. kernel.shutdown();
  193. done();
  194. }).catch(done);
  195. });
  196. });
  197. describe('#addMimeData', () => {
  198. it('should add a mime type to an output data bundle', () => {
  199. let model = new OutputAreaModel();
  200. model.add({
  201. output_type: 'display_data',
  202. data: { 'text/plain': 'hello, world' },
  203. metadata: {}
  204. });
  205. let output = model.get(0) as nbformat.IDisplayData;
  206. model.addMimeData(output, 'application/json', { 'foo': 1 });
  207. output = model.get(0) as nbformat.IDisplayData;
  208. expect((output.data['application/json'] as any)['foo']).to.be(1);
  209. });
  210. it('should refuse to add to an output not contained in the model', () => {
  211. let model = new OutputAreaModel();
  212. let output: nbformat.IDisplayData = {
  213. output_type: 'display_data',
  214. data: { },
  215. metadata: {}
  216. };
  217. expect(() => { model.addMimeData(output, 'text/plain', 'foo'); }).to.throwError();
  218. });
  219. it('should refuse to add an existing mime type', () => {
  220. let model = new OutputAreaModel();
  221. model.add({
  222. output_type: 'display_data',
  223. data: { 'text/plain': 'hello, world' },
  224. metadata: {}
  225. });
  226. let output = model.get(0) as nbformat.IDisplayData;
  227. model.addMimeData(output, 'text/plain', 'foo');
  228. output = model.get(0) as nbformat.IDisplayData;
  229. expect(output.data['text/plain']).to.be('hello, world');
  230. });
  231. it('should refuse to add an invalid mime type/value pair', () => {
  232. let model = new OutputAreaModel();
  233. model.add({
  234. output_type: 'display_data',
  235. data: { 'text/plain': 'hello, world' },
  236. metadata: {}
  237. });
  238. let output = model.get(0) as nbformat.IDisplayData;
  239. model.addMimeData(output, 'application/json', 'foo');
  240. output = model.get(0) as nbformat.IDisplayData;
  241. expect(output.data['application/json']).to.be(void 0);
  242. });
  243. });
  244. });
  245. });