context.spec.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. Contents, ServiceManager
  6. } from '@jupyterlab/services';
  7. import {
  8. Widget
  9. } from 'phosphor/lib/ui/widget';
  10. import {
  11. Context, DocumentRegistry, TextModelFactory
  12. } from '../../../lib/docregistry';
  13. import {
  14. waitForDialog, acceptDialog
  15. } from '../utils';
  16. describe('docregistry/context', () => {
  17. let manager: ServiceManager.IManager;
  18. let factory = new TextModelFactory();
  19. before((done) => {
  20. manager = new ServiceManager();
  21. manager.ready().then(done, done);
  22. });
  23. describe('Context', () => {
  24. let context: Context<DocumentRegistry.IModel>;
  25. beforeEach(() => {
  26. context = new Context({ manager, factory, path: 'foo' });
  27. });
  28. afterEach((done) => {
  29. if (context.kernel) {
  30. context.kernel.ready().then(() => {
  31. context.dispose();
  32. }).then(done, done);
  33. } else {
  34. context.dispose();
  35. done();
  36. }
  37. });
  38. describe('#constructor()', () => {
  39. it('should create a new context', () => {
  40. context = new Context({ manager, factory, path: 'bar' });
  41. expect(context).to.be.a(Context);
  42. });
  43. });
  44. describe('#kernelChanged', () => {
  45. it('should be emitted when the kernel changes', (done) => {
  46. let name = manager.specs.default;
  47. context.kernelChanged.connect((sender, args) => {
  48. expect(sender).to.be(context);
  49. expect(args.name).to.be(name);
  50. done();
  51. });
  52. context.changeKernel({ name });
  53. });
  54. });
  55. describe('#pathChanged', () => {
  56. it('should be emitted when the path changes', (done) => {
  57. context.pathChanged.connect((sender, args) => {
  58. expect(sender).to.be(context);
  59. expect(args).to.be('foo');
  60. done();
  61. });
  62. context.save().then(() => {
  63. return manager.contents.rename(context.path, 'foo');
  64. }).catch(done);
  65. });
  66. });
  67. describe('#fileChanged', () => {
  68. it('should be emitted when the file is saved', (done) => {
  69. context.fileChanged.connect((sender, args) => {
  70. expect(sender).to.be(context);
  71. expect(args.name).to.be('foo');
  72. done();
  73. });
  74. context.save();
  75. });
  76. });
  77. describe('#isReady', () => {
  78. it('should indicate whether the context is ready', (done) => {
  79. expect(context.isReady).to.be(false);
  80. context.ready().then(() => {
  81. expect(context.isReady).to.be(true);
  82. done();
  83. }).catch(done);
  84. context.save().catch(done);
  85. });
  86. });
  87. describe('#ready()', () => {
  88. it('should resolve when the file is saved for the first time', (done) => {
  89. context.ready().then(done, done);
  90. context.save().catch(done);
  91. });
  92. it('should resolve when the file is reverted for the first time', (done) => {
  93. manager.contents.save(context.path, {
  94. type: factory.contentType,
  95. format: factory.fileFormat,
  96. content: 'foo'
  97. });
  98. context.ready().then(done, done);
  99. context.revert().catch(done);
  100. });
  101. });
  102. describe('#disposed', () => {
  103. it('should be emitted when the context is disposed', (done) => {
  104. context.disposed.connect((sender, args) => {
  105. expect(sender).to.be(context);
  106. expect(args).to.be(void 0);
  107. done();
  108. });
  109. context.dispose();
  110. });
  111. });
  112. describe('#model', () => {
  113. it('should be the model associated with the document', () => {
  114. expect(context.model.toString()).to.be('');
  115. });
  116. });
  117. describe('#kernel', () => {
  118. it('should default to `null`', () => {
  119. expect(context.kernel).to.be(null);
  120. });
  121. it('should be set after switching kernels', (done) => {
  122. let name = manager.specs.default;
  123. context.changeKernel({ name }).then(() => {
  124. expect(context.kernel.name).to.be(name);
  125. done();
  126. }).catch(done);
  127. });
  128. });
  129. describe('#path', () => {
  130. it('should be the current path for the context', () => {
  131. expect(context.path).to.be('foo');
  132. });
  133. });
  134. describe('#contentsModel', () => {
  135. it('should be `null` before poulation', () => {
  136. expect(context.contentsModel).to.be(null);
  137. });
  138. it('should be set after poulation', (done) => {
  139. context.ready().then(() => {
  140. expect(context.contentsModel.name).to.be('foo');
  141. done();
  142. });
  143. context.save().catch(done);
  144. });
  145. });
  146. describe('#factoryName', () => {
  147. it('should be the name of the factory used by the context', () => {
  148. expect(context.factoryName).to.be(factory.name);
  149. });
  150. });
  151. describe('#isDisposed', () => {
  152. it('should test whether the context is disposed', () => {
  153. expect(context.isDisposed).to.be(false);
  154. context.dispose();
  155. expect(context.isDisposed).to.be(true);
  156. });
  157. });
  158. describe('#dispose()', () => {
  159. it('should dispose of the resources used by the context', () => {
  160. context.dispose();
  161. expect(context.isDisposed).to.be(true);
  162. context.dispose();
  163. expect(context.isDisposed).to.be(true);
  164. });
  165. });
  166. describe('#startDefaultKernel()', () => {
  167. it('should start the default kernel for the context', (done) => {
  168. context.save().then(() => {
  169. return context.startDefaultKernel();
  170. }).then(kernel => {
  171. expect(kernel.name).to.be.ok();
  172. done();
  173. }).catch(done);
  174. });
  175. });
  176. describe('#changeKernel()', () => {
  177. it('should change the kernel instance', (done) => {
  178. let name = manager.specs.default;
  179. context.changeKernel({ name }).then(() => {
  180. expect(context.kernel.name).to.be(name);
  181. }).then(done, done);
  182. });
  183. it('should shut down the session if given `null`', (done) => {
  184. let name = manager.specs.default;
  185. context.changeKernel({ name }).then(() => {
  186. expect(context.kernel.name).to.be(name);
  187. return context.kernel.ready();
  188. }).then(() => {
  189. return context.changeKernel(null);
  190. }).then(() => {
  191. expect(context.kernel).to.be(null);
  192. done();
  193. }).catch(done);
  194. });
  195. });
  196. describe('#save()', () => {
  197. it('should save the contents of the file to disk', (done) => {
  198. context.model.fromString('foo');
  199. context.save().then(() => {
  200. let opts: Contents.IFetchOptions = {
  201. format: factory.fileFormat,
  202. type: factory.contentType,
  203. content: true
  204. };
  205. return manager.contents.get(context.path, opts);
  206. }).then(model => {
  207. expect(model.content).to.be('foo');
  208. done();
  209. }).catch(done);
  210. });
  211. });
  212. describe('#saveAs()', () => {
  213. it('should save the document to a different path chosen by the user', (done) => {
  214. waitForDialog().then(() => {
  215. let dialog = document.body.getElementsByClassName('jp-Dialog')[0];
  216. let input = dialog.getElementsByTagName('input')[0];
  217. input.value = 'bar';
  218. acceptDialog();
  219. });
  220. context.saveAs().then(() => {
  221. expect(context.path).to.be('bar');
  222. done();
  223. }).catch(done);
  224. });
  225. });
  226. describe('#revert()', () => {
  227. it('should revert the contents of the file to the disk', (done) => {
  228. manager.contents.save(context.path, {
  229. type: factory.contentType,
  230. format: factory.fileFormat,
  231. content: 'foo'
  232. }).then(() => {
  233. context.model.fromString('bar');
  234. return context.revert();
  235. }).then(() => {
  236. expect(context.model.toString()).to.be('foo');
  237. done();
  238. }).catch(done);
  239. });
  240. });
  241. describe('#createCheckpoint()', () => {
  242. it('should create a checkpoint for the file', (done) => {
  243. context.createCheckpoint().then(model => {
  244. expect(model.id).to.be.ok();
  245. expect(model.last_modified).to.be.ok();
  246. done();
  247. }).catch(done);
  248. });
  249. });
  250. describe('#deleteCheckpoint()', () => {
  251. it('should delete the given checkpoint', (done) => {
  252. context.createCheckpoint().then(model => {
  253. return context.deleteCheckpoint(model.id);
  254. }).then(() => {
  255. return context.listCheckpoints();
  256. }).then(models => {
  257. expect(models.length).to.be(0);
  258. done();
  259. }).catch(done);
  260. });
  261. });
  262. describe('#restoreCheckpoint()', () => {
  263. it('should restore the value to the last checkpoint value', (done) => {
  264. context.model.fromString('bar');
  265. let id = '';
  266. context.save().then(() => {
  267. return context.createCheckpoint();
  268. }).then(model => {
  269. context.model.fromString('foo');
  270. id = model.id;
  271. return context.save();
  272. }).then(() => {
  273. return context.restoreCheckpoint(id);
  274. }).then(() => {
  275. return context.revert();
  276. }).then(() => {
  277. expect(context.model.toString()).to.be('bar');
  278. done();
  279. }).catch(done);
  280. });
  281. });
  282. describe('#listCheckpoints()', () => {
  283. it('should list the checkpoints for the file', (done) => {
  284. let id = '';
  285. context.createCheckpoint().then(model => {
  286. id = model.id;
  287. return context.listCheckpoints();
  288. }).then(models => {
  289. for (let model of models) {
  290. if (model.id === id) {
  291. done();
  292. return;
  293. }
  294. }
  295. }).catch(done);
  296. });
  297. });
  298. describe('#resolveUrl()', () => {
  299. it('should resolve a relative url to a correct server path', () => {
  300. let path = context.resolveUrl('./foo');
  301. expect(path).to.be(manager.contents.getDownloadUrl('foo'));
  302. });
  303. it('should ignore urls that have a protocol', () => {
  304. let path = context.resolveUrl('http://foo');
  305. expect(path).to.be('http://foo');
  306. });
  307. });
  308. describe('#addSibling()', () => {
  309. it('should add a sibling widget', () => {
  310. let called = false;
  311. let opener = (widget: Widget) => {
  312. called = true;
  313. };
  314. context = new Context({ manager, factory, path: 'foo', opener });
  315. context.addSibling(new Widget());
  316. expect(called).to.be(true);
  317. });
  318. });
  319. });
  320. });