default.spec.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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. Message
  6. } from '@phosphor/messaging';
  7. import {
  8. PanelLayout, Widget
  9. } from '@phosphor/widgets';
  10. import {
  11. ABCWidgetFactory, Base64ModelFactory, DocumentModel,
  12. DocumentRegistry, TextModelFactory, Context,
  13. MimeDocument, MimeDocumentFactory
  14. } from '@jupyterlab/docregistry';
  15. import {
  16. createFileContext, defaultRenderMime
  17. } from '../utils';
  18. const RENDERMIME = defaultRenderMime();
  19. class LogRenderer extends MimeDocument {
  20. methods: string[] = [];
  21. protected onAfterAttach(msg: Message): void {
  22. super.onAfterAttach(msg);
  23. this.methods.push('onAfterAttach');
  24. }
  25. protected onUpdateRequest(msg: Message): void {
  26. super.onUpdateRequest(msg);
  27. this.methods.push('onUpdateRequest');
  28. }
  29. }
  30. class DocWidget extends Widget implements DocumentRegistry.IReadyWidget {
  31. get ready(): Promise<void> {
  32. return Promise.resolve(undefined);
  33. }
  34. }
  35. class WidgetFactory extends ABCWidgetFactory<DocumentRegistry.IReadyWidget, DocumentRegistry.IModel> {
  36. protected createNewWidget(context: DocumentRegistry.Context): DocumentRegistry.IReadyWidget {
  37. let widget = new DocWidget();
  38. widget.addClass('WidgetFactory');
  39. return widget;
  40. }
  41. }
  42. function createFactory(): WidgetFactory {
  43. return new WidgetFactory({
  44. name: 'test',
  45. fileExtensions: ['.txt']
  46. });
  47. }
  48. describe('docmanager/default', () => {
  49. let context: Context<DocumentRegistry.IModel>;
  50. beforeEach(() => {
  51. context = createFileContext();
  52. });
  53. afterEach(() => {
  54. context.dispose();
  55. });
  56. describe('ABCWidgetFactory', () => {
  57. describe('#fileExtensions', () => {
  58. it('should be the value passed in', () => {
  59. let factory = new WidgetFactory({
  60. name: 'test',
  61. fileExtensions: ['.txt'],
  62. });
  63. expect(factory.fileExtensions).to.eql(['.txt']);
  64. });
  65. });
  66. describe('#name', () => {
  67. it('should be the value passed in', () => {
  68. let factory = new WidgetFactory({
  69. name: 'test',
  70. fileExtensions: ['.txt'],
  71. });
  72. expect(factory.name).to.be('test');
  73. });
  74. });
  75. describe('#defaultFor', () => {
  76. it('should default to an empty array', () => {
  77. let factory = new WidgetFactory({
  78. name: 'test',
  79. fileExtensions: ['.txt'],
  80. });
  81. expect(factory.defaultFor).to.eql([]);
  82. });
  83. it('should be the value passed in', () => {
  84. let factory = new WidgetFactory({
  85. name: 'test',
  86. fileExtensions: ['.txt'],
  87. defaultFor: ['.md']
  88. });
  89. expect(factory.defaultFor).to.eql(['.md']);
  90. });
  91. });
  92. describe('#readOnly', () => {
  93. it('should default to false', () => {
  94. let factory = new WidgetFactory({
  95. name: 'test',
  96. fileExtensions: ['.txt'],
  97. });
  98. expect(factory.readOnly).to.be(false);
  99. });
  100. it('should be the value passed in', () => {
  101. let factory = new WidgetFactory({
  102. name: 'test',
  103. fileExtensions: ['.txt'],
  104. readOnly: true
  105. });
  106. expect(factory.readOnly).to.be(true);
  107. });
  108. });
  109. describe('#modelName', () => {
  110. it('should default to `text`', () => {
  111. let factory = new WidgetFactory({
  112. name: 'test',
  113. fileExtensions: ['.txt'],
  114. });
  115. expect(factory.modelName).to.be('text');
  116. });
  117. it('should be the value passed in', () => {
  118. let factory = new WidgetFactory({
  119. name: 'test',
  120. fileExtensions: ['.txt'],
  121. modelName: 'notebook'
  122. });
  123. expect(factory.modelName).to.be('notebook');
  124. });
  125. });
  126. describe('#preferKernel', () => {
  127. it('should default to false', () => {
  128. let factory = new WidgetFactory({
  129. name: 'test',
  130. fileExtensions: ['.txt'],
  131. });
  132. expect(factory.preferKernel).to.be(false);
  133. });
  134. it('should be the value passed in', () => {
  135. let factory = new WidgetFactory({
  136. name: 'test',
  137. fileExtensions: ['.txt'],
  138. preferKernel: true
  139. });
  140. expect(factory.preferKernel).to.be(true);
  141. });
  142. });
  143. describe('#canStartKernel', () => {
  144. it('should default to false', () => {
  145. let factory = new WidgetFactory({
  146. name: 'test',
  147. fileExtensions: ['.txt'],
  148. });
  149. expect(factory.canStartKernel).to.be(false);
  150. });
  151. it('should be the value passed in', () => {
  152. let factory = new WidgetFactory({
  153. name: 'test',
  154. fileExtensions: ['.txt'],
  155. canStartKernel: true
  156. });
  157. expect(factory.canStartKernel).to.be(true);
  158. });
  159. });
  160. describe('#isDisposed', () => {
  161. it('should get whether the factory has been disposed', () => {
  162. let factory = createFactory();
  163. expect(factory.isDisposed).to.be(false);
  164. factory.dispose();
  165. expect(factory.isDisposed).to.be(true);
  166. });
  167. });
  168. describe('#dispose()', () => {
  169. it('should dispose of the resources held by the factory', () => {
  170. let factory = createFactory();
  171. factory.dispose();
  172. expect(factory.isDisposed).to.be(true);
  173. });
  174. it('should be safe to call multiple times', () => {
  175. let factory = createFactory();
  176. factory.dispose();
  177. factory.dispose();
  178. expect(factory.isDisposed).to.be(true);
  179. });
  180. });
  181. describe('#createNew()', () => {
  182. it('should create a new widget given a document model and a context', () => {
  183. let factory = createFactory();
  184. let widget = factory.createNew(context);
  185. expect(widget).to.be.a(Widget);
  186. });
  187. });
  188. });
  189. describe('Base64ModelFactory', () => {
  190. describe('#name', () => {
  191. it('should get the name of the model type', () => {
  192. let factory = new Base64ModelFactory();
  193. expect(factory.name).to.be('base64');
  194. });
  195. });
  196. describe('#contentType', () => {
  197. it('should get the file type', () => {
  198. let factory = new Base64ModelFactory();
  199. expect(factory.contentType).to.be('file');
  200. });
  201. });
  202. describe('#fileFormat', () => {
  203. it('should get the file format', () => {
  204. let factory = new Base64ModelFactory();
  205. expect(factory.fileFormat).to.be('base64');
  206. });
  207. });
  208. });
  209. describe('DocumentModel', () => {
  210. describe('#constructor()', () => {
  211. it('should create a new document model', () => {
  212. let model = new DocumentModel();
  213. expect(model).to.be.a(DocumentModel);
  214. });
  215. it('should accept an optional language preference', () => {
  216. let model = new DocumentModel('foo');
  217. expect(model.defaultKernelLanguage).to.be('foo');
  218. });
  219. });
  220. describe('#isDisposed', () => {
  221. it('should get whether the model has been disposed', () => {
  222. let model = new DocumentModel();
  223. expect(model.isDisposed).to.be(false);
  224. model.dispose();
  225. expect(model.isDisposed).to.be(true);
  226. });
  227. });
  228. describe('#contentChanged', () => {
  229. it('should be emitted when the content of the model changes', () => {
  230. let model = new DocumentModel();
  231. let called = false;
  232. model.contentChanged.connect((sender, args) => {
  233. expect(sender).to.be(model);
  234. expect(args).to.be(void 0);
  235. called = true;
  236. });
  237. model.fromString('foo');
  238. expect(called).to.be(true);
  239. });
  240. it('should not be emitted if the content does not change', () => {
  241. let model = new DocumentModel();
  242. let called = false;
  243. model.contentChanged.connect(() => { called = true; });
  244. model.fromString('');
  245. expect(called).to.be(false);
  246. });
  247. });
  248. describe('#stateChanged', () => {
  249. it('should be emitted when the state of the model changes', () => {
  250. let model = new DocumentModel();
  251. let called = false;
  252. model.stateChanged.connect((sender, args) => {
  253. expect(sender).to.be(model);
  254. expect(args.name).to.be('readOnly');
  255. expect(args.oldValue).to.be(false);
  256. expect(args.newValue).to.be(true);
  257. called = true;
  258. });
  259. model.readOnly = true;
  260. expect(called).to.be(true);
  261. });
  262. it('should not be emitted if the state does not change', () => {
  263. let model = new DocumentModel();
  264. let called = false;
  265. model.stateChanged.connect(() => { called = true; });
  266. model.dirty = false;
  267. expect(called).to.be(false);
  268. });
  269. });
  270. describe('#dirty', () => {
  271. it('should get the dirty state of the document', () => {
  272. let model = new DocumentModel();
  273. expect(model.dirty).to.be(false);
  274. });
  275. it('should emit `stateChanged` when changed', () => {
  276. let model = new DocumentModel();
  277. let called = false;
  278. model.stateChanged.connect((sender, args) => {
  279. expect(sender).to.be(model);
  280. expect(args.name).to.be('dirty');
  281. expect(args.oldValue).to.be(false);
  282. expect(args.newValue).to.be(true);
  283. called = true;
  284. });
  285. model.dirty = true;
  286. expect(called).to.be(true);
  287. });
  288. it('should not emit `stateChanged` when not changed', () => {
  289. let model = new DocumentModel();
  290. let called = false;
  291. model.stateChanged.connect(() => { called = true; });
  292. model.dirty = false;
  293. expect(called).to.be(false);
  294. });
  295. });
  296. describe('#readOnly', () => {
  297. it('should get the read only state of the document', () => {
  298. let model = new DocumentModel();
  299. expect(model.readOnly).to.be(false);
  300. });
  301. it('should emit `stateChanged` when changed', () => {
  302. let model = new DocumentModel();
  303. let called = false;
  304. model.stateChanged.connect((sender, args) => {
  305. expect(sender).to.be(model);
  306. expect(args.name).to.be('readOnly');
  307. expect(args.oldValue).to.be(false);
  308. expect(args.newValue).to.be(true);
  309. called = true;
  310. });
  311. model.readOnly = true;
  312. expect(called).to.be(true);
  313. });
  314. it('should not emit `stateChanged` when not changed', () => {
  315. let model = new DocumentModel();
  316. let called = false;
  317. model.stateChanged.connect(() => { called = true; });
  318. model.readOnly = false;
  319. expect(called).to.be(false);
  320. });
  321. });
  322. describe('#defaultKernelName', () => {
  323. it('should get the default kernel name of the document', () => {
  324. let model = new DocumentModel();
  325. expect(model.defaultKernelName).to.be('');
  326. });
  327. });
  328. describe('defaultKernelLanguage', () => {
  329. it('should get the default kernel langauge of the document', () => {
  330. let model = new DocumentModel();
  331. expect(model.defaultKernelLanguage).to.be('');
  332. });
  333. it('should be set by the constructor arg', () => {
  334. let model = new DocumentModel('foo');
  335. expect(model.defaultKernelLanguage).to.be('foo');
  336. });
  337. });
  338. describe('#dispose()', () => {
  339. it('should dispose of the resources held by the document manager', () => {
  340. let model = new DocumentModel();
  341. model.dispose();
  342. expect(model.isDisposed).to.be(true);
  343. });
  344. it('should be safe to call more than once', () => {
  345. let model = new DocumentModel();
  346. model.dispose();
  347. model.dispose();
  348. expect(model.isDisposed).to.be(true);
  349. });
  350. });
  351. describe('#toString()', () => {
  352. it('should serialize the model to a string', () => {
  353. let model = new DocumentModel();
  354. expect(model.toString()).to.be('');
  355. });
  356. });
  357. describe('#fromString()', () => {
  358. it('should deserialize the model from a string', () => {
  359. let model = new DocumentModel();
  360. model.fromString('foo');
  361. expect(model.toString()).to.be('foo');
  362. });
  363. });
  364. describe('#toJSON()', () => {
  365. it('should serialize the model to JSON', () => {
  366. let model = new DocumentModel();
  367. let data = { 'foo': 1 };
  368. model.fromJSON(data);
  369. expect(model.toJSON()).to.eql(data);
  370. });
  371. });
  372. describe('#fromJSON()', () => {
  373. it('should deserialize the model from JSON', () => {
  374. let model = new DocumentModel();
  375. let data = null;
  376. model.fromJSON(data);
  377. expect(model.toString()).to.be('null');
  378. });
  379. });
  380. });
  381. describe('TextModelFactory', () => {
  382. describe('#name', () => {
  383. it('should get the name of the model type', () => {
  384. let factory = new TextModelFactory();
  385. expect(factory.name).to.be('text');
  386. });
  387. });
  388. describe('#contentType', () => {
  389. it('should get the file type', () => {
  390. let factory = new TextModelFactory();
  391. expect(factory.contentType).to.be('file');
  392. });
  393. });
  394. describe('#fileFormat', () => {
  395. it('should get the file format', () => {
  396. let factory = new TextModelFactory();
  397. expect(factory.fileFormat).to.be('text');
  398. });
  399. });
  400. describe('#isDisposed', () => {
  401. it('should get whether the factory is disposed', () => {
  402. let factory = new TextModelFactory();
  403. expect(factory.isDisposed).to.be(false);
  404. factory.dispose();
  405. expect(factory.isDisposed).to.be(true);
  406. });
  407. });
  408. describe('#dispose()', () => {
  409. it('should dispose of the resources held by the factory', () => {
  410. let factory = new TextModelFactory();
  411. factory.dispose();
  412. expect(factory.isDisposed).to.be(true);
  413. });
  414. it('should be safe to call multiple times', () => {
  415. let factory = new TextModelFactory();
  416. factory.dispose();
  417. factory.dispose();
  418. expect(factory.isDisposed).to.be(true);
  419. });
  420. });
  421. describe('#createNew()', () => {
  422. it('should create a new model', () => {
  423. let factory = new TextModelFactory();
  424. let model = factory.createNew();
  425. expect(model).to.be.a(DocumentModel);
  426. });
  427. it('should accept a language preference', () => {
  428. let factory = new TextModelFactory();
  429. let model = factory.createNew('foo');
  430. expect(model.defaultKernelLanguage).to.be('foo');
  431. });
  432. });
  433. describe('#preferredLanguage()', () => {
  434. it('should get the preferred kernel language given an extension', () => {
  435. let factory = new TextModelFactory();
  436. expect(factory.preferredLanguage('.py')).to.be('python');
  437. expect(factory.preferredLanguage('.jl')).to.be('julia');
  438. });
  439. });
  440. });
  441. describe('MimeDocumentFactory', () => {
  442. describe('#createNew()', () => {
  443. it('should require a context parameter', () => {
  444. let widgetFactory = new MimeDocumentFactory({
  445. name: 'markdown',
  446. fileExtensions: ['.md'],
  447. rendermime: RENDERMIME,
  448. mimeType: 'text/markdown'
  449. });
  450. expect(widgetFactory.createNew(context)).to.be.a(MimeDocument);
  451. });
  452. });
  453. });
  454. describe('MimeDocument', () => {
  455. describe('#constructor()', () => {
  456. it('should require options', () => {
  457. let widget = new MimeDocument({
  458. context,
  459. rendermime: RENDERMIME,
  460. mimeType: 'text/markdown',
  461. renderTimeout: 1000,
  462. dataType: 'string'
  463. });
  464. expect(widget).to.be.a(MimeDocument);
  465. });
  466. });
  467. describe('#ready', () => {
  468. it('should resolve when the widget is ready', () => {
  469. let widget = new LogRenderer({
  470. context,
  471. rendermime: RENDERMIME,
  472. mimeType: 'text/markdown',
  473. renderTimeout: 1000,
  474. dataType: 'string'
  475. });
  476. context.save();
  477. return widget.ready.then(() => {
  478. let layout = widget.layout as PanelLayout;
  479. expect(layout.widgets.length).to.be(2);
  480. });
  481. });
  482. });
  483. });
  484. });