widget.spec.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { MessageLoop, Message } from '@lumino/messaging';
  4. import { Widget } from '@lumino/widgets';
  5. import { simulate } from 'simulate-event';
  6. import { UUID } from '@lumino/coreutils';
  7. import { ServiceManager } from '@jupyterlab/services';
  8. import {
  9. CodeMirrorEditorFactory,
  10. CodeMirrorMimeTypeService
  11. } from '@jupyterlab/codemirror';
  12. import {
  13. Context,
  14. DocumentRegistry,
  15. TextModelFactory,
  16. DocumentWidget
  17. } from '@jupyterlab/docregistry';
  18. import {
  19. FileEditor,
  20. FileEditorCodeWrapper,
  21. FileEditorFactory
  22. } from '@jupyterlab/fileeditor';
  23. import { framePromise } from '@jupyterlab/testutils';
  24. import * as Mock from '@jupyterlab/testutils/lib/mock';
  25. class LogFileEditor extends FileEditor {
  26. events: string[] = [];
  27. methods: string[] = [];
  28. handleEvent(event: Event): void {
  29. this.events.push(event.type);
  30. super.handleEvent(event);
  31. }
  32. protected onAfterAttach(msg: Message): void {
  33. super.onAfterAttach(msg);
  34. this.methods.push('onAfterAttach');
  35. }
  36. protected onBeforeDetach(msg: Message): void {
  37. super.onBeforeDetach(msg);
  38. this.methods.push('onBeforeDetach');
  39. }
  40. protected onActivateRequest(msg: Message): void {
  41. super.onActivateRequest(msg);
  42. this.methods.push('onActivateRequest');
  43. }
  44. }
  45. describe('fileeditorcodewrapper', () => {
  46. const factoryService = new CodeMirrorEditorFactory();
  47. const modelFactory = new TextModelFactory();
  48. const mimeTypeService = new CodeMirrorMimeTypeService();
  49. let context: Context<DocumentRegistry.ICodeModel>;
  50. let manager: ServiceManager.IManager;
  51. beforeAll(() => {
  52. manager = new Mock.ServiceManagerMock();
  53. return manager.ready;
  54. });
  55. describe('FileEditorCodeWrapper', () => {
  56. let widget: FileEditorCodeWrapper;
  57. beforeEach(() => {
  58. const path = UUID.uuid4() + '.py';
  59. context = new Context({ manager, factory: modelFactory, path });
  60. widget = new FileEditorCodeWrapper({
  61. factory: options => factoryService.newDocumentEditor(options),
  62. mimeTypeService,
  63. context
  64. });
  65. });
  66. afterEach(() => {
  67. widget.dispose();
  68. });
  69. describe('#constructor()', () => {
  70. it('should create an editor wrapper widget', () => {
  71. expect(widget).toBeInstanceOf(FileEditorCodeWrapper);
  72. });
  73. it('should update the editor text when the model changes', async () => {
  74. await context.initialize(true);
  75. await context.ready;
  76. widget.context.model.fromString('foo');
  77. expect(widget.editor.model.value.text).toBe('foo');
  78. });
  79. });
  80. describe('#context', () => {
  81. it('should be the context used by the widget', () => {
  82. expect(widget.context).toBe(context);
  83. });
  84. });
  85. });
  86. describe('FileEditor', () => {
  87. let widget: LogFileEditor;
  88. beforeEach(() => {
  89. const path = UUID.uuid4() + '.py';
  90. context = new Context({ manager, factory: modelFactory, path });
  91. widget = new LogFileEditor({
  92. factory: options => factoryService.newDocumentEditor(options),
  93. mimeTypeService,
  94. context
  95. });
  96. });
  97. afterEach(() => {
  98. widget.dispose();
  99. });
  100. describe('#constructor()', () => {
  101. it('should create an editor widget', () => {
  102. expect(widget).toBeInstanceOf(FileEditor);
  103. });
  104. it('should update the editor text when the model changes', async () => {
  105. await context.initialize(true);
  106. await context.ready;
  107. widget.context.model.fromString('foo');
  108. expect(widget.editor.model.value.text).toBe('foo');
  109. });
  110. it('should set the mime type for the path', () => {
  111. expect(widget.editor.model.mimeType).toBe('text/x-python');
  112. });
  113. it('should update the mime type when the path changes', async () => {
  114. let called = false;
  115. context.pathChanged.connect((sender, args) => {
  116. expect(widget.editor.model.mimeType).toBe('text/x-julia');
  117. called = true;
  118. });
  119. await context.initialize(true);
  120. await manager.contents.rename(context.path, UUID.uuid4() + '.jl');
  121. expect(called).toBe(true);
  122. });
  123. });
  124. describe('#context', () => {
  125. it('should be the context used by the widget', () => {
  126. expect(widget.context).toBe(context);
  127. });
  128. });
  129. describe('#handleEvent()', () => {
  130. beforeEach(() => {
  131. Widget.attach(widget, document.body);
  132. return framePromise();
  133. });
  134. afterEach(() => {
  135. widget.dispose();
  136. });
  137. describe('mousedown', () => {
  138. it('should focus the editor', () => {
  139. simulate(widget.node, 'mousedown');
  140. expect(widget.events).toContain('mousedown');
  141. expect(widget.editor.hasFocus()).toBe(true);
  142. });
  143. });
  144. });
  145. describe('#onAfterAttach()', () => {
  146. it('should add event listeners', async () => {
  147. Widget.attach(widget, document.body);
  148. await framePromise();
  149. expect(widget.methods).toContain('onAfterAttach');
  150. simulate(widget.node, 'mousedown');
  151. expect(widget.events).toContain('mousedown');
  152. });
  153. });
  154. describe('#onBeforeDetach()', () => {
  155. it('should remove event listeners', async () => {
  156. Widget.attach(widget, document.body);
  157. await framePromise();
  158. Widget.detach(widget);
  159. expect(widget.methods).toContain('onBeforeDetach');
  160. widget.events = [];
  161. simulate(widget.node, 'mousedown');
  162. expect(widget.events).not.toContain('mousedown');
  163. });
  164. });
  165. describe('#onActivateRequest()', () => {
  166. it('should focus the node after an update', async () => {
  167. Widget.attach(widget, document.body);
  168. MessageLoop.sendMessage(widget, Widget.Msg.ActivateRequest);
  169. expect(widget.methods).toContain('onActivateRequest');
  170. await framePromise();
  171. expect(widget.editor.hasFocus()).toBe(true);
  172. });
  173. });
  174. });
  175. describe('FileEditorFactory', () => {
  176. const widgetFactory = new FileEditorFactory({
  177. editorServices: {
  178. factoryService,
  179. mimeTypeService
  180. },
  181. factoryOptions: {
  182. name: 'editor',
  183. fileTypes: ['*'],
  184. defaultFor: ['*']
  185. }
  186. });
  187. describe('#constructor()', () => {
  188. it('should create an FileEditorFactory', () => {
  189. expect(widgetFactory).toBeInstanceOf(FileEditorFactory);
  190. });
  191. });
  192. describe('#createNewWidget()', () => {
  193. it('should create a document widget', () => {
  194. const d = widgetFactory.createNew(context);
  195. expect(d).toBeInstanceOf(DocumentWidget);
  196. expect(d.content).toBeInstanceOf(FileEditor);
  197. });
  198. });
  199. });
  200. });