widget.spec.ts 6.6 KB

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